From eeb0db0a9fbde6cb6b9e3334b567467653414b24 Mon Sep 17 00:00:00 2001 From: Apostolof Date: Mon, 2 Jan 2017 13:38:50 +0200 Subject: [PATCH] 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