mirror of https://github.com/ThmmyNoLife/mTHMMY
Browse Source
# Conflicts: # app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileParser.javapull/24/head
Apostolos Fanakis
8 years ago
15 changed files with 887 additions and 133 deletions
@ -0,0 +1,96 @@ |
|||||
|
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<RecyclerView.ViewHolder> { |
||||
|
private static final String TAG = "LatestPostsAdapter"; |
||||
|
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) { |
||||
|
Log.d(TAG, "GOT"); |
||||
|
View view = LayoutInflater.from(parent.getContext()). |
||||
|
inflate(R.layout.recycler_loading_item, parent, false); |
||||
|
return new LoadingViewHolder(view); |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
@SuppressLint("SetJavaScriptEnabled") |
||||
|
@Override |
||||
|
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { |
||||
|
if (holder instanceof LatestPostViewHolder) { |
||||
|
TopicSummary topic = parsedTopicSummaries.get(position); |
||||
|
final LatestPostViewHolder latestPostViewHolder = (LatestPostViewHolder) holder; |
||||
|
latestPostViewHolder.postTitle.setText(topic.getTitle()); |
||||
|
latestPostViewHolder.postDate.setText(topic.getDateTimeModified()); |
||||
|
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); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@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 { |
||||
|
MaterialProgressBar progressBar; |
||||
|
|
||||
|
LoadingViewHolder(View itemView) { |
||||
|
super(itemView); |
||||
|
progressBar = (MaterialProgressBar) itemView.findViewById(R.id.recycler_progress_bar); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,272 @@ |
|||||
|
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; |
||||
|
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; |
||||
|
|
||||
|
/** |
||||
|
* 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<TopicSummary> parsedTopicSummaries; |
||||
|
private LatestPostsAdapter latestPostsAdapter = new LatestPostsAdapter(); |
||||
|
private int numberOfPages = -1; |
||||
|
private int pagesLoaded = 0; |
||||
|
private String profileUrl; |
||||
|
private ProfileLatestPostsTask profileLatestPostsTask; |
||||
|
private MaterialProgressBar progressBar; |
||||
|
private boolean isLoadingMore; |
||||
|
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 View onCreateView(LayoutInflater inflater, ViewGroup container, |
||||
|
Bundle savedInstanceState) { |
||||
|
final View rootView = inflater.inflate(R.layout.profile_fragment_latest_posts, container, false); |
||||
|
|
||||
|
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() { |
||||
|
if (pagesLoaded < numberOfPages) { |
||||
|
parsedTopicSummaries.add(null); |
||||
|
latestPostsAdapter.notifyItemInserted(parsedTopicSummaries.size() - 1); |
||||
|
|
||||
|
//Load data
|
||||
|
profileLatestPostsTask = new ProfileLatestPostsTask(); |
||||
|
profileLatestPostsTask.execute(profileUrl + ";sa=showPosts;start=" + pagesLoaded * 15); |
||||
|
++pagesLoaded; |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
latestPostsAdapter.setOnLoadMoreListener(onLoadMoreListener); |
||||
|
mainContent.addOnScrollListener(new RecyclerView.OnScrollListener() { |
||||
|
@Override |
||||
|
public void onScrolled(RecyclerView recyclerView, int dx, int dy) { |
||||
|
super.onScrolled(recyclerView, dx, dy); |
||||
|
|
||||
|
totalItemCount = layoutManager.getItemCount(); |
||||
|
lastVisibleItem = layoutManager.findLastVisibleItemPosition(); |
||||
|
|
||||
|
if (!isLoadingMore && totalItemCount <= (lastVisibleItem + visibleThreshold)) { |
||||
|
isLoadingMore = true; |
||||
|
onLoadMoreListener.onLoadMore(); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
progressBar = (MaterialProgressBar) rootView.findViewById(R.id.progressBar); |
||||
|
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. |
||||
|
* <p>ProfileTask's {@link AsyncTask#execute execute} method needs a profile's url as String |
||||
|
* parameter!</p> |
||||
|
*/ |
||||
|
public class ProfileLatestPostsTask extends AsyncTask<String, Void, Boolean> { |
||||
|
//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() { |
||||
|
if (!isLoadingMore) { |
||||
|
Log.d(TAG, "false"); |
||||
|
progressBar.setVisibility(ProgressBar.VISIBLE); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected Boolean doInBackground(String... profileUrl) { |
||||
|
String pageUrl = profileUrl[0]; |
||||
|
|
||||
|
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(); |
||||
|
isLoadingMore = 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"); |
||||
|
|
||||
|
//Removes loading item
|
||||
|
if (isLoadingMore) { |
||||
|
parsedTopicSummaries.remove(parsedTopicSummaries.size() - 1); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
for (Element row : latestPostsRows) { |
||||
|
String pTopicUrl, pTopicTitle, pDateTime, pPost; |
||||
|
if (Integer.parseInt(row.attr("cellpadding")) == 4) { |
||||
|
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 = row.select("div.post").first().outerHtml(); |
||||
|
|
||||
|
{ //Fixes embedded videos
|
||||
|
Elements noembedTag = row.select("div").select(".post").first().select("noembed"); |
||||
|
ArrayList<String> 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("<embed")) { |
||||
|
if (tmp_counter > embededVideosUrls.size()) |
||||
|
break; |
||||
|
pPost = pPost.replace( |
||||
|
pPost.substring(pPost.indexOf("<embed"), pPost.indexOf("/noembed>") + 9) |
||||
|
, "<div class=\"embedded-video\">" |
||||
|
+ "<a href=\"https://www.youtube.com/" |
||||
|
+ embededVideosUrls.get(tmp_counter) + "\" target=\"_blank\">" |
||||
|
+ "<img src=\"https://img.youtube.com/vi/" |
||||
|
+ embededVideosUrls.get(tmp_counter) + "/default.jpg\" alt=\"\" border=\"0\">" |
||||
|
+ "</a>" |
||||
|
//+ "<img class=\"embedded-video-play\" src=\"http://www.youtube.com/yt/brand/media/image/YouTube_light_color_icon.png\">"
|
||||
|
+ "</div>"); |
||||
|
} |
||||
|
} |
||||
|
//Add stuff to make it work in WebView
|
||||
|
//style.css
|
||||
|
pPost = ("<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />" + pPost); |
||||
|
|
||||
|
parsedTopicSummaries.add(new TopicSummary(pTopicUrl, pTopicTitle, "", pDateTime, pPost)); |
||||
|
} |
||||
|
} |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,8 @@ |
|||||
|
package gr.thmmy.mthmmy.activities.profile.stats; |
||||
|
|
||||
|
/** |
||||
|
* Created by apostolof on 1/1/17. |
||||
|
*/ |
||||
|
|
||||
|
public class StatsFragment { |
||||
|
} |
@ -0,0 +1,231 @@ |
|||||
|
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; |
||||
|
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.profile.ProfileActivity; |
||||
|
import mthmmy.utils.Report; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* Use the {@link SummaryFragment#newInstance} factory method to create an instance of this fragment. |
||||
|
*/ |
||||
|
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 SummaryFragment.ProfileSummaryTask}. |
||||
|
*/ |
||||
|
private ArrayList<String> parsedProfileSummaryData; |
||||
|
/** |
||||
|
* A {@link Document} holding this profile's source code. |
||||
|
*/ |
||||
|
private Document profileSummaryDocument; |
||||
|
private ProfileSummaryTask profileSummaryTask; |
||||
|
private LinearLayout mainContent; |
||||
|
|
||||
|
public SummaryFragment() { |
||||
|
// Required empty public constructor
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 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(Document profileSummaryDocument) { |
||||
|
SummaryFragment fragment = new SummaryFragment(); |
||||
|
Bundle args = new Bundle(); |
||||
|
args.putString(PROFILE_DOCUMENT, profileSummaryDocument.toString()); |
||||
|
fragment.setArguments(args); |
||||
|
return fragment; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onCreate(Bundle savedInstanceState) { |
||||
|
super.onCreate(savedInstanceState); |
||||
|
profileSummaryDocument = Jsoup.parse(getArguments().getString(PROFILE_DOCUMENT)); |
||||
|
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); |
||||
|
mainContent = (LinearLayout) rootView.findViewById(R.id.profile_activity_content); |
||||
|
return rootView; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 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. |
||||
|
* <p> |
||||
|
* <p>Calling ProfileSummaryTask's {@link AsyncTask#execute execute} method needs to have profile's url |
||||
|
* as String parameter!</p> |
||||
|
*/ |
||||
|
public class ProfileSummaryTask extends AsyncTask<Document, Void, Void> { |
||||
|
//Class variables
|
||||
|
/** |
||||
|
* Debug Tag for logging debug output to LogCat |
||||
|
*/ |
||||
|
@SuppressWarnings("unused") |
||||
|
private static final String TAG = "TopicTask"; //Separate tag for AsyncTask
|
||||
|
|
||||
|
protected Void doInBackground(Document... profileSummaryPage) { |
||||
|
parsedProfileSummaryData = parseProfileSummary(profileSummaryPage[0]); |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
protected void onPostExecute(Void result) { |
||||
|
populateLayout(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 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 |
||||
|
* @see org.jsoup.Jsoup Jsoup |
||||
|
*/ |
||||
|
ArrayList<String> parseProfileSummary(Document profile) { |
||||
|
//Method's variables
|
||||
|
ArrayList<String> 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<String> 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("<embed")) { |
||||
|
if (tmp_counter > embededVideosUrls.size()) |
||||
|
break; |
||||
|
pHtml = pHtml.replace( |
||||
|
pHtml.substring(pHtml.indexOf("<embed"), pHtml.indexOf("/noembed>") + 9) |
||||
|
, "<div class=\"embedded-video\">" |
||||
|
+ "<a href=\"https://www.youtube.com/" |
||||
|
+ embededVideosUrls.get(tmp_counter) + "\" target=\"_blank\">" |
||||
|
+ "<img src=\"https://img.youtube.com/vi/" |
||||
|
+ embededVideosUrls.get(tmp_counter) + "/default.jpg\" alt=\"\" border=\"0\">" |
||||
|
+ "</a>" |
||||
|
//+ "<img class=\"embedded-video-play\" src=\"http://www.youtube.com/yt/brand/media/image/YouTube_light_color_icon.png\">"
|
||||
|
+ "</div>"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
//Add stuff to make it work in WebView
|
||||
|
//style.css
|
||||
|
pHtml = ("<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />" + 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 = "<b>" + row.select("td").first().text() + "</b> " |
||||
|
+ row.select("td").get(1).text(); |
||||
|
} |
||||
|
parsedInformation.add(pHtml); |
||||
|
} |
||||
|
return parsedInformation; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Simple method that builds the UI of a {@link SummaryFragment}. |
||||
|
* <p>Use this method <b>only after</b> parsing profile's data with |
||||
|
* {@link gr.thmmy.mthmmy.activities.profile.ProfileActivity.ProfileTask} as it reads from |
||||
|
* {@link #parsedProfileSummaryData}</p> |
||||
|
*/ |
||||
|
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); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,26 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<RelativeLayout |
||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent"> |
||||
|
|
||||
|
<android.support.v7.widget.RecyclerView |
||||
|
android:id="@+id/profile_latest_posts_recycler" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent" |
||||
|
android:background="@color/background" |
||||
|
android:scrollbars="none"> |
||||
|
</android.support.v7.widget.RecyclerView> |
||||
|
|
||||
|
<me.zhanghai.android.materialprogressbar.MaterialProgressBar |
||||
|
android:id="@+id/progressBar" |
||||
|
style="@style/Widget.MaterialProgressBar.ProgressBar.Horizontal.NoPadding" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_alignParentTop="true" |
||||
|
android:indeterminate="true" |
||||
|
android:visibility="invisible" |
||||
|
app:mpb_indeterminateTint="@color/accent" |
||||
|
app:mpb_progressStyle="horizontal"/> |
||||
|
</RelativeLayout> |
@ -0,0 +1,54 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<RelativeLayout |
||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:background="@color/card_background" |
||||
|
android:clickable="true" |
||||
|
android:foreground="?android:attr/selectableItemBackground" |
||||
|
android:paddingBottom="6dp" |
||||
|
android:paddingLeft="10dp" |
||||
|
android:paddingRight="10dp" |
||||
|
android:paddingTop="6dp"> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/title" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_alignParentStart="true" |
||||
|
android:layout_alignParentTop="true" |
||||
|
android:textAppearance="?attr/textAppearanceListItem" |
||||
|
android:textColor="@color/primary_text"/> |
||||
|
|
||||
|
<TextView |
||||
|
android:id="@+id/date" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_alignParentStart="true" |
||||
|
android:layout_below="@+id/title" |
||||
|
android:textColor="@color/secondary_text"/> |
||||
|
|
||||
|
|
||||
|
<View |
||||
|
android:id="@+id/spacer_divider" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="1dp" |
||||
|
android:layout_below="@+id/date" |
||||
|
android:layout_marginBottom="3dp" |
||||
|
android:layout_marginTop="3dp" |
||||
|
android:background="@color/divider"/> |
||||
|
|
||||
|
<FrameLayout |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent" |
||||
|
android:layout_alignParentStart="true" |
||||
|
android:layout_below="@+id/spacer_divider"> |
||||
|
|
||||
|
<WebView |
||||
|
android:id="@+id/post" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:background="@color/card_background" |
||||
|
android:text="@string/post"/> |
||||
|
</FrameLayout> |
||||
|
</RelativeLayout> |
@ -0,0 +1,7 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
android:orientation="vertical" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent"> |
||||
|
|
||||
|
</LinearLayout> |
@ -0,0 +1,19 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<android.support.v4.widget.NestedScrollView |
||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
android:id="@+id/nested_scroll" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="match_parent" |
||||
|
android:background="@color/background" |
||||
|
android:paddingEnd="16dp" |
||||
|
android:paddingStart="16dp" |
||||
|
android:scrollbars="none"> |
||||
|
|
||||
|
<LinearLayout |
||||
|
android:id="@+id/profile_activity_content" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:gravity="center" |
||||
|
android:orientation="vertical"> |
||||
|
</LinearLayout> |
||||
|
</android.support.v4.widget.NestedScrollView> |
@ -0,0 +1,17 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<LinearLayout |
||||
|
xmlns:android="http://schemas.android.com/apk/res/android" |
||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
|
android:layout_width="match_parent" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:orientation="vertical"> |
||||
|
|
||||
|
|
||||
|
<me.zhanghai.android.materialprogressbar.MaterialProgressBar |
||||
|
android:id="@+id/recycler_progress_bar" |
||||
|
android:layout_width="wrap_content" |
||||
|
android:layout_height="wrap_content" |
||||
|
android:layout_gravity="center_horizontal" |
||||
|
android:indeterminate="true" |
||||
|
app:mpb_indeterminateTint="@color/accent"/> |
||||
|
</LinearLayout> |
Loading…
Reference in new issue