Browse Source

Work in progress: latest posts tab init

pull/24/head
Apostolos Fanakis 8 years ago
parent
commit
78a9690fc0
  1. 7
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileActivity.java
  2. 88
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsAdapter.java
  3. 224
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java
  4. 3
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java
  5. 13
      app/src/main/java/gr/thmmy/mthmmy/data/TopicSummary.java
  6. 29
      app/src/main/res/layout/profile_fragment_latest_posts.xml
  7. 47
      app/src/main/res/layout/profile_fragment_latest_posts_row.xml
  8. 13
      app/src/main/res/layout/recycler_loading_item.xml

7
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);
}

88
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<RecyclerView.ViewHolder> {
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);
}
}
}

224
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<TopicSummary> 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.
* <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() {
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;
}
}
}

3
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<String> parsedProfileSummaryData;
/**

13
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;}
}

29
app/src/main/res/layout/profile_fragment_latest_posts.xml

@ -1,7 +1,26 @@
<?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">
<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">
</LinearLayout>
<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>

47
app/src/main/res/layout/profile_fragment_latest_posts_row.xml

@ -1,7 +1,44 @@
<?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">
<RelativeLayout
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:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
android:paddingBottom="6dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="6dp">
</LinearLayout>
<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"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:layout_below="@+id/date">
<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>

13
app/src/main/res/layout/recycler_loading_item.xml

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"/>
</LinearLayout>
Loading…
Cancel
Save