From eeb0db0a9fbde6cb6b9e3334b567467653414b24 Mon Sep 17 00:00:00 2001 From: Apostolof Date: Mon, 2 Jan 2017 13:38:50 +0200 Subject: [PATCH 1/4] Work in progress: profile activity tabs --- .../activities/profile/ProfileActivity.java | 34 +-- .../latestPosts/LatestPostsAdapter.java | 8 + .../latestPosts/LatestPostsFragment.java | 8 + .../profile/stats/StatsFragment.java | 8 + .../profile/summary/SummaryFragment.java | 235 ++++++++++++++++++ .../activities/topic/TopicActivity.java | 2 +- .../main/res/layout-v21/activity_profile.xml | 25 +- app/src/main/res/layout/activity_profile.xml | 28 +-- .../layout/profile_fragment_latest_posts.xml | 7 + .../res/layout/profile_fragment_stats.xml | 7 + .../res/layout/profile_fragment_summary.xml | 36 +++ 11 files changed, 335 insertions(+), 63 deletions(-) create mode 100644 app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java create mode 100644 app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java create mode 100644 app/src/main/java/gr/thmmy/mthmmy/activities/profile/stats/StatsFragment.java create mode 100644 app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java create mode 100644 app/src/main/res/layout/profile_fragment_latest_posts.xml create mode 100644 app/src/main/res/layout/profile_fragment_stats.xml create mode 100644 app/src/main/res/layout/profile_fragment_summary.xml 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 e48ac81a..0c73e611 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 @@ -8,6 +8,7 @@ import android.os.Build; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; import android.support.v4.content.res.ResourcesCompat; +import android.support.v4.view.ViewPager; import android.support.v7.app.AlertDialog; import android.support.v7.widget.Toolbar; import android.text.Html; @@ -53,9 +54,9 @@ public class ProfileActivity extends BaseActivity { private ImageView userThumbnail; private TextView userName; private TextView personalText; - private LinearLayout mainContent; private MaterialProgressBar progressBar; private FloatingActionButton replyFAB; + private ViewPager viewPager; //Other variables /** @@ -97,9 +98,8 @@ public class ProfileActivity extends BaseActivity { progressBar = (MaterialProgressBar) findViewById(R.id.progressBar); userThumbnail = (ImageView) findViewById(R.id.user_thumbnail); - userName = (TextView) findViewById(R.id.profile_act_username); + userName = (TextView) findViewById(R.id.profile_activity_username); personalText = (TextView) findViewById(R.id.profile_activity_personal_text); - mainContent = (LinearLayout) findViewById(R.id.profile_activity_content); replyFAB = (FloatingActionButton) findViewById(R.id.profile_fab); //TODO hide fab while logged out replyFAB.setEnabled(false); @@ -149,7 +149,7 @@ public class ProfileActivity extends BaseActivity { * data. {@link AsyncTask#onPostExecute(Object) OnPostExecute} method calls {@link #populateLayout()} * to build graphics. *

- *

Calling ProfileTask's {@link AsyncTask#execute execute} method needs to have profile's url + *

Calling ProfileSummaryTask's {@link AsyncTask#execute execute} method needs to have profile's url * as String parameter!

*/ public class ProfileTask extends AsyncTask { @@ -225,31 +225,5 @@ public class ProfileActivity extends BaseActivity { } else { personalText.setVisibility(View.GONE); } - - for (int i = PERSONAL_TEXT_INDEX + 1; i < parsedProfileData.size(); ++i) { - if (parsedProfileData.get(i).contains("Signature") - || parsedProfileData.get(i).contains("Υπογραφή")) { - WebView signatureEntry = new WebView(this); - signatureEntry.loadDataWithBaseURL("file:///android_asset/", parsedProfileData.get(i), "text/html", "UTF-8", null); - } - TextView entry = new TextView(this); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - entry.setTextColor(getResources().getColor(R.color.primary_text, null)); - } else { - //noinspection deprecation - entry.setTextColor(getResources().getColor(R.color.primary_text)); - - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - entry.setText(Html.fromHtml(parsedProfileData.get(i), Html.FROM_HTML_MODE_LEGACY)); - } else { - //noinspection deprecation - entry.setText(Html.fromHtml(parsedProfileData.get(i))); - } - - mainContent.addView(entry); - Log.d(TAG, "new: " + parsedProfileData.get(i)); - } } } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java new file mode 100644 index 00000000..607fce64 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java @@ -0,0 +1,8 @@ +package gr.thmmy.mthmmy.activities.profile.latestPosts; + +/** + * Created by apostolof on 1/1/17. + */ + +public class LatestPostsAdapter { +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java new file mode 100644 index 00000000..cd2692c7 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java @@ -0,0 +1,8 @@ +package gr.thmmy.mthmmy.activities.profile.latestPosts; + +/** + * Created by apostolof on 1/1/17. + */ + +public class LatestPostsFragment { +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/stats/StatsFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/stats/StatsFragment.java new file mode 100644 index 00000000..b16139c4 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/stats/StatsFragment.java @@ -0,0 +1,8 @@ +package gr.thmmy.mthmmy.activities.profile.stats; + +/** + * Created by apostolof on 1/1/17. + */ + +public class StatsFragment { +} diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java new file mode 100644 index 00000000..c5b41d62 --- /dev/null +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java @@ -0,0 +1,235 @@ +package gr.thmmy.mthmmy.activities.profile.summary; + +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.text.Html; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.webkit.WebView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.util.ArrayList; +import java.util.Objects; + +import gr.thmmy.mthmmy.R; +import gr.thmmy.mthmmy.activities.base.BaseFragment; +import gr.thmmy.mthmmy.activities.profile.ProfileActivity; +import me.zhanghai.android.materialprogressbar.MaterialProgressBar; +import mthmmy.utils.Report; + + +/** + * A {@link BaseFragment} subclass. + * Use the {@link SummaryFragment#newInstance} factory method to + * create an instance of this fragment. + */ +public class SummaryFragment extends BaseFragment { + static final String PROFILE_DOCUMENT = "PROFILE_DOCUMENT"; + private static final String TAG = "SummaryFragment"; + /** + * {@link ArrayList} of Strings used to hold profile's information. Data are added in {@link ProfileActivity.ProfileTask}. + */ + private Document profileSummaryDocument; + private ProfileSummaryTask profileSummaryTask; + private ArrayList parsedProfileSummaryData; + private LinearLayout mainContent; + private MaterialProgressBar progressBar; + + public SummaryFragment() { + // Required empty public constructor + this.profileSummaryDocument = Jsoup.parse(getArguments().getString(PROFILE_DOCUMENT)); + } + + /** + * Use ONLY this factory method to create a new instance of + * this fragment using the provided parameters. + * + * @return A new instance of fragment Summary. + */ + public static SummaryFragment newInstance(int sectionNumber, Document profileSummaryDocument) { + SummaryFragment fragment = new SummaryFragment(); + Bundle args = new Bundle(); + args.putString(ARG_TAG, TAG); + args.putInt(ARG_SECTION_NUMBER, sectionNumber); + args.putString(PROFILE_DOCUMENT, profileSummaryDocument.toString()); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + parsedProfileSummaryData = new ArrayList<>(); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + if (parsedProfileSummaryData.isEmpty()) { + profileSummaryTask = new ProfileSummaryTask(); + profileSummaryTask.execute(profileSummaryDocument); + + } + Report.d(TAG, "onActivityCreated"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (profileSummaryTask != null && profileSummaryTask.getStatus() != AsyncTask.Status.RUNNING) + profileSummaryTask.cancel(true); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + final View rootView = inflater.inflate(R.layout.profile_fragment_summary, container, false); + progressBar = (MaterialProgressBar) rootView.findViewById(R.id.progressBar); + mainContent = (LinearLayout) rootView.findViewById(R.id.profile_activity_content); + populateLayout(); + return rootView; + } + + /** + * 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. + *

+ *

Calling ProfileSummaryTask's {@link AsyncTask#execute execute} method needs to have profile's url + * as String parameter!

+ */ + public class ProfileSummaryTask extends AsyncTask { + //Class variables + /** + * Debug Tag for logging debug output to LogCat + */ + private static final String TAG = "TopicTask"; //Separate tag for AsyncTask + + protected void onPreExecute() { + progressBar.setVisibility(MaterialProgressBar.VISIBLE); + } + + protected Void doInBackground(Document... profileSummaryPage) { + parsedProfileSummaryData = parseProfileSummary(profileSummaryPage[0]); + return null; + } + + protected void onPostExecute(Void result) { + progressBar.setVisibility(MaterialProgressBar.INVISIBLE); + populateLayout(); + } + + /** + * Returns an {@link ArrayList} of {@link String}s. This method is used to parse all available + * information in a user profile. + *

+ * 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 + */ + ArrayList parseProfileSummary(Document profile) { + //Method's variables + ArrayList parsedInformation = new ArrayList<>(); + + //Contains all summary's rows + Elements summaryRows = profile.select(".bordercolor > tbody:nth-child(1) > tr:nth-child(2) tr"); + + for (Element row : summaryRows) { + String rowText = row.text(), pHtml = ""; + + //Horizontal rule rows + if (row.select("td").size() == 1) + pHtml = ""; + else if (rowText.contains("Signature") || rowText.contains("Υπογραφή")) { + //This needs special handling since it may have css + { //Fix embedded videos + Elements noembedTag = row.select("noembed"); + ArrayList embededVideosUrls = new ArrayList<>(); + + for (Element _noembed : noembedTag) { + embededVideosUrls.add(_noembed.text().substring(_noembed.text() + .indexOf("href=\"https://www.youtube.com/watch?") + 38 + , _noembed.text().indexOf("target") - 2)); + } + + pHtml = row.html(); + + int tmp_counter = 0; + while (pHtml.contains(" embededVideosUrls.size()) + break; + pHtml = pHtml.replace( + pHtml.substring(pHtml.indexOf("") + 9) + , "

" + + "" + + "\"\"" + + "" + //+ "" + + "
"); + } + } + + //Add stuff to make it work in WebView + //style.css + pHtml = ("" + pHtml); + } else if (!rowText.contains("Name") && !rowText.contains("Όνομα")) { //Don't add username twice + if (Objects.equals(row.select("td").get(1).text(), "")) + continue; + //Style parsed information with html + pHtml = "" + row.select("td").first().text() + " " + + row.select("td").get(1).text(); + } + parsedInformation.add(pHtml); + } + return parsedInformation; + } + } + + /** + * Simple method that builds the UI of a {@link ProfileActivity}. + *

Use this method only after parsing profile's data with + * {@link gr.thmmy.mthmmy.activities.profile.ProfileActivity.ProfileTask} as it reads from + * {@link #parsedProfileSummaryData}

+ */ + private void populateLayout() { + for (String profileSummaryRow : parsedProfileSummaryData) { + if (profileSummaryRow.contains("Signature") + || profileSummaryRow.contains("Υπογραφή")) { + WebView signatureEntry = new WebView(this.getContext()); + signatureEntry.loadDataWithBaseURL("file:///android_asset/", profileSummaryRow, "text/html", "UTF-8", null); + } + TextView entry = new TextView(this.getContext()); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + entry.setTextColor(getResources().getColor(R.color.primary_text, null)); + } else { + //noinspection deprecation + entry.setTextColor(getResources().getColor(R.color.primary_text)); + + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + entry.setText(Html.fromHtml(profileSummaryRow, Html.FROM_HTML_MODE_LEGACY)); + } else { + //noinspection deprecation + entry.setText(Html.fromHtml(profileSummaryRow)); + } + + mainContent.addView(entry); + Log.d(TAG, "new: " + profileSummaryRow); + } + } +} 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 a2573910..7e9e540b 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 @@ -309,7 +309,7 @@ public class TopicActivity extends BaseActivity { * data. {@link AsyncTask#onPostExecute(Object) OnPostExecute} method calls {@link RecyclerView#swapAdapter} * to build graphics. *

- *

Calling ProfileTask's {@link AsyncTask#execute execute} method needs to have profile's url + *

Calling TopicTask's {@link AsyncTask#execute execute} method needs to have profile's url * as String parameter!

*/ class TopicTask extends AsyncTask { diff --git a/app/src/main/res/layout-v21/activity_profile.xml b/app/src/main/res/layout-v21/activity_profile.xml index ddda7c59..aaaec83b 100644 --- a/app/src/main/res/layout-v21/activity_profile.xml +++ b/app/src/main/res/layout-v21/activity_profile.xml @@ -64,36 +64,29 @@ app:popupTheme="@style/ToolbarTheme"> + + - - - - - + app:layout_behavior="@string/appbar_scrolling_view_behavior"/> + + - - - - - + app:layout_behavior="@string/appbar_scrolling_view_behavior"/> + + + \ No newline at end of file diff --git a/app/src/main/res/layout/profile_fragment_stats.xml b/app/src/main/res/layout/profile_fragment_stats.xml new file mode 100644 index 00000000..fb3d8a25 --- /dev/null +++ b/app/src/main/res/layout/profile_fragment_stats.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/profile_fragment_summary.xml b/app/src/main/res/layout/profile_fragment_summary.xml new file mode 100644 index 00000000..0b8b479f --- /dev/null +++ b/app/src/main/res/layout/profile_fragment_summary.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + \ No newline at end of file From cfd19618d73c8cc226c44bf1f7d8038a5bf30c68 Mon Sep 17 00:00:00 2001 From: Apostolof Date: Mon, 2 Jan 2017 16:05:13 +0200 Subject: [PATCH 2/4] Work in progress: summary tab completed --- .../activities/profile/ProfileActivity.java | 172 +++++++++++------- .../activities/profile/ProfileParser.java | 143 --------------- .../profile/summary/SummaryFragment.java | 59 +++--- .../mthmmy/activities/topic/TopicAdapter.java | 15 +- .../main/res/layout-v21/activity_profile.xml | 2 +- app/src/main/res/layout/activity_profile.xml | 2 +- .../profile_fragment_latest_posts_row.xml | 7 + .../res/layout/profile_fragment_summary.xml | 43 ++--- 8 files changed, 165 insertions(+), 278 deletions(-) delete mode 100644 app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileParser.java create mode 100644 app/src/main/res/layout/profile_fragment_latest_posts_row.xml 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 0c73e611..c85795d0 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 @@ -2,21 +2,19 @@ package gr.thmmy.mthmmy.activities.profile; import android.content.DialogInterface; import android.content.Intent; -import android.content.SharedPreferences; import android.os.AsyncTask; -import android.os.Build; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; +import android.support.design.widget.TabLayout; +import android.support.v4.app.Fragment; +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.AlertDialog; import android.support.v7.widget.Toolbar; -import android.text.Html; -import android.util.Log; import android.view.View; -import android.webkit.WebView; import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; @@ -25,64 +23,65 @@ import com.squareup.picasso.Picasso; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; import java.util.ArrayList; +import java.util.List; import javax.net.ssl.SSLHandshakeException; import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.activities.LoginActivity; import gr.thmmy.mthmmy.activities.base.BaseActivity; +import gr.thmmy.mthmmy.activities.profile.summary.SummaryFragment; import gr.thmmy.mthmmy.utils.CircleTransform; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; import mthmmy.utils.Report; import okhttp3.Request; import okhttp3.Response; -import static gr.thmmy.mthmmy.activities.profile.ProfileParser.PERSONAL_TEXT_INDEX; -import static gr.thmmy.mthmmy.activities.profile.ProfileParser.THUMBNAIL_URL_INDEX; -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; - /** - * Activity for user's profile. When creating an Intent of this activity you need to bundle a String - * containing this user's profile url using the key {@link #EXTRAS_PROFILE_URL}. + * Activity for user profile. When creating an Intent of this activity you need to bundle a String + * containing this user's profile url using the key {@link #BUNDLE_PROFILE_URL}, a String containing + * this user's avatar url and a String containing the username. */ public class ProfileActivity extends BaseActivity { - //Graphic element variables - private ImageView userThumbnail; - private TextView userName; - private TextView personalText; + //Graphics + private TextView personalTextView; private MaterialProgressBar progressBar; private FloatingActionButton replyFAB; private ViewPager viewPager; - - //Other variables + //Other variables and constants /** * Debug Tag for logging debug output to LogCat */ @SuppressWarnings("unused") private static final String TAG = "ProfileActivity"; - 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"; + public static final String BUNDLE_PROFILE_URL = "PROFILE_URL"; /** - * {@link ArrayList} of Strings used to hold profile's information. Data are added in {@link ProfileTask}. + * The key to use when putting user's thumbnail url String to {@link ProfileActivity}'s Bundle. + * If user doesn't have a thumbnail put an empty string. */ - private ArrayList parsedProfileData; - private ProfileTask profileTask; + public static final String BUNDLE_THUMBNAIL_URL = "THUMBNAIL_URL"; + /** + * The key to use when putting username String to {@link ProfileActivity}'s Bundle. + */ + public static final String BUNDLE_USERNAME = "USERNAME"; private static final int THUMBNAIL_SIZE = 200; + private ProfileTask profileTask; + private String personalText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_profile); - PACKAGE_NAME = getApplicationContext().getPackageName(); Bundle extras = getIntent().getExtras(); + String thumbnailUrl = extras.getString(BUNDLE_THUMBNAIL_URL); + String username = extras.getString(BUNDLE_USERNAME); //Initializes graphic elements toolbar = (Toolbar) findViewById(R.id.toolbar); @@ -97,9 +96,24 @@ public class ProfileActivity extends BaseActivity { progressBar = (MaterialProgressBar) findViewById(R.id.progressBar); - userThumbnail = (ImageView) findViewById(R.id.user_thumbnail); - userName = (TextView) findViewById(R.id.profile_activity_username); - personalText = (TextView) findViewById(R.id.profile_activity_personal_text); + ImageView thumbnailView = (ImageView) findViewById(R.id.user_thumbnail); + if (thumbnailUrl != null) + //noinspection ConstantConditions + Picasso.with(this) + .load(thumbnailUrl) + .resize(THUMBNAIL_SIZE, THUMBNAIL_SIZE) + .centerCrop() + .error(ResourcesCompat.getDrawable(this.getResources() + , R.drawable.ic_default_user_thumbnail, null)) + .placeholder(ResourcesCompat.getDrawable(this.getResources() + , R.drawable.ic_default_user_thumbnail, null)) + .transform(new CircleTransform()) + .into(thumbnailView); + TextView usernameView = (TextView) findViewById(R.id.profile_activity_username); + usernameView.setText(username); + personalTextView = (TextView) findViewById(R.id.profile_activity_personal_text); + + viewPager = (ViewPager) findViewById(R.id.profile_tab_container); replyFAB = (FloatingActionButton) findViewById(R.id.profile_fab); //TODO hide fab while logged out replyFAB.setEnabled(false); @@ -131,10 +145,8 @@ public class ProfileActivity extends BaseActivity { } }); - //Gets info - parsedProfileData = new ArrayList<>(); profileTask = new ProfileTask(); - profileTask.execute(extras.getString(EXTRAS_PROFILE_URL)); //Attempt data parsing + profileTask.execute(extras.getString(BUNDLE_PROFILE_URL)); //Attempt data parsing } @Override @@ -145,19 +157,19 @@ public class ProfileActivity extends BaseActivity { } /** - * 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. - *

- *

Calling ProfileSummaryTask's {@link AsyncTask#execute execute} method needs to have profile's url - * as String parameter!

+ * An {@link AsyncTask} that handles asynchronous fetching of a profile page and parsing this + * user's personal text. + *

ProfileTask's {@link AsyncTask#execute execute} method needs a profile's url as String + * parameter!

*/ public class ProfileTask extends AsyncTask { //Class variables /** * Debug Tag for logging debug output to LogCat */ - private static final String TAG = "TopicTask"; //Separate tag for AsyncTask + @SuppressWarnings("unused") + private static final String TAG = "ProfileTask"; //Separate tag for AsyncTask + Document profilePage; protected void onPreExecute() { progressBar.setVisibility(ProgressBar.VISIBLE); @@ -165,7 +177,6 @@ public class ProfileActivity extends BaseActivity { } protected Boolean doInBackground(String... profileUrl) { - Document document; String pageUrl = profileUrl[0] + ";wap"; //Profile's page wap url Request request = new Request.Builder() @@ -173,10 +184,18 @@ public class ProfileActivity extends BaseActivity { .build(); try { Response response = client.newCall(request).execute(); - document = Jsoup.parse(response.body().string()); - //long parseStartTime = System.nanoTime(); - parsedProfileData = parseProfileSummary(document); - //long parseEndTime = System.nanoTime(); + profilePage = Jsoup.parse(response.body().string()); + { //Finds personal text + Element tmpEl = profilePage.select("td.windowbg:nth-child(2)").first(); + if (tmpEl != null) { + personalText = tmpEl.text().trim(); + } else { + //Should never get here! + //Something is wrong. + Report.e(TAG, "An error occurred while trying to find profile's personal text."); + personalText = null; + } + } return true; } catch (SSLHandshakeException e) { Report.w(TAG, "Certificate problem (please switch to unsafe connection)."); @@ -194,36 +213,55 @@ public class ProfileActivity extends BaseActivity { } //Parse was successful progressBar.setVisibility(ProgressBar.INVISIBLE); - populateLayout(); + + if (personalText != null) { + personalTextView.setVisibility(View.VISIBLE); + personalTextView.setText(personalText); + } else { + personalTextView.setVisibility(View.GONE); + } + + setupViewPager(viewPager, profilePage); + TabLayout tabLayout = (TabLayout) findViewById(R.id.profile_tabs); + tabLayout.setupWithViewPager(viewPager); } } /** - * Simple method that builds the UI of a {@link ProfileActivity}. - *

Use this method only after parsing profile's data with {@link ProfileTask} as it - * reads from {@link #parsedProfileData}

+ * Simple method that sets up the {@link ViewPager} of a {@link ProfileActivity} + * @param viewPager the ViewPager to be setup + * @param profilePage this profile's parsed page */ - private void populateLayout() { - if (parsedProfileData.get(THUMBNAIL_URL_INDEX) != null) - //noinspection ConstantConditions - Picasso.with(this) - .load(parsedProfileData.get(THUMBNAIL_URL_INDEX)) - .resize(THUMBNAIL_SIZE, THUMBNAIL_SIZE) - .centerCrop() - .error(ResourcesCompat.getDrawable(this.getResources() - , R.drawable.ic_default_user_thumbnail, null)) - .placeholder(ResourcesCompat.getDrawable(this.getResources() - , R.drawable.ic_default_user_thumbnail, null)) - .transform(new CircleTransform()) - .into(userThumbnail); + private void setupViewPager(ViewPager viewPager, Document profilePage) { + ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager()); + adapter.addFrag(SummaryFragment.newInstance(profilePage), "SUMMARY"); + //adapter.addFrag(new ); + //adapter.addFrag(new ); + viewPager.setAdapter(adapter); + } - userName.setText(parsedProfileData.get(USERNAME_INDEX)); + class ViewPagerAdapter extends FragmentPagerAdapter { + private final List mFragmentList = new ArrayList<>(); + private final List mFragmentTitleList = new ArrayList<>(); - if (parsedProfileData.get(PERSONAL_TEXT_INDEX) != null) { - personalText.setVisibility(View.VISIBLE); - personalText.setText(parsedProfileData.get(PERSONAL_TEXT_INDEX)); - } else { - personalText.setVisibility(View.GONE); + ViewPagerAdapter(FragmentManager manager) { + super(manager); + } + @Override + public Fragment getItem(int position) { + return mFragmentList.get(position); + } + @Override + public int getCount() { + return mFragmentList.size(); + } + void addFrag(Fragment fragment, String title) { + mFragmentList.add(fragment); + mFragmentTitleList.add(title); + } + @Override + public CharSequence getPageTitle(int position) { + return mFragmentTitleList.get(position); } } } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileParser.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileParser.java deleted file mode 100644 index fab4e120..00000000 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileParser.java +++ /dev/null @@ -1,143 +0,0 @@ -package gr.thmmy.mthmmy.activities.profile; - -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; - -import java.util.ArrayList; -import java.util.Objects; - -import mthmmy.utils.Report; - -import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.PACKAGE_NAME; - -/** - * Singleton used for parsing user's profile. - *

Class contains the methods:

  • {@link #parseProfileSummary(Document)}
  • - *

- */ -class ProfileParser { - /** - * Debug Tag for logging debug output to LogCat - */ - @SuppressWarnings("unused") - private static final String TAG = "ProfileParser"; - /** - * Index of user's thumbnail url in parsed information ArrayList - *

Not the url itself!

- */ - static final int THUMBNAIL_URL_INDEX = 0; - /** - * Index of user's username in parsed information ArrayList - *

Not the username itself!

- */ - static final int USERNAME_INDEX = 1; - /** - * Index of user's personal text in parsed information ArrayList - *

Not the text itself!

- */ - static final int PERSONAL_TEXT_INDEX = 2; - - /** - * Returns an {@link ArrayList} of {@link String}s. This method is used to parse all available - * information in a user profile. - *

- * 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 parseProfileSummary(Document profile) { - //Method's variables - ArrayList parsedInformation = new ArrayList<>(); - - //Contains all summary's rows - Elements summaryRows = profile.select(".bordercolor > tbody:nth-child(1) > tr:nth-child(2) tr"); - - { //Finds thumbnail's url - Element tmpEl = profile.select(".bordercolor img.avatar").first(); - if (tmpEl != null) - parsedInformation.add(THUMBNAIL_URL_INDEX, tmpEl.attr("abs:src")); - else //User doesn't have an avatar - parsedInformation.add(THUMBNAIL_URL_INDEX, null); - } - - { //Finds username - Element tmpEl = summaryRows.first(); - if (tmpEl != null) { - parsedInformation.add(USERNAME_INDEX, tmpEl.select("td").get(1).text()); - } else { - //Should never get here! - //Something is wrong. - Report.e(PACKAGE_NAME + "." + TAG, "An error occurred while trying to find profile's username."); - parsedInformation.add(USERNAME_INDEX, null); - } - } - - { //Finds personal text - Element tmpEl = profile.select("td.windowbg:nth-child(2)").first(); - 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) { - String rowText = row.text(), pHtml = ""; - - //Horizontal rule rows - if (row.select("td").size() == 1) - pHtml = ""; - else if (rowText.contains("Signature") || rowText.contains("Υπογραφή")) { - //This needs special handling since it may have css - { //Fix embedded videos - Elements noembedTag = row.select("noembed"); - ArrayList embededVideosUrls = new ArrayList<>(); - - for (Element _noembed : noembedTag) { - embededVideosUrls.add(_noembed.text().substring(_noembed.text() - .indexOf("href=\"https://www.youtube.com/watch?") + 38 - , _noembed.text().indexOf("target") - 2)); - } - - pHtml = row.html(); - - int tmp_counter = 0; - while (pHtml.contains(" embededVideosUrls.size()) - break; - pHtml = pHtml.replace( - pHtml.substring(pHtml.indexOf("") + 9) - , "

" - + "" - + "\"\"" - + "" - //+ "" - + "
"); - } - } - - //Add stuff to make it work in WebView - //style.css - pHtml = ("" + pHtml); - } else if (!rowText.contains("Name") && !rowText.contains("Όνομα")) { //Don't add username twice - if (Objects.equals(row.select("td").get(1).text(), "")) - continue; - //Style parsed information with html - pHtml = "" + row.select("td").first().text() + " " - + row.select("td").get(1).text(); - } - parsedInformation.add(pHtml); - } - return parsedInformation; - } -} diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java index c5b41d62..a5776d10 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java @@ -3,6 +3,7 @@ package gr.thmmy.mthmmy.activities.profile.summary; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; +import android.support.v4.app.Fragment; import android.text.Html; import android.util.Log; import android.view.LayoutInflater; @@ -21,45 +22,48 @@ import java.util.ArrayList; import java.util.Objects; import gr.thmmy.mthmmy.R; -import gr.thmmy.mthmmy.activities.base.BaseFragment; import gr.thmmy.mthmmy.activities.profile.ProfileActivity; -import me.zhanghai.android.materialprogressbar.MaterialProgressBar; import mthmmy.utils.Report; /** - * A {@link BaseFragment} subclass. - * Use the {@link SummaryFragment#newInstance} factory method to - * create an instance of this fragment. + * Use the {@link SummaryFragment#newInstance} factory method to create an instance of this fragment. */ -public class SummaryFragment extends BaseFragment { - static final String PROFILE_DOCUMENT = "PROFILE_DOCUMENT"; +public class SummaryFragment extends Fragment { + /** + * Debug Tag for logging debug output to LogCat + */ + @SuppressWarnings("unused") private static final String TAG = "SummaryFragment"; + /** + * The key to use when putting profile's source code String to {@link SummaryFragment}'s Bundle. + */ + static final String PROFILE_DOCUMENT = "PROFILE_DOCUMENT"; /** * {@link ArrayList} of Strings used to hold profile's information. Data are added in {@link ProfileActivity.ProfileTask}. */ + private ArrayList parsedProfileSummaryData; + /** + * A {@link Document} holding this profile's source code. + */ private Document profileSummaryDocument; private ProfileSummaryTask profileSummaryTask; - private ArrayList parsedProfileSummaryData; private LinearLayout mainContent; - private MaterialProgressBar progressBar; public SummaryFragment() { // Required empty public constructor - this.profileSummaryDocument = Jsoup.parse(getArguments().getString(PROFILE_DOCUMENT)); } /** - * Use ONLY this factory method to create a new instance of - * this fragment using the provided parameters. + * Use ONLY this factory method to create a new instance of this fragment using the provided + * parameters. * + * @param profileSummaryDocument a {@link Document} containing this profile's parsed page * @return A new instance of fragment Summary. */ - public static SummaryFragment newInstance(int sectionNumber, Document profileSummaryDocument) { + public static SummaryFragment newInstance(Document profileSummaryDocument) { SummaryFragment fragment = new SummaryFragment(); Bundle args = new Bundle(); - args.putString(ARG_TAG, TAG); - args.putInt(ARG_SECTION_NUMBER, sectionNumber); args.putString(PROFILE_DOCUMENT, profileSummaryDocument.toString()); fragment.setArguments(args); return fragment; @@ -68,6 +72,7 @@ public class SummaryFragment extends BaseFragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + profileSummaryDocument = Jsoup.parse(getArguments().getString(PROFILE_DOCUMENT)); parsedProfileSummaryData = new ArrayList<>(); } @@ -77,7 +82,6 @@ public class SummaryFragment extends BaseFragment { if (parsedProfileSummaryData.isEmpty()) { profileSummaryTask = new ProfileSummaryTask(); profileSummaryTask.execute(profileSummaryDocument); - } Report.d(TAG, "onActivityCreated"); } @@ -93,15 +97,13 @@ public class SummaryFragment extends BaseFragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View rootView = inflater.inflate(R.layout.profile_fragment_summary, container, false); - progressBar = (MaterialProgressBar) rootView.findViewById(R.id.progressBar); mainContent = (LinearLayout) rootView.findViewById(R.id.profile_activity_content); - populateLayout(); return rootView; } /** - * 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()} + * An {@link AsyncTask} that handles asynchronous parsing of a profile page's data. + * {@link AsyncTask#onPostExecute(Object) OnPostExecute} method calls {@link #populateLayout()} * to build graphics. *

*

Calling ProfileSummaryTask's {@link AsyncTask#execute execute} method needs to have profile's url @@ -112,28 +114,20 @@ public class SummaryFragment extends BaseFragment { /** * Debug Tag for logging debug output to LogCat */ + @SuppressWarnings("unused") private static final String TAG = "TopicTask"; //Separate tag for AsyncTask - protected void onPreExecute() { - progressBar.setVisibility(MaterialProgressBar.VISIBLE); - } - protected Void doInBackground(Document... profileSummaryPage) { parsedProfileSummaryData = parseProfileSummary(profileSummaryPage[0]); return null; } protected void onPostExecute(Void result) { - progressBar.setVisibility(MaterialProgressBar.INVISIBLE); populateLayout(); } /** - * Returns an {@link ArrayList} of {@link String}s. This method is used to parse all available - * information in a user profile. - *

- * 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. + * This method is used to parse all available information in a user profile. * * @param profile {@link Document} object containing this profile's source code * @return ArrayList containing this profile's parsed information @@ -200,7 +194,7 @@ public class SummaryFragment extends BaseFragment { } /** - * Simple method that builds the UI of a {@link ProfileActivity}. + * Simple method that builds the UI of a {@link SummaryFragment}. *

Use this method only after parsing profile's data with * {@link gr.thmmy.mthmmy.activities.profile.ProfileActivity.ProfileTask} as it reads from * {@link #parsedProfileSummaryData}

@@ -210,7 +204,8 @@ public class SummaryFragment extends BaseFragment { if (profileSummaryRow.contains("Signature") || profileSummaryRow.contains("Υπογραφή")) { WebView signatureEntry = new WebView(this.getContext()); - signatureEntry.loadDataWithBaseURL("file:///android_asset/", profileSummaryRow, "text/html", "UTF-8", null); + signatureEntry.loadDataWithBaseURL("file:///android_asset/", profileSummaryRow, + "text/html", "UTF-8", null); } TextView entry = new TextView(this.getContext()); 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 27267f12..b80c4f77 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 @@ -50,7 +50,9 @@ import me.zhanghai.android.materialprogressbar.MaterialProgressBar; import mthmmy.utils.Report; 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.profile.ProfileActivity.BUNDLE_PROFILE_URL; +import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_THUMBNAIL_URL; +import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.BUNDLE_USERNAME; import static gr.thmmy.mthmmy.activities.topic.TopicActivity.base_url; import static gr.thmmy.mthmmy.activities.topic.TopicActivity.toQuoteList; @@ -310,9 +312,14 @@ class TopicAdapter extends RecyclerView.Adapter { if (viewProperties.get(holder.getAdapterPosition())[isUserExtraInfoVisibile]) { Intent intent = new Intent(context, ProfileActivity.class); - Bundle b = new Bundle(); - b.putString(EXTRAS_PROFILE_URL, currentPost.getProfileURL()); - intent.putExtras(b); + Bundle extras = new Bundle(); + extras.putString(BUNDLE_PROFILE_URL, currentPost.getProfileURL()); + if(currentPost.getThumbnailUrl() == null) + extras.putString(BUNDLE_THUMBNAIL_URL, ""); + else + extras.putString(BUNDLE_THUMBNAIL_URL, currentPost.getThumbnailUrl()); + extras.putString(BUNDLE_USERNAME, currentPost.getAuthor()); + intent.putExtras(extras); intent.setFlags(FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } diff --git a/app/src/main/res/layout-v21/activity_profile.xml b/app/src/main/res/layout-v21/activity_profile.xml index aaaec83b..7601b753 100644 --- a/app/src/main/res/layout-v21/activity_profile.xml +++ b/app/src/main/res/layout-v21/activity_profile.xml @@ -95,7 +95,7 @@ android:layout_height="wrap_content" android:indeterminate="true" android:visibility="invisible" - app:layout_anchor="@id/nested_scroll" + app:layout_anchor="@id/profile_tab_container" app:layout_anchorGravity="top|center" app:mpb_indeterminateTint="@color/accent" app:mpb_progressStyle="horizontal"/> diff --git a/app/src/main/res/layout/activity_profile.xml b/app/src/main/res/layout/activity_profile.xml index 43032e29..47599ee8 100644 --- a/app/src/main/res/layout/activity_profile.xml +++ b/app/src/main/res/layout/activity_profile.xml @@ -97,7 +97,7 @@ android:layout_height="wrap_content" android:indeterminate="true" android:visibility="invisible" - app:layout_anchor="@id/nested_scroll" + app:layout_anchor="@id/profile_tab_container" app:layout_anchorGravity="top|center" app:mpb_indeterminateTint="@color/accent" app:mpb_progressStyle="horizontal"/> diff --git a/app/src/main/res/layout/profile_fragment_latest_posts_row.xml b/app/src/main/res/layout/profile_fragment_latest_posts_row.xml new file mode 100644 index 00000000..fb3d8a25 --- /dev/null +++ b/app/src/main/res/layout/profile_fragment_latest_posts_row.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/profile_fragment_summary.xml b/app/src/main/res/layout/profile_fragment_summary.xml index 0b8b479f..7a5a14a1 100644 --- a/app/src/main/res/layout/profile_fragment_summary.xml +++ b/app/src/main/res/layout/profile_fragment_summary.xml @@ -1,36 +1,19 @@ - + android:layout_height="match_parent" + android:background="@color/background" + android:paddingEnd="16dp" + android:paddingStart="16dp" + android:scrollbars="none"> - - - - - - - - \ No newline at end of file + android:gravity="center" + android:orientation="vertical"> + + \ No newline at end of file From 78a9690fc097a83de63eae9c244985a5d4f80b08 Mon Sep 17 00:00:00 2001 From: Apostolof Date: Mon, 2 Jan 2017 20:12:36 +0200 Subject: [PATCH 3/4] Work in progress: latest posts tab init --- .../activities/profile/ProfileActivity.java | 7 +- .../latestPosts/LatestPostsAdapter.java | 90 ++++++- .../latestPosts/LatestPostsFragment.java | 224 +++++++++++++++++- .../profile/summary/SummaryFragment.java | 3 +- .../gr/thmmy/mthmmy/data/TopicSummary.java | 13 + .../layout/profile_fragment_latest_posts.xml | 29 ++- .../profile_fragment_latest_posts_row.xml | 47 +++- .../main/res/layout/recycler_loading_item.xml | 13 + 8 files changed, 406 insertions(+), 20 deletions(-) create mode 100644 app/src/main/res/layout/recycler_loading_item.xml 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 c85795d0..cf0f651e 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 @@ -33,6 +33,7 @@ import javax.net.ssl.SSLHandshakeException; import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.activities.LoginActivity; import gr.thmmy.mthmmy.activities.base.BaseActivity; +import gr.thmmy.mthmmy.activities.profile.latestPosts.LatestPostsFragment; import gr.thmmy.mthmmy.activities.profile.summary.SummaryFragment; import gr.thmmy.mthmmy.utils.CircleTransform; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; @@ -73,6 +74,7 @@ public class ProfileActivity extends BaseActivity { private static final int THUMBNAIL_SIZE = 200; private ProfileTask profileTask; private String personalText; + private String profileUrl; @Override protected void onCreate(Bundle savedInstanceState) { @@ -82,6 +84,7 @@ public class ProfileActivity extends BaseActivity { Bundle extras = getIntent().getExtras(); String thumbnailUrl = extras.getString(BUNDLE_THUMBNAIL_URL); String username = extras.getString(BUNDLE_USERNAME); + profileUrl = extras.getString(BUNDLE_PROFILE_URL); //Initializes graphic elements toolbar = (Toolbar) findViewById(R.id.toolbar); @@ -146,7 +149,7 @@ public class ProfileActivity extends BaseActivity { }); profileTask = new ProfileTask(); - profileTask.execute(extras.getString(BUNDLE_PROFILE_URL)); //Attempt data parsing + profileTask.execute(profileUrl); //Attempt data parsing } @Override @@ -235,7 +238,7 @@ public class ProfileActivity extends BaseActivity { private void setupViewPager(ViewPager viewPager, Document profilePage) { ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager()); adapter.addFrag(SummaryFragment.newInstance(profilePage), "SUMMARY"); - //adapter.addFrag(new ); + adapter.addFrag(LatestPostsFragment.newInstance(profileUrl), "LATEST POSTS"); //adapter.addFrag(new ); viewPager.setAdapter(adapter); } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java index 607fce64..e62722f2 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java @@ -1,8 +1,88 @@ package gr.thmmy.mthmmy.activities.profile.latestPosts; -/** - * Created by apostolof on 1/1/17. - */ +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.webkit.WebView; +import android.widget.ProgressBar; +import android.widget.TextView; -public class LatestPostsAdapter { -} +import gr.thmmy.mthmmy.R; +import gr.thmmy.mthmmy.data.TopicSummary; + +import static gr.thmmy.mthmmy.activities.profile.latestPosts.LatestPostsFragment.parsedTopicSummaries; + +class LatestPostsAdapter extends RecyclerView.Adapter { + private final int VIEW_TYPE_ITEM = 0; + private final int VIEW_TYPE_LOADING = 1; + private OnLoadMoreListener mOnLoadMoreListener; + + public interface OnLoadMoreListener { + void onLoadMore(); + } + + public void setOnLoadMoreListener(OnLoadMoreListener mOnLoadMoreListener) { + this.mOnLoadMoreListener = mOnLoadMoreListener; + } + + @Override + public int getItemViewType(int position) { + return parsedTopicSummaries.get(position) == null ? VIEW_TYPE_LOADING : VIEW_TYPE_ITEM; + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + if (viewType == VIEW_TYPE_ITEM) { + View view = LayoutInflater.from(parent.getContext()). + inflate(R.layout.profile_fragment_latest_posts_row, parent, false); + return new LatestPostViewHolder(view); + } else if (viewType == VIEW_TYPE_LOADING) { + View view = LayoutInflater.from(parent.getContext()). + inflate(R.layout.recycler_loading_item, parent, false); + return new LoadingViewHolder(view); + } + return null; + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + if (holder instanceof LatestPostViewHolder) { + TopicSummary topic = parsedTopicSummaries.get(position); + LatestPostViewHolder latestPostViewHolder = (LatestPostViewHolder) holder; + latestPostViewHolder.postTitle.setText(topic.getTitle()); + latestPostViewHolder.postDate.setText(topic.getDateTimeModified()); + //latestPostViewHolder.post. + } else if (holder instanceof LoadingViewHolder) { + LoadingViewHolder loadingViewHolder = (LoadingViewHolder) holder; + loadingViewHolder.progressBar.setIndeterminate(true); + } + } + + @Override + public int getItemCount() { + return parsedTopicSummaries == null ? 0 : parsedTopicSummaries.size(); + } + + private static class LatestPostViewHolder extends RecyclerView.ViewHolder { + TextView postTitle; + TextView postDate; + WebView post; + + LatestPostViewHolder(View itemView) { + super(itemView); + postTitle = (TextView) itemView.findViewById(R.id.title); + postDate = (TextView) itemView.findViewById(R.id.date); + post = (WebView) itemView.findViewById(R.id.post); + } + } + + private static class LoadingViewHolder extends RecyclerView.ViewHolder { + ProgressBar progressBar; + + LoadingViewHolder(View itemView) { + super(itemView); + progressBar = (ProgressBar) itemView.findViewById(R.id.progress_bar); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java index cd2692c7..16880ffd 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java @@ -1,8 +1,228 @@ package gr.thmmy.mthmmy.activities.profile.latestPosts; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ProgressBar; +import android.widget.Toast; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.util.ArrayList; + +import javax.net.ssl.SSLHandshakeException; + +import gr.thmmy.mthmmy.R; +import gr.thmmy.mthmmy.activities.base.BaseActivity; +import gr.thmmy.mthmmy.data.TopicSummary; +import me.zhanghai.android.materialprogressbar.MaterialProgressBar; +import mthmmy.utils.Report; +import okhttp3.Request; +import okhttp3.Response; + /** - * Created by apostolof on 1/1/17. + * Use the {@link LatestPostsFragment#newInstance} factory method to create an instance of this fragment. */ +public class LatestPostsFragment extends Fragment { + /** + * Debug Tag for logging debug output to LogCat + */ + @SuppressWarnings("unused") + private static final String TAG = "LatestPostsFragment"; + /** + * The key to use when putting profile's url String to {@link LatestPostsFragment}'s Bundle. + */ + static final String PROFILE_URL = "PROFILE_DOCUMENT"; + /** + * {@link ArrayList} of {@link TopicSummary} objects used to hold profile's latest posts. Data + * are added in {@link LatestPostsFragment.ProfileLatestPostsTask}. + */ + static ArrayList parsedTopicSummaries; + private RecyclerView mainContent; + private LatestPostsAdapter latestPostsAdapter = new LatestPostsAdapter(); + private int numberOfPages = -1; + private String profileUrl; + private ProfileLatestPostsTask profileLatestPostsTask; + private MaterialProgressBar progressBar; + private boolean isLoading; + static int visibleThreshold = 5; + private int lastVisibleItem, totalItemCount; + + public LatestPostsFragment() { + // Required empty public constructor + } + + /** + * Use ONLY this factory method to create a new instance of this fragment using the provided + * parameters. + * + * @param profileUrl String containing this profile's url + * @return A new instance of fragment Summary. + */ + public static LatestPostsFragment newInstance(String profileUrl) { + LatestPostsFragment fragment = new LatestPostsFragment(); + Bundle args = new Bundle(); + args.putString(PROFILE_URL, profileUrl); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + profileUrl = getArguments().getString(PROFILE_URL); + parsedTopicSummaries = new ArrayList<>(); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + if (parsedTopicSummaries.isEmpty()) { + profileLatestPostsTask = new ProfileLatestPostsTask(); + profileLatestPostsTask.execute(profileUrl); + } + Report.d(TAG, "onActivityCreated"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (profileLatestPostsTask != null && profileLatestPostsTask.getStatus() != AsyncTask.Status.RUNNING) + profileLatestPostsTask.cancel(true); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + final View rootView = inflater.inflate(R.layout.profile_fragment_latest_posts, container, false); + + mainContent = (RecyclerView) rootView.findViewById(R.id.profile_latest_posts_recycler); + mainContent.setAdapter(latestPostsAdapter); + + final LatestPostsAdapter.OnLoadMoreListener onLoadMoreListener = new LatestPostsAdapter.OnLoadMoreListener() { + @Override + public void onLoadMore() { + parsedTopicSummaries.add(null); + latestPostsAdapter.notifyItemInserted(parsedTopicSummaries.size() - 1); + + //Removes loading item + parsedTopicSummaries.remove(parsedTopicSummaries.size() - 1); + latestPostsAdapter.notifyItemRemoved(parsedTopicSummaries.size()); + + //Load data + profileLatestPostsTask = new ProfileLatestPostsTask(); + profileLatestPostsTask.execute(profileUrl); + } + }; + + latestPostsAdapter.setOnLoadMoreListener(onLoadMoreListener); + final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) mainContent.getLayoutManager(); + mainContent.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); + + totalItemCount = linearLayoutManager.getItemCount(); + lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition(); + + if (!isLoading && totalItemCount <= (lastVisibleItem + visibleThreshold)) { + onLoadMoreListener.onLoadMore(); + isLoading = true; + } + } + }); + progressBar = (MaterialProgressBar) rootView.findViewById(R.id.progressBar); + return rootView; + } + + /** + * An {@link AsyncTask} that handles asynchronous fetching of a profile page and parsing this + * user's personal text. + *

ProfileTask's {@link AsyncTask#execute execute} method needs a profile's url as String + * parameter!

+ */ + public class ProfileLatestPostsTask extends AsyncTask { + //Class variables + /** + * Debug Tag for logging debug output to LogCat + */ + @SuppressWarnings("unused") + private static final String TAG = "ProfileLatestPostsTask"; //Separate tag for AsyncTask + + protected void onPreExecute() { + progressBar.setVisibility(ProgressBar.VISIBLE); + } + + protected Boolean doInBackground(String... profileUrl) { + String pageUrl = profileUrl[0] + ";sa=showPosts"; //Profile's page wap url + + Request request = new Request.Builder() + .url(pageUrl) + .build(); + try { + Response response = BaseActivity.getClient().newCall(request).execute(); + return parseLatestPosts(Jsoup.parse(response.body().string())); + } catch (SSLHandshakeException e) { + Report.w(TAG, "Certificate problem (please switch to unsafe connection)."); + } catch (Exception e) { + Report.e("TAG", "ERROR", e); + } + return false; + } + + protected void onPostExecute(Boolean result) { + if (!result) { //Parse failed! + Toast.makeText(getContext() + , "Fatal error!\n Aborting...", Toast.LENGTH_LONG).show(); + getActivity().finish(); + } + //Parse was successful + progressBar.setVisibility(ProgressBar.INVISIBLE); + latestPostsAdapter.notifyDataSetChanged(); + isLoading = false; + } + + private boolean parseLatestPosts(Document latestPostsPage) { + Elements latestPostsRows = latestPostsPage. + select("td:has(table:Contains(Show Posts)):not([style]) > table"); + if (latestPostsRows == null) + latestPostsRows = latestPostsPage. + select("td:has(table:Contains(Show Posts)):not([style]) > table"); + + for (Element row : latestPostsRows) { + String pTopicUrl, pTopicTitle, pDateTime, pPost; + + if (row.text().contains("Show Posts Pages: ") || row.text().contains("")) { + if (numberOfPages == -1) { + Elements pages = row.select("tr.catbg3 a"); + for (Element page : pages) { + if (Integer.parseInt(page.text()) >= numberOfPages) + numberOfPages = Integer.parseInt(page.text()); + } + } + } else { + Elements rowHeader = row.select("td.middletext"); + if (rowHeader.size() != 2) { + return false; + } else { + pTopicTitle = rowHeader.text(); + pTopicUrl = rowHeader.first().select("a").last().attr("href"); + pDateTime = rowHeader.last().text(); + } + pPost = rowHeader.select("div.post").first().html(); -public class LatestPostsFragment { + parsedTopicSummaries.add(new TopicSummary(pTopicUrl, pTopicTitle, "", pDateTime, pPost)); + } + } + return true; + } + } } diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java index a5776d10..be76d2b0 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java @@ -40,7 +40,8 @@ public class SummaryFragment extends Fragment { */ static final String PROFILE_DOCUMENT = "PROFILE_DOCUMENT"; /** - * {@link ArrayList} of Strings used to hold profile's information. Data are added in {@link ProfileActivity.ProfileTask}. + * {@link ArrayList} of Strings used to hold profile's information. Data are added in + * {@link SummaryFragment.ProfileSummaryTask}. */ private ArrayList parsedProfileSummaryData; /** diff --git a/app/src/main/java/gr/thmmy/mthmmy/data/TopicSummary.java b/app/src/main/java/gr/thmmy/mthmmy/data/TopicSummary.java index 935af6b1..41ea18b3 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/data/TopicSummary.java +++ b/app/src/main/java/gr/thmmy/mthmmy/data/TopicSummary.java @@ -5,6 +5,7 @@ public class TopicSummary { private final String title; private final String lastUser; private final String dateTimeModified; + private final String post; public TopicSummary(String topicUrl, String title, String lastUser, String dateTimeModified) { @@ -12,6 +13,16 @@ public class TopicSummary { this.title = title; this.lastUser = lastUser; this.dateTimeModified = dateTimeModified; + this.post = ""; + } + + public TopicSummary(String topicUrl, String title, String username, String dateTimeModified, + String post) { + this.topicUrl = topicUrl; + this.title = title; + this.lastUser = username; + this.dateTimeModified = dateTimeModified; + this.post = post; } public String getTopicUrl() { @@ -29,4 +40,6 @@ public class TopicSummary { public String getDateTimeModified() { return dateTimeModified; } + + public String getPost(){ return post;} } diff --git a/app/src/main/res/layout/profile_fragment_latest_posts.xml b/app/src/main/res/layout/profile_fragment_latest_posts.xml index fb3d8a25..9a7cc589 100644 --- a/app/src/main/res/layout/profile_fragment_latest_posts.xml +++ b/app/src/main/res/layout/profile_fragment_latest_posts.xml @@ -1,7 +1,26 @@ - + - \ No newline at end of file + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/profile_fragment_latest_posts_row.xml b/app/src/main/res/layout/profile_fragment_latest_posts_row.xml index fb3d8a25..bbc2c7e3 100644 --- a/app/src/main/res/layout/profile_fragment_latest_posts_row.xml +++ b/app/src/main/res/layout/profile_fragment_latest_posts_row.xml @@ -1,7 +1,44 @@ - + - \ No newline at end of file + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/recycler_loading_item.xml b/app/src/main/res/layout/recycler_loading_item.xml new file mode 100644 index 00000000..6585f174 --- /dev/null +++ b/app/src/main/res/layout/recycler_loading_item.xml @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file From 22610da03faf2206ce18d6c7adc6e2ad36c6791c Mon Sep 17 00:00:00 2001 From: Apostolof Date: Tue, 3 Jan 2017 16:45:11 +0200 Subject: [PATCH 4/4] Work in progress: latest posts tab completed --- .../latestPosts/LatestPostsAdapter.java | 16 ++- .../latestPosts/LatestPostsFragment.java | 124 ++++++++++++------ .../profile_fragment_latest_posts_row.xml | 14 +- .../main/res/layout/recycler_loading_item.xml | 12 +- 4 files changed, 116 insertions(+), 50 deletions(-) diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java index e62722f2..401298c2 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java @@ -1,19 +1,24 @@ package gr.thmmy.mthmmy.activities.profile.latestPosts; +import android.annotation.SuppressLint; import android.support.v7.widget.RecyclerView; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.webkit.WebView; +import android.webkit.WebViewClient; import android.widget.ProgressBar; import android.widget.TextView; import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.data.TopicSummary; +import me.zhanghai.android.materialprogressbar.MaterialProgressBar; import static gr.thmmy.mthmmy.activities.profile.latestPosts.LatestPostsFragment.parsedTopicSummaries; class LatestPostsAdapter extends RecyclerView.Adapter { + private static final String TAG = "LatestPostsAdapter"; private final int VIEW_TYPE_ITEM = 0; private final int VIEW_TYPE_LOADING = 1; private OnLoadMoreListener mOnLoadMoreListener; @@ -38,6 +43,7 @@ class LatestPostsAdapter extends RecyclerView.Adapter { inflate(R.layout.profile_fragment_latest_posts_row, parent, false); return new LatestPostViewHolder(view); } else if (viewType == VIEW_TYPE_LOADING) { + Log.d(TAG, "GOT"); View view = LayoutInflater.from(parent.getContext()). inflate(R.layout.recycler_loading_item, parent, false); return new LoadingViewHolder(view); @@ -45,14 +51,16 @@ class LatestPostsAdapter extends RecyclerView.Adapter { return null; } + @SuppressLint("SetJavaScriptEnabled") @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (holder instanceof LatestPostViewHolder) { TopicSummary topic = parsedTopicSummaries.get(position); - LatestPostViewHolder latestPostViewHolder = (LatestPostViewHolder) holder; + final LatestPostViewHolder latestPostViewHolder = (LatestPostViewHolder) holder; latestPostViewHolder.postTitle.setText(topic.getTitle()); latestPostViewHolder.postDate.setText(topic.getDateTimeModified()); - //latestPostViewHolder.post. + latestPostViewHolder.post.loadDataWithBaseURL("file:///android_asset/" + , topic.getPost(), "text/html", "UTF-8", null); } else if (holder instanceof LoadingViewHolder) { LoadingViewHolder loadingViewHolder = (LoadingViewHolder) holder; loadingViewHolder.progressBar.setIndeterminate(true); @@ -78,11 +86,11 @@ class LatestPostsAdapter extends RecyclerView.Adapter { } private static class LoadingViewHolder extends RecyclerView.ViewHolder { - ProgressBar progressBar; + MaterialProgressBar progressBar; LoadingViewHolder(View itemView) { super(itemView); - progressBar = (ProgressBar) itemView.findViewById(R.id.progress_bar); + progressBar = (MaterialProgressBar) itemView.findViewById(R.id.recycler_progress_bar); } } } \ No newline at end of file diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java index 16880ffd..5cd81712 100644 --- a/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java +++ b/app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java @@ -3,8 +3,10 @@ package gr.thmmy.mthmmy.activities.profile.latestPosts; import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.app.Fragment; +import android.support.v7.widget.DividerItemDecoration; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -46,13 +48,13 @@ public class LatestPostsFragment extends Fragment { * are added in {@link LatestPostsFragment.ProfileLatestPostsTask}. */ static ArrayList parsedTopicSummaries; - private RecyclerView mainContent; private LatestPostsAdapter latestPostsAdapter = new LatestPostsAdapter(); private int numberOfPages = -1; + private int pagesLoaded = 0; private String profileUrl; private ProfileLatestPostsTask profileLatestPostsTask; private MaterialProgressBar progressBar; - private boolean isLoading; + private boolean isLoadingMore; static int visibleThreshold = 5; private int lastVisibleItem, totalItemCount; @@ -82,60 +84,46 @@ public class LatestPostsFragment extends Fragment { parsedTopicSummaries = new ArrayList<>(); } - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - if (parsedTopicSummaries.isEmpty()) { - profileLatestPostsTask = new ProfileLatestPostsTask(); - profileLatestPostsTask.execute(profileUrl); - } - Report.d(TAG, "onActivityCreated"); - } - - @Override - public void onDestroy() { - super.onDestroy(); - if (profileLatestPostsTask != null && profileLatestPostsTask.getStatus() != AsyncTask.Status.RUNNING) - profileLatestPostsTask.cancel(true); - } - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View rootView = inflater.inflate(R.layout.profile_fragment_latest_posts, container, false); - mainContent = (RecyclerView) rootView.findViewById(R.id.profile_latest_posts_recycler); + RecyclerView mainContent = (RecyclerView) rootView.findViewById(R.id.profile_latest_posts_recycler); mainContent.setAdapter(latestPostsAdapter); + final LinearLayoutManager layoutManager = new LinearLayoutManager(getContext()); + mainContent.setLayoutManager(layoutManager); + DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(mainContent.getContext(), + layoutManager.getOrientation()); + mainContent.addItemDecoration(dividerItemDecoration); final LatestPostsAdapter.OnLoadMoreListener onLoadMoreListener = new LatestPostsAdapter.OnLoadMoreListener() { @Override public void onLoadMore() { - parsedTopicSummaries.add(null); - latestPostsAdapter.notifyItemInserted(parsedTopicSummaries.size() - 1); - - //Removes loading item - parsedTopicSummaries.remove(parsedTopicSummaries.size() - 1); - latestPostsAdapter.notifyItemRemoved(parsedTopicSummaries.size()); + if (pagesLoaded < numberOfPages) { + parsedTopicSummaries.add(null); + latestPostsAdapter.notifyItemInserted(parsedTopicSummaries.size() - 1); - //Load data - profileLatestPostsTask = new ProfileLatestPostsTask(); - profileLatestPostsTask.execute(profileUrl); + //Load data + profileLatestPostsTask = new ProfileLatestPostsTask(); + profileLatestPostsTask.execute(profileUrl + ";sa=showPosts;start=" + pagesLoaded * 15); + ++pagesLoaded; + } } }; latestPostsAdapter.setOnLoadMoreListener(onLoadMoreListener); - final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) mainContent.getLayoutManager(); mainContent.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); - totalItemCount = linearLayoutManager.getItemCount(); - lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition(); + totalItemCount = layoutManager.getItemCount(); + lastVisibleItem = layoutManager.findLastVisibleItemPosition(); - if (!isLoading && totalItemCount <= (lastVisibleItem + visibleThreshold)) { + if (!isLoadingMore && totalItemCount <= (lastVisibleItem + visibleThreshold)) { + isLoadingMore = true; onLoadMoreListener.onLoadMore(); - isLoading = true; } } }); @@ -143,6 +131,24 @@ public class LatestPostsFragment extends Fragment { return rootView; } + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + if (parsedTopicSummaries.isEmpty()) { + profileLatestPostsTask = new ProfileLatestPostsTask(); + profileLatestPostsTask.execute(profileUrl + ";sa=showPosts"); + pagesLoaded = 1; + } + Report.d(TAG, "onActivityCreated"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (profileLatestPostsTask != null && profileLatestPostsTask.getStatus() != AsyncTask.Status.RUNNING) + profileLatestPostsTask.cancel(true); + } + /** * An {@link AsyncTask} that handles asynchronous fetching of a profile page and parsing this * user's personal text. @@ -158,11 +164,14 @@ public class LatestPostsFragment extends Fragment { private static final String TAG = "ProfileLatestPostsTask"; //Separate tag for AsyncTask protected void onPreExecute() { - progressBar.setVisibility(ProgressBar.VISIBLE); + if (!isLoadingMore) { + Log.d(TAG, "false"); + progressBar.setVisibility(ProgressBar.VISIBLE); + } } protected Boolean doInBackground(String... profileUrl) { - String pageUrl = profileUrl[0] + ";sa=showPosts"; //Profile's page wap url + String pageUrl = profileUrl[0]; Request request = new Request.Builder() .url(pageUrl) @@ -187,7 +196,7 @@ public class LatestPostsFragment extends Fragment { //Parse was successful progressBar.setVisibility(ProgressBar.INVISIBLE); latestPostsAdapter.notifyDataSetChanged(); - isLoading = false; + isLoadingMore = false; } private boolean parseLatestPosts(Document latestPostsPage) { @@ -197,10 +206,15 @@ public class LatestPostsFragment extends Fragment { latestPostsRows = latestPostsPage. select("td:has(table:Contains(Show Posts)):not([style]) > table"); + //Removes loading item + if (isLoadingMore) { + parsedTopicSummaries.remove(parsedTopicSummaries.size() - 1); + + } + for (Element row : latestPostsRows) { String pTopicUrl, pTopicTitle, pDateTime, pPost; - - if (row.text().contains("Show Posts Pages: ") || row.text().contains("")) { + if (Integer.parseInt(row.attr("cellpadding")) == 4) { if (numberOfPages == -1) { Elements pages = row.select("tr.catbg3 a"); for (Element page : pages) { @@ -217,7 +231,37 @@ public class LatestPostsFragment extends Fragment { pTopicUrl = rowHeader.first().select("a").last().attr("href"); pDateTime = rowHeader.last().text(); } - pPost = rowHeader.select("div.post").first().html(); + pPost = row.select("div.post").first().outerHtml(); + + { //Fixes embedded videos + Elements noembedTag = row.select("div").select(".post").first().select("noembed"); + ArrayList embededVideosUrls = new ArrayList<>(); + + for (Element _noembed : noembedTag) { + embededVideosUrls.add(_noembed.text().substring(_noembed.text() + .indexOf("href=\"https://www.youtube.com/watch?") + 38 + , _noembed.text().indexOf("target") - 2)); + } + + int tmp_counter = 0; + while (pPost.contains(" embededVideosUrls.size()) + break; + pPost = pPost.replace( + pPost.substring(pPost.indexOf("") + 9) + , "
" + + "" + + "\"\"" + + "" + //+ "" + + "
"); + } + } + //Add stuff to make it work in WebView + //style.css + pPost = ("" + pPost); parsedTopicSummaries.add(new TopicSummary(pTopicUrl, pTopicTitle, "", pDateTime, pPost)); } diff --git a/app/src/main/res/layout/profile_fragment_latest_posts_row.xml b/app/src/main/res/layout/profile_fragment_latest_posts_row.xml index bbc2c7e3..5623df43 100644 --- a/app/src/main/res/layout/profile_fragment_latest_posts_row.xml +++ b/app/src/main/res/layout/profile_fragment_latest_posts_row.xml @@ -3,7 +3,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@color/primary_light" + android:background="@color/card_background" android:clickable="true" android:foreground="?android:attr/selectableItemBackground" android:paddingBottom="6dp" @@ -28,11 +28,21 @@ android:layout_below="@+id/title" android:textColor="@color/secondary_text"/> + + + + android:layout_below="@+id/spacer_divider"> - + android:layout_gravity="center_horizontal" + android:indeterminate="true" + app:mpb_indeterminateTint="@color/accent"/> \ No newline at end of file