Browse Source

More code cleanup and (javadoc style) commenting

pull/24/head
Apostolos Fanakis 8 years ago
parent
commit
b7718500df
  1. 66
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileActivity.java
  2. 82
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileParser.java
  3. 3
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java
  4. 187
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicParser.java
  5. 19
      app/src/main/java/gr/thmmy/mthmmy/utils/CircleTransform.java
  6. 4
      app/src/main/java/gr/thmmy/mthmmy/utils/ScrollAwareFABBehavior.java

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

@ -37,16 +37,19 @@ import mthmmy.utils.Report;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
import static gr.thmmy.mthmmy.activities.profile.ProfileParser.NAME_INDEX;
import static gr.thmmy.mthmmy.activities.profile.ProfileParser.PERSONAL_TEXT_INDEX; import static gr.thmmy.mthmmy.activities.profile.ProfileParser.PERSONAL_TEXT_INDEX;
import static gr.thmmy.mthmmy.activities.profile.ProfileParser.THUMBNAIL_URL; import static gr.thmmy.mthmmy.activities.profile.ProfileParser.THUMBNAIL_URL_INDEX;
import static gr.thmmy.mthmmy.activities.profile.ProfileParser.parseProfile; import static gr.thmmy.mthmmy.activities.profile.ProfileParser.USERNAME_INDEX;
import static gr.thmmy.mthmmy.activities.profile.ProfileParser.parseProfileSummary;
import static gr.thmmy.mthmmy.session.SessionManager.LOGGED_IN; import static gr.thmmy.mthmmy.session.SessionManager.LOGGED_IN;
import static gr.thmmy.mthmmy.session.SessionManager.LOGIN_STATUS; import static gr.thmmy.mthmmy.session.SessionManager.LOGIN_STATUS;
/**
* Activity for user's profile. When creating an Intent of this activity you need to bundle a <b>String</b>
* containing this user's profile url using the key {@link #EXTRAS_PROFILE_URL}.
*/
public class ProfileActivity extends BaseActivity { public class ProfileActivity extends BaseActivity {
//Graphic element variables
//Graphic elements
private ImageView userThumbnail; private ImageView userThumbnail;
private TextView userName; private TextView userName;
private TextView personalText; private TextView personalText;
@ -55,10 +58,20 @@ public class ProfileActivity extends BaseActivity {
private FloatingActionButton replyFAB; private FloatingActionButton replyFAB;
//Other variables //Other variables
private ArrayList<String> parsedProfileData; /**
* Debug Tag for logging debug output to LogCat
*/
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static final String TAG = "ProfileActivity"; private static final String TAG = "ProfileActivity";
static String PACKAGE_NAME; static String PACKAGE_NAME;
/**
* The key to use when putting profile's url String to {@link ProfileActivity}'s Bundle.
*/
public static final String EXTRAS_PROFILE_URL = "PROFILE_URL";
/**
* {@link ArrayList} of Strings used to hold profile's information. Data are added in {@link ProfileTask}.
*/
private ArrayList<String> parsedProfileData;
private static final int THUMBNAIL_SIZE = 200; private static final int THUMBNAIL_SIZE = 200;
@Override @Override
@ -67,10 +80,9 @@ public class ProfileActivity extends BaseActivity {
setContentView(R.layout.activity_profile); setContentView(R.layout.activity_profile);
PACKAGE_NAME = getApplicationContext().getPackageName(); PACKAGE_NAME = getApplicationContext().getPackageName();
Bundle extras = getIntent().getExtras(); Bundle extras = getIntent().getExtras();
//Initialize toolbar, drawer and ProgressBar //Initialize graphic elements
toolbar = (Toolbar) findViewById(R.id.toolbar); toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitle(null); toolbar.setTitle(null);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
@ -78,9 +90,7 @@ public class ProfileActivity extends BaseActivity {
getSupportActionBar().setDisplayShowTitleEnabled(false); getSupportActionBar().setDisplayShowTitleEnabled(false);
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} }
createDrawer(); createDrawer();
progressBar = (ProgressBar) findViewById(R.id.progressBar); progressBar = (ProgressBar) findViewById(R.id.progressBar);
userThumbnail = (ImageView) findViewById(R.id.user_thumbnail); userThumbnail = (ImageView) findViewById(R.id.user_thumbnail);
@ -136,22 +146,32 @@ public class ProfileActivity extends BaseActivity {
} }
}); });
new ProfileTask().execute(extras.getString("PROFILE_URL")); //Attempt data parsing new ProfileTask().execute(extras.getString(EXTRAS_PROFILE_URL)); //Attempt data parsing
} }
/**
* An {@link AsyncTask} that handles asynchronous fetching of a profile page and parsing it's
* data. {@link AsyncTask#onPostExecute(Object) OnPostExecute} method calls {@link #populateLayout()}
* to build graphics.
* <p>
* <p>Calling ProfileTask's {@link AsyncTask#execute execute} method needs to have profile's url
* as String parameter!</p>
*/
public class ProfileTask extends AsyncTask<String, Void, Boolean> { public class ProfileTask extends AsyncTask<String, Void, Boolean> {
//Class variables //Class variables
/**
* Debug Tag for logging debug output to LogCat
*/
private static final String TAG = "TopicTask"; //Separate tag for AsyncTask private static final String TAG = "TopicTask"; //Separate tag for AsyncTask
//Show a progress bar until done
protected void onPreExecute() { protected void onPreExecute() {
progressBar.setVisibility(ProgressBar.VISIBLE); progressBar.setVisibility(ProgressBar.VISIBLE);
replyFAB.setEnabled(false); replyFAB.setEnabled(false);
} }
protected Boolean doInBackground(String... strings) { protected Boolean doInBackground(String... profileUrl) {
Document document; Document document;
String pageUrl = strings[0] + ";wap"; //Profile's page wap url String pageUrl = profileUrl[0] + ";wap"; //Profile's page wap url
Request request = new Request.Builder() Request request = new Request.Builder()
.url(pageUrl) .url(pageUrl)
@ -160,7 +180,7 @@ public class ProfileActivity extends BaseActivity {
Response response = client.newCall(request).execute(); Response response = client.newCall(request).execute();
document = Jsoup.parse(response.body().string()); document = Jsoup.parse(response.body().string());
//long parseStartTime = System.nanoTime(); //long parseStartTime = System.nanoTime();
parsedProfileData = parseProfile(document); //Parse data parsedProfileData = parseProfileSummary(document);
//long parseEndTime = System.nanoTime(); //long parseEndTime = System.nanoTime();
return true; return true;
} catch (SSLHandshakeException e) { } catch (SSLHandshakeException e) {
@ -173,22 +193,26 @@ public class ProfileActivity extends BaseActivity {
protected void onPostExecute(Boolean result) { protected void onPostExecute(Boolean result) {
if (!result) { //Parse failed! if (!result) { //Parse failed!
//Should never happen
Toast.makeText(getBaseContext() Toast.makeText(getBaseContext()
, "Fatal error!\n Aborting...", Toast.LENGTH_LONG).show(); , "Fatal error!\n Aborting...", Toast.LENGTH_LONG).show();
finish(); finish();
} }
//Parse was successful //Parse was successful
progressBar.setVisibility(ProgressBar.INVISIBLE); //Hide progress bar progressBar.setVisibility(ProgressBar.INVISIBLE);
populateLayout(); //Show parsed data populateLayout();
} }
} }
/**
* Simple method that builds the UI of a {@link ProfileActivity}.
* <p>Use this method <b>only after</b> parsing profile's data with {@link ProfileTask} as it
* reads from {@link #parsedProfileData}</p>
*/
private void populateLayout() { private void populateLayout() {
if (parsedProfileData.get(THUMBNAIL_URL) != null) if (parsedProfileData.get(THUMBNAIL_URL_INDEX) != null)
//noinspection ConstantConditions //noinspection ConstantConditions
Picasso.with(this) Picasso.with(this)
.load(parsedProfileData.get(THUMBNAIL_URL)) .load(parsedProfileData.get(THUMBNAIL_URL_INDEX))
.resize(THUMBNAIL_SIZE, THUMBNAIL_SIZE) .resize(THUMBNAIL_SIZE, THUMBNAIL_SIZE)
.centerCrop() .centerCrop()
.error(ResourcesCompat.getDrawable(this.getResources() .error(ResourcesCompat.getDrawable(this.getResources()
@ -198,7 +222,7 @@ public class ProfileActivity extends BaseActivity {
.transform(new CircleTransform()) .transform(new CircleTransform())
.into(userThumbnail); .into(userThumbnail);
userName.setText(parsedProfileData.get(NAME_INDEX)); userName.setText(parsedProfileData.get(USERNAME_INDEX));
if (parsedProfileData.get(PERSONAL_TEXT_INDEX) != null) { if (parsedProfileData.get(PERSONAL_TEXT_INDEX) != null) {
personalText.setVisibility(View.VISIBLE); personalText.setVisibility(View.VISIBLE);

82
app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileParser.java

@ -9,51 +9,94 @@ import java.util.Objects;
import mthmmy.utils.Report; import mthmmy.utils.Report;
import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.PACKAGE_NAME;
/**
* Singleton used for parsing user's profile.
* <p>Class contains the methods:<ul><li>{@link #parseProfileSummary(Document)}</li>
* </ul></p>
*/
class ProfileParser { class ProfileParser {
//Other variables /**
* Debug Tag for logging debug output to LogCat
*/
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static final String TAG = "ProfileParser"; private static final String TAG = "ProfileParser";
static final int THUMBNAIL_URL = 0; /**
static final int NAME_INDEX = 1; * Index of user's thumbnail url in parsed information ArrayList
* <p><b>Not the url itself!</b></p>
*/
static final int THUMBNAIL_URL_INDEX = 0;
/**
* Index of user's username in parsed information ArrayList
* <p><b>Not the username itself!</b></p>
*/
static final int USERNAME_INDEX = 1;
/**
* Index of user's personal text in parsed information ArrayList
* <p><b>Not the text itself!</b></p>
*/
static final int PERSONAL_TEXT_INDEX = 2; static final int PERSONAL_TEXT_INDEX = 2;
static ArrayList<String> parseProfile(Document doc) { /**
* Returns an {@link ArrayList} of {@link String}s. This method is used to parse all available
* information in a user profile.
* <p>
* User's thumbnail image url, username and personal text are placed at Array's indexes defined
* by public constants THUMBNAIL_URL_INDEX, USERNAME_INDEX and PERSONAL_TEXT_INDEX respectively.
*
* @param profile {@link Document} object containing this profile's source code
* @return ArrayList containing this profile's parsed information
* @see org.jsoup.Jsoup Jsoup
*/
static ArrayList<String> parseProfileSummary(Document profile) {
//Method's variables //Method's variables
ArrayList<String> returnArray = new ArrayList<>(); ArrayList<String> parsedInformation = new ArrayList<>();
//Contains all summary's rows //Contains all summary's rows
Elements summaryRows = doc.select(".bordercolor > tbody:nth-child(1) > tr:nth-child(2) tr"); Elements summaryRows = profile.select(".bordercolor > tbody:nth-child(1) > tr:nth-child(2) tr");
{ //Find thumbnail url { //Finds thumbnail's url
Element tmpEl = doc.select(".bordercolor img.avatar").first(); Element tmpEl = profile.select(".bordercolor img.avatar").first();
if (tmpEl != null) if (tmpEl != null)
returnArray.add(THUMBNAIL_URL, tmpEl.attr("abs:src")); parsedInformation.add(THUMBNAIL_URL_INDEX, tmpEl.attr("abs:src"));
else //User doesn't have an avatar else //User doesn't have an avatar
returnArray.add(THUMBNAIL_URL, null); parsedInformation.add(THUMBNAIL_URL_INDEX, null);
} }
{ //Find username { //Finds username
Element tmpEl = summaryRows.first(); Element tmpEl = summaryRows.first();
if (tmpEl != null) { if (tmpEl != null) {
returnArray.add(NAME_INDEX, tmpEl.select("td").get(1).text()); parsedInformation.add(USERNAME_INDEX, tmpEl.select("td").get(1).text());
} else { } else {
//Should never get here! //Should never get here!
//Something is wrong. //Something is wrong.
Report.e(TAG, "An error occurred while trying to find profile's username."); Report.e(PACKAGE_NAME + "." + TAG, "An error occurred while trying to find profile's username.");
parsedInformation.add(USERNAME_INDEX, null);
} }
} }
{ //Find personal text { //Finds personal text
String tmpPersonalText = doc.select("td.windowbg:nth-child(2)").first().text().trim(); Element tmpEl = profile.select("td.windowbg:nth-child(2)").first();
returnArray.add(PERSONAL_TEXT_INDEX, tmpPersonalText); if (tmpEl != null) {
String tmpPersonalText = tmpEl.text().trim();
parsedInformation.add(PERSONAL_TEXT_INDEX, tmpPersonalText);
} else {
//Should never get here!
//Something is wrong.
Report.e(PACKAGE_NAME + "." + TAG, "An error occurred while trying to find profile's personal text.");
parsedInformation.add(PERSONAL_TEXT_INDEX, null);
}
} }
for (Element row : summaryRows) { for (Element row : summaryRows) {
String rowText = row.text(), pHtml = ""; String rowText = row.text(), pHtml = "";
//Horizontal rule rows
if (row.select("td").size() == 1) if (row.select("td").size() == 1)
pHtml = ""; pHtml = "";
else if (rowText.contains("Signature") || rowText.contains("Υπογραφή")) { else if (rowText.contains("Signature") || rowText.contains("Υπογραφή")) {
//This needs special handling since it may have css
{ //Fix embedded videos { //Fix embedded videos
Elements noembedTag = row.select("noembed"); Elements noembedTag = row.select("noembed");
ArrayList<String> embededVideosUrls = new ArrayList<>(); ArrayList<String> embededVideosUrls = new ArrayList<>();
@ -86,14 +129,15 @@ class ProfileParser {
//Add stuff to make it work in WebView //Add stuff to make it work in WebView
//style.css //style.css
pHtml = ("<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />" + pHtml); pHtml = ("<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />" + pHtml);
} else if (!rowText.contains("Name") && !rowText.contains("Όνομα")) { } else if (!rowText.contains("Name") && !rowText.contains("Όνομα")) { //Don't add username twice
if (Objects.equals(row.select("td").get(1).text(), "")) if (Objects.equals(row.select("td").get(1).text(), ""))
continue; continue;
//Style parsed information with html
pHtml = "<b>" + row.select("td").first().text() + "</b> " pHtml = "<b>" + row.select("td").first().text() + "</b> "
+ row.select("td").get(1).text(); + row.select("td").get(1).text();
} }
returnArray.add(pHtml); parsedInformation.add(pHtml);
} }
return returnArray; return parsedInformation;
} }
} }

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

@ -42,6 +42,7 @@ import gr.thmmy.mthmmy.utils.CircleTransform;
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;
import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.EXTRAS_PROFILE_URL;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.NO_POST_FOCUS; import static gr.thmmy.mthmmy.activities.topic.TopicActivity.NO_POST_FOCUS;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.base_url; import static gr.thmmy.mthmmy.activities.topic.TopicActivity.base_url;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.downloadFileAsync; import static gr.thmmy.mthmmy.activities.topic.TopicActivity.downloadFileAsync;
@ -296,7 +297,7 @@ class TopicAdapter extends RecyclerView.Adapter<TopicAdapter.MyViewHolder> {
Intent intent = new Intent(context, ProfileActivity.class); Intent intent = new Intent(context, ProfileActivity.class);
Bundle b = new Bundle(); Bundle b = new Bundle();
b.putString("PROFILE_URL", currentPost.getProfileURL()); //Profile url b.putString(EXTRAS_PROFILE_URL, currentPost.getProfileURL()); //Profile url
intent.putExtras(b); //Put url to next Intent intent.putExtras(b); //Put url to next Intent
intent.setFlags(FLAG_ACTIVITY_NEW_TASK); intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent); context.startActivity(intent);

187
app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicParser.java

@ -14,12 +14,27 @@ import java.util.Objects;
import gr.thmmy.mthmmy.data.Post; import gr.thmmy.mthmmy.data.Post;
/**
* Singleton used for parsing a topic.
* <p>Class contains the methods:<ul><li>{@link #parseUsersViewingThisTopic(Document, String)}</li>
* <li>{@link #parseCurrentPageIndex(Document, String)}</li>
* <li>{@link #parseTopicNumberOfPages(Document, int, String)}</li>
* <li>{@link #parseTopic(Document, String)}</li>
* <li>{@link #defineLanguage(Document)}</li>
* <li>(private) {@link #colorPicker(String)}</li></ul></p>
*/
class TopicParser { class TopicParser {
//Languages supported //Languages supported
/**
* String constant containing one of the supported forum languages
*/
private static final String LANGUAGE_GREEK = "Greek"; private static final String LANGUAGE_GREEK = "Greek";
/**
* String constant containing one of the supported forum languages
*/
private static final String LANGUAGE_ENGLISH = "English"; private static final String LANGUAGE_ENGLISH = "English";
//User colors variables //User colors
private static final int USER_COLOR_BLACK = Color.parseColor("#000000"); private static final int USER_COLOR_BLACK = Color.parseColor("#000000");
private static final int USER_COLOR_RED = Color.parseColor("#F44336"); private static final int USER_COLOR_RED = Color.parseColor("#F44336");
private static final int USER_COLOR_GREEN = Color.parseColor("#4CAF50"); private static final int USER_COLOR_GREEN = Color.parseColor("#4CAF50");
@ -27,64 +42,91 @@ class TopicParser {
private static final int USER_COLOR_PINK = Color.parseColor("#FF4081"); private static final int USER_COLOR_PINK = Color.parseColor("#FF4081");
private static final int USER_COLOR_YELLOW = Color.parseColor("#FFEB3B"); private static final int USER_COLOR_YELLOW = Color.parseColor("#FFEB3B");
/**
* Debug Tag for logging debug output to LogCat
*/
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static final String TAG = "TopicParser"; private static final String TAG = "TopicParser";
static String parseUsersViewingThisTopic(Document doc, String language) { /**
* Returns users currently viewing this topic.
*
* @param topic {@link Document} object containing this topic's source code
* @param language a String containing this topic's language set, this is returned by
* {@link #defineLanguage(Document)}
* @return String containing html with the usernames of users
* @see org.jsoup.Jsoup Jsoup
*/
static String parseUsersViewingThisTopic(Document topic, String language) {
if (Objects.equals(language, LANGUAGE_GREEK)) if (Objects.equals(language, LANGUAGE_GREEK))
return doc.select("td:containsOwn(διαβάζουν αυτό το θέμα)").first().html(); return topic.select("td:containsOwn(διαβάζουν αυτό το θέμα)").first().html();
return doc.select("td:containsOwn(are viewing this topic)").first().html(); return topic.select("td:containsOwn(are viewing this topic)").first().html();
} }
static int parseCurrentPageIndex(Document doc, String language) { /**
int returnPage = 1; * Returns current topic's page index.
*
* @param topic {@link Document} object containing this topic's source code
* @param language a String containing this topic's language set, this is returned by
* {@link #defineLanguage(Document)}
* @return int containing parsed topic's current page
* @see org.jsoup.Jsoup Jsoup
*/
static int parseCurrentPageIndex(Document topic, String language) {
int parsedPage = 1;
if (Objects.equals(language, LANGUAGE_GREEK)) { if (Objects.equals(language, LANGUAGE_GREEK)) {
//Contains pages Elements findCurrentPage = topic.select("td:contains(Σελίδες:)>b");
Elements findCurrentPage = doc.select("td:contains(Σελίδες:)>b");
for (Element item : findCurrentPage) { for (Element item : findCurrentPage) {
if (!item.text().contains("...") //It's not "..." if (!item.text().contains("...")
&& !item.text().contains("Σελίδες:")) { //Nor "Σελίδες:" && !item.text().contains("Σελίδες:")) {
returnPage = Integer.parseInt(item.text()); parsedPage = Integer.parseInt(item.text());
break; break;
} }
} }
} else { } else {
Elements findCurrentPage = doc.select("td:contains(Pages:)>b"); Elements findCurrentPage = topic.select("td:contains(Pages:)>b");
for (Element item : findCurrentPage) { for (Element item : findCurrentPage) {
if (!item.text().contains("...") && !item.text().contains("Pages:")) { if (!item.text().contains("...") && !item.text().contains("Pages:")) {
returnPage = Integer.parseInt(item.text()); parsedPage = Integer.parseInt(item.text());
break; break;
} }
} }
} }
return returnPage; return parsedPage;
} }
static int parseTopicNumberOfPages(Document doc, int thisPage, String language) { /**
//Method's variables * Returns the number of this topic's pages.
*
* @param topic {@link Document} object containing this topic's source code
* @param currentPage an int containing current page of this topic
* @param language a String containing this topic's language set, this is returned by
* {@link #defineLanguage(Document)}
* @return int containing the number of pages
* @see org.jsoup.Jsoup Jsoup
*/
static int parseTopicNumberOfPages(Document topic, int currentPage, String language) {
int returnPages = 1; int returnPages = 1;
if (Objects.equals(language, LANGUAGE_GREEK)) { if (Objects.equals(language, LANGUAGE_GREEK)) {
//Contains all pages Elements pages = topic.select("td:contains(Σελίδες:)>a.navPages");
Elements pages = doc.select("td:contains(Σελίδες:)>a.navPages");
if (pages.size() != 0) { if (pages.size() != 0) {
returnPages = thisPage; //Initialize the number returnPages = currentPage;
for (Element item : pages) { //Just a max for (Element item : pages) {
if (Integer.parseInt(item.text()) > returnPages) if (Integer.parseInt(item.text()) > returnPages)
returnPages = Integer.parseInt(item.text()); returnPages = Integer.parseInt(item.text());
} }
} }
} else { } else {
//Contains all pages Elements pages = topic.select("td:contains(Pages:)>a.navPages");
Elements pages = doc.select("td:contains(Pages:)>a.navPages");
if (pages.size() != 0) { if (pages.size() != 0) {
returnPages = thisPage; returnPages = currentPage;
for (Element item : pages) { for (Element item : pages) {
if (Integer.parseInt(item.text()) > returnPages) if (Integer.parseInt(item.text()) > returnPages)
returnPages = Integer.parseInt(item.text()); returnPages = Integer.parseInt(item.text());
@ -95,20 +137,30 @@ class TopicParser {
return returnPages; return returnPages;
} }
static ArrayList<Post> parseTopic(Document doc, String language) { /**
* This method parses all the information of a topic and it's posts.
*
* @param topic {@link Document} object containing this topic's source code
* @param language a String containing this topic's language set, this is returned by
* {@link #defineLanguage(Document)}
* @return {@link ArrayList} of {@link Post}s
* @see org.jsoup.Jsoup Jsoup
*/
static ArrayList<Post> parseTopic(Document topic, String language) {
//Method's variables //Method's variables
final int NO_INDEX = -1; final int NO_INDEX = -1;
ArrayList<Post> returnList = new ArrayList<>(); ArrayList<Post> parsedPostsList = new ArrayList<>();
Elements rows; Elements postRows;
//Each row is a post
if (Objects.equals(language, LANGUAGE_GREEK)) if (Objects.equals(language, LANGUAGE_GREEK))
rows = doc.select("form[id=quickModForm]>table>tbody>tr:matches(στις)"); postRows = topic.select("form[id=quickModForm]>table>tbody>tr:matches(στις)");
else { else {
rows = doc.select("form[id=quickModForm]>table>tbody>tr:matches(on)"); postRows = topic.select("form[id=quickModForm]>table>tbody>tr:matches(on)");
} }
for (Element item : rows) { //For every post for (Element item : postRows) {
//Variables to pass //Variables for Post constructor
String p_userName, p_thumbnailUrl, p_subject, p_post, p_postDate, p_profileURL, p_rank, String p_userName, p_thumbnailUrl, p_subject, p_post, p_postDate, p_profileURL, p_rank,
p_specialRank, p_gender, p_personalText, p_numberOfPosts; p_specialRank, p_gender, p_personalText, p_numberOfPosts;
int p_postNum, p_postIndex, p_numberOfStars, p_userColor; int p_postNum, p_postIndex, p_numberOfStars, p_userColor;
@ -127,20 +179,20 @@ class TopicParser {
p_attachedFiles = new ArrayList<>(); p_attachedFiles = new ArrayList<>();
//Language independent parsing //Language independent parsing
//Find thumbnail url //Finds thumbnail url
Element thumbnailUrl = item.select("img.avatar").first(); Element thumbnailUrl = item.select("img.avatar").first();
p_thumbnailUrl = null; //In case user doesn't have an avatar p_thumbnailUrl = null; //In case user doesn't have an avatar
if (thumbnailUrl != null) { if (thumbnailUrl != null) {
p_thumbnailUrl = thumbnailUrl.attr("abs:src"); p_thumbnailUrl = thumbnailUrl.attr("abs:src");
} }
//Find subject //Finds subject
p_subject = item.select("div[id^=subject_]").first().select("a").first().text(); p_subject = item.select("div[id^=subject_]").first().select("a").first().text();
//Find post's text //Finds post's text
p_post = item.select("div").select(".post").first().outerHtml(); p_post = item.select("div").select(".post").first().outerHtml();
{ //Fix embedded videos { //Fixes embedded videos
Elements noembedTag = item.select("div").select(".post").first().select("noembed"); Elements noembedTag = item.select("div").select(".post").first().select("noembed");
ArrayList<String> embededVideosUrls = new ArrayList<>(); ArrayList<String> embededVideosUrls = new ArrayList<>();
@ -172,6 +224,8 @@ class TopicParser {
p_post = ("<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />" + p_post); p_post = ("<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />" + p_post);
//Find post's index //Find post's index
//This is an int assigned by the forum used for post focusing and quotes, it is not
//the same as reply index.
Element postIndex = item.select("a[name^=msg]").first(); Element postIndex = item.select("a[name^=msg]").first();
if (postIndex == null) if (postIndex == null)
p_postIndex = NO_INDEX; p_postIndex = NO_INDEX;
@ -183,7 +237,7 @@ class TopicParser {
//Language dependent parsing //Language dependent parsing
Element userName; Element userName;
if (Objects.equals(language, LANGUAGE_GREEK)) { if (Objects.equals(language, LANGUAGE_GREEK)) {
//Find username //Finds username and profile's url
userName = item.select("a[title^=Εμφάνιση προφίλ του μέλους]").first(); userName = item.select("a[title^=Εμφάνιση προφίλ του μέλους]").first();
if (userName == null) { //Deleted profile if (userName == null) { //Deleted profile
p_isDeleted = true; p_isDeleted = true;
@ -197,13 +251,13 @@ class TopicParser {
p_profileURL = userName.attr("href"); p_profileURL = userName.attr("href");
} }
//Find post's submit date //Finds post's submit date
Element postDate = item.select("div.smalltext:matches(στις:)").first(); Element postDate = item.select("div.smalltext:matches(στις:)").first();
p_postDate = postDate.text(); p_postDate = postDate.text();
p_postDate = p_postDate.substring(p_postDate.indexOf("στις:") + 6 p_postDate = p_postDate.substring(p_postDate.indexOf("στις:") + 6
, p_postDate.indexOf(" »")); , p_postDate.indexOf(" »"));
//Find post's number //Finds post's reply index number
Element postNum = item.select("div.smalltext:matches(Απάντηση #)").first(); Element postNum = item.select("div.smalltext:matches(Απάντηση #)").first();
if (postNum == null) { //Topic starter if (postNum == null) { //Topic starter
p_postNum = 0; p_postNum = 0;
@ -213,7 +267,7 @@ class TopicParser {
} }
//Find attached file's urls, names and info, if present //Finds attached file's urls, names and info, if present
Elements postAttachments = item.select("div:containsOwn(έγινε λήψη):containsOwn(φορές.)"); Elements postAttachments = item.select("div:containsOwn(έγινε λήψη):containsOwn(φορές.)");
if (postAttachments != null) { if (postAttachments != null) {
Elements attachedFiles = postAttachments.select("a"); Elements attachedFiles = postAttachments.select("a");
@ -222,12 +276,12 @@ class TopicParser {
for (int i = 0; i < attachedFiles.size(); ++i) { for (int i = 0; i < attachedFiles.size(); ++i) {
String[] attachedArray = new String[3]; String[] attachedArray = new String[3];
//Get file's url and filename //Gets file's url and filename
Element tmpAttachedFileUrlAndName = attachedFiles.get(i); Element tmpAttachedFileUrlAndName = attachedFiles.get(i);
attachedArray[0] = tmpAttachedFileUrlAndName.attr("href"); attachedArray[0] = tmpAttachedFileUrlAndName.attr("href");
attachedArray[1] = tmpAttachedFileUrlAndName.text().substring(1); attachedArray[1] = tmpAttachedFileUrlAndName.text().substring(1);
//Get file's info (size and download count) //Gets file's info (size and download count)
String postAttachmentsTextSbstr = postAttachmentsText.substring( String postAttachmentsTextSbstr = postAttachmentsText.substring(
postAttachmentsText.indexOf(attachedArray[1])); postAttachmentsText.indexOf(attachedArray[1]));
@ -238,7 +292,7 @@ class TopicParser {
} }
} }
} else { } else {
//Find username //Finds username
userName = item.select("a[title^=View the profile of]").first(); userName = item.select("a[title^=View the profile of]").first();
if (userName == null) { //Deleted profile if (userName == null) { //Deleted profile
p_isDeleted = true; p_isDeleted = true;
@ -252,13 +306,13 @@ class TopicParser {
p_profileURL = userName.attr("href"); p_profileURL = userName.attr("href");
} }
//Find post's submit date //Finds post's submit date
Element postDate = item.select("div.smalltext:matches(on:)").first(); Element postDate = item.select("div.smalltext:matches(on:)").first();
p_postDate = postDate.text(); p_postDate = postDate.text();
p_postDate = p_postDate.substring(p_postDate.indexOf("on:") + 4 p_postDate = p_postDate.substring(p_postDate.indexOf("on:") + 4
, p_postDate.indexOf(" »")); , p_postDate.indexOf(" »"));
//Find post's number //Finds post's reply index number
Element postNum = item.select("div.smalltext:matches(Reply #)").first(); Element postNum = item.select("div.smalltext:matches(Reply #)").first();
if (postNum == null) { //Topic starter if (postNum == null) { //Topic starter
p_postNum = 0; p_postNum = 0;
@ -268,7 +322,7 @@ class TopicParser {
} }
//Find attached file's urls, names and info, if present //Finds attached file's urls, names and info, if present
Elements postAttachments = item.select("div:containsOwn(downloaded):containsOwn(times.)"); Elements postAttachments = item.select("div:containsOwn(downloaded):containsOwn(times.)");
if (postAttachments != null) { if (postAttachments != null) {
Elements attachedFiles = postAttachments.select("a"); Elements attachedFiles = postAttachments.select("a");
@ -277,12 +331,12 @@ class TopicParser {
for (int i = 0; i < attachedFiles.size(); ++i) { for (int i = 0; i < attachedFiles.size(); ++i) {
String[] attachedArray = new String[3]; String[] attachedArray = new String[3];
//Get file's url and filename //Gets file's url and filename
Element tmpAttachedFileUrlAndName = attachedFiles.get(i); Element tmpAttachedFileUrlAndName = attachedFiles.get(i);
attachedArray[0] = tmpAttachedFileUrlAndName.attr("href"); attachedArray[0] = tmpAttachedFileUrlAndName.attr("href");
attachedArray[1] = tmpAttachedFileUrlAndName.text().substring(1); attachedArray[1] = tmpAttachedFileUrlAndName.text().substring(1);
//Get file's info (size and download count) //Gets file's info (size and download count)
String postAttachmentsTextSbstr = postAttachmentsText.substring( String postAttachmentsTextSbstr = postAttachmentsText.substring(
postAttachmentsText.indexOf(attachedArray[1])); postAttachmentsText.indexOf(attachedArray[1]));
@ -295,12 +349,12 @@ class TopicParser {
} }
if (!p_isDeleted) { //Active user if (!p_isDeleted) { //Active user
//Get extra info //Gets extra info
int postsLineIndex = -1; int postsLineIndex = -1;
int starsLineIndex = -1; int starsLineIndex = -1;
Element info = userName.parent().nextElementSibling(); //Get sibling "div" Element usersExtraInfo = userName.parent().nextElementSibling(); //Get sibling "div"
List<String> infoList = Arrays.asList(info.html().split("<br>")); List<String> infoList = Arrays.asList(usersExtraInfo.html().split("<br>"));
if (Objects.equals(language, LANGUAGE_GREEK)) { if (Objects.equals(language, LANGUAGE_GREEK)) {
for (String line : infoList) { for (String line : infoList) {
@ -349,7 +403,6 @@ class TopicParser {
//If this member has no stars yet ==> New member, //If this member has no stars yet ==> New member,
//or is just a member //or is just a member
if (starsLineIndex == -1 || starsLineIndex == 1) { if (starsLineIndex == -1 || starsLineIndex == 1) {
//In this case:
p_rank = infoList.get(0).trim(); //First line has the rank p_rank = infoList.get(0).trim(); //First line has the rank
p_specialRank = null; //They don't have a special rank p_specialRank = null; //They don't have a special rank
} else if (starsLineIndex == 2) { //This member has a special rank } else if (starsLineIndex == 2) { //This member has a special rank
@ -357,43 +410,53 @@ class TopicParser {
p_rank = infoList.get(1).trim(); //Second line has the rank p_rank = infoList.get(1).trim(); //Second line has the rank
} }
for (int i = postsLineIndex + 1; i < infoList.size() - 1; ++i) { for (int i = postsLineIndex + 1; i < infoList.size() - 1; ++i) {
//Search under "Posts:" //Searches under "Posts:"
//and above "Personal Message", "View Profile" etc buttons //and above "Personal Message", "View Profile" etc buttons
String thisLine = infoList.get(i); String thisLine = infoList.get(i);
//If this line isn't empty and doesn't contain user's avatar
if (!Objects.equals(thisLine, "") && thisLine != null if (!Objects.equals(thisLine, "") && thisLine != null
&& !Objects.equals(thisLine, " \n") && !Objects.equals(thisLine, " \n")
&& !thisLine.contains("avatar") && !thisLine.contains("avatar")
&& !thisLine.contains("<a href=")) { && !thisLine.contains("<a href=")) {
p_personalText = thisLine; //Then this line has user's personal text p_personalText = thisLine;
//Remove any line breaks and spaces on the start and end
p_personalText = p_personalText.replace("\n", "").replace("\r", "").trim(); p_personalText = p_personalText.replace("\n", "").replace("\r", "").trim();
} }
} }
//Add new post in postsList, extended information needed //Add new post in postsList, extended information needed
returnList.add(new Post(p_thumbnailUrl, p_userName, p_subject, p_post, p_postIndex 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_postNum, p_postDate, p_profileURL, p_rank, p_specialRank, p_gender
, p_numberOfPosts, p_personalText, p_numberOfStars, p_userColor , p_numberOfPosts, p_personalText, p_numberOfStars, p_userColor
, p_attachedFiles)); , p_attachedFiles));
} else { //Deleted user } else { //Deleted user
//Add new post in postsList, only standard information needed //Add new post in postsList, only standard information needed
returnList.add(new Post(p_thumbnailUrl, p_userName, p_subject, p_post, p_postIndex parsedPostsList.add(new Post(p_thumbnailUrl, p_userName, p_subject, p_post, p_postIndex
, p_postNum, p_postDate, p_userColor, p_attachedFiles)); , p_postNum, p_postDate, p_userColor, p_attachedFiles));
} }
} }
return returnList; return parsedPostsList;
} }
static String defineLanguage(Document doc) { /**
if (doc.select("h3").text().contains("Καλώς ορίσατε")) { * Returns one of the supported forum languages.
* <p>Forum supports: <ul><li>{@link #LANGUAGE_ENGLISH}</li>
* <li>{@link #LANGUAGE_GREEK}</li></ul></p>
* @param topic {@link Document} object containing this topic's source code
* @return String containing the language of a topic
* @see org.jsoup.Jsoup Jsoup
*/
static String defineLanguage(Document topic) {
if (topic.select("h3").text().contains("Καλώς ορίσατε")) {
return LANGUAGE_GREEK; return LANGUAGE_GREEK;
} else { //Default is english (eg. guest's language) } else { //Default is english (eg. guest's language)
return LANGUAGE_ENGLISH; return LANGUAGE_ENGLISH;
} }
} }
/**
* Returns the color of a user according to user's rank on forum.
* @param starsUrl String containing the URL of a user's stars
* @return an int corresponding to the right color
*/
private static int colorPicker(String starsUrl) { private static int colorPicker(String starsUrl) {
if (starsUrl.contains("/star.gif")) if (starsUrl.contains("/star.gif"))
return USER_COLOR_YELLOW; return USER_COLOR_YELLOW;

19
app/src/main/java/gr/thmmy/mthmmy/utils/CircleTransform.java

@ -1,19 +1,4 @@
package gr.thmmy.mthmmy.utils; package gr.thmmy.mthmmy.utils;
/*
* Copyright 2014 Julian Shen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapShader; import android.graphics.BitmapShader;
@ -22,6 +7,10 @@ import android.graphics.Paint;
import com.squareup.picasso.Transformation; import com.squareup.picasso.Transformation;
/**
* Used as parameter for PICASSO library's {@link com.squareup.picasso.RequestCreator#transform(Transformation) transform} method.
* @see com.squareup.picasso.Picasso Picasso
*/
public class CircleTransform implements Transformation { public class CircleTransform implements Transformation {
@Override @Override
public Bitmap transform(Bitmap source) { public Bitmap transform(Bitmap source) {

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

@ -7,6 +7,10 @@ import android.support.v4.view.ViewCompat;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View; import android.view.View;
/**
* Extends FloatingActionButton's behavior so the button will hide when scrolling down and show
* otherwise.
*/
public class ScrollAwareFABBehavior extends CoordinatorLayout.Behavior<FloatingActionButton> { public class ScrollAwareFABBehavior extends CoordinatorLayout.Behavior<FloatingActionButton> {
public ScrollAwareFABBehavior(Context context, AttributeSet attrs) { public ScrollAwareFABBehavior(Context context, AttributeSet attrs) {
super(); super();

Loading…
Cancel
Save