diff --git a/app/build.gradle b/app/build.gradle
index 764cd232..522d12b7 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -3,13 +3,13 @@ apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "25.0.0"
+
defaultConfig {
applicationId "gr.thmmy.mthmmy"
- minSdkVersion 15
+ minSdkVersion 16
targetSdkVersion 25
versionCode 1
- versionName "1.0"
- testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ versionName "0.12"
}
buildTypes {
release {
@@ -21,9 +21,17 @@ android {
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
- androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
- exclude group: 'com.android.support', module: 'support-annotations'
- })
compile 'com.android.support:appcompat-v7:25.0.0'
- testCompile 'junit:junit:4.12'
+ compile 'com.android.support:design:25.0.0'
+ compile 'com.squareup.okhttp3:okhttp:3.4.1'
+ compile 'org.jsoup:jsoup:1.10.1'
+ compile 'com.android.support:support-v4:25.0.0'
+ compile 'com.android.support:cardview-v7:25.0.0'
+ compile 'com.android.support:recyclerview-v7:25.0.0'
+ compile 'com.github.franmontiel:PersistentCookieJar:v1.0.0'
+ compile('com.mikepenz:materialdrawer:5.7.0@aar') {
+ transitive = true
+ }
+
+
}
diff --git a/app/src/androidTest/java/gr/thmmy/mthmmy/ExampleInstrumentedTest.java b/app/src/androidTest/java/gr/thmmy/mthmmy/ExampleInstrumentedTest.java
deleted file mode 100644
index 7c6fd486..00000000
--- a/app/src/androidTest/java/gr/thmmy/mthmmy/ExampleInstrumentedTest.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package gr.thmmy.mthmmy;
-
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static org.junit.Assert.*;
-
-/**
- * Instrumentation test, which will execute on an Android device.
- *
- * @see Testing documentation
- */
-@RunWith(AndroidJUnit4.class)
-public class ExampleInstrumentedTest {
- @Test
- public void useAppContext() throws Exception {
- // Context of the app under test.
- Context appContext = InstrumentationRegistry.getTargetContext();
-
- assertEquals("gr.thmmy.mthmmy", appContext.getPackageName());
- }
-}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 7517b415..f2f51291 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,13 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
\ No newline at end of file
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/AboutActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/AboutActivity.java
new file mode 100644
index 00000000..2c31a71a
--- /dev/null
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/AboutActivity.java
@@ -0,0 +1,23 @@
+package gr.thmmy.mthmmy.activities;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.widget.TextView;
+
+import gr.thmmy.mthmmy.BuildConfig;
+import gr.thmmy.mthmmy.R;
+
+public class AboutActivity extends BaseActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_about);
+ String versionName = BuildConfig.VERSION_NAME;
+ TextView tv= (TextView) findViewById(R.id.version);
+ if(tv!=null)
+ tv.setText(getString(R.string.version, versionName));
+
+ //TODO: add licenses
+ }
+}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/BaseActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/BaseActivity.java
new file mode 100644
index 00000000..f06396fd
--- /dev/null
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/BaseActivity.java
@@ -0,0 +1,39 @@
+package gr.thmmy.mthmmy.activities;
+
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+
+import com.franmontiel.persistentcookiejar.PersistentCookieJar;
+import com.franmontiel.persistentcookiejar.cache.SetCookieCache;
+import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor;
+
+import okhttp3.CookieJar;
+
+public class BaseActivity extends AppCompatActivity {
+
+ private static boolean cookiesInit=false; //To initialize cookie stuff only once per app start
+ protected static CookieJar cookieJar;
+ protected static SharedPrefsCookiePersistor sharedPrefsCookiePersistor;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if(!cookiesInit)
+ {
+ sharedPrefsCookiePersistor = new SharedPrefsCookiePersistor(BaseActivity.this);
+ cookieJar = new PersistentCookieJar(new SetCookieCache(), sharedPrefsCookiePersistor);
+ cookiesInit=true;
+ }
+
+ }
+
+ public static CookieJar getCookieJar()
+ {
+ return cookieJar;
+ }
+
+ public static SharedPrefsCookiePersistor getSharedPrefsCookiePersistor() {
+ return sharedPrefsCookiePersistor;
+ }
+}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/MainActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/MainActivity.java
new file mode 100644
index 00000000..b7e53aee
--- /dev/null
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/MainActivity.java
@@ -0,0 +1,115 @@
+package gr.thmmy.mthmmy.activities;
+
+import android.content.Intent;
+import android.os.Bundle;
+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.view.ViewPager;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.Menu;
+import android.view.MenuItem;
+
+import gr.thmmy.mthmmy.R;
+import gr.thmmy.mthmmy.data.TopicSummary;
+import gr.thmmy.mthmmy.sections.recent.RecentFragment;
+
+public class MainActivity extends BaseActivity implements RecentFragment.OnListFragmentInteractionListener
+{
+ private SectionsPagerAdapter mSectionsPagerAdapter;
+ private ViewPager mViewPager; /** The {@link ViewPager} that will host the section contents.*/
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ // Create the adapter that will return a fragment for each section of the activity
+ mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
+
+ // Set up the ViewPager with the sections adapter.
+ mViewPager = (ViewPager) findViewById(R.id.container);
+ mViewPager.setAdapter(mSectionsPagerAdapter);
+
+ TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
+ tabLayout.setupWithViewPager(mViewPager);
+
+ //TODO: Drawer
+// new DrawerBuilder().withActivity(this)
+// .withToolbar(toolbar)
+// .build();
+
+ }
+
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.menu_main, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item)
+ {
+ int id = item.getItemId();
+
+ if (id == R.id.action_about) {
+ Intent i = new Intent(MainActivity.this, AboutActivity.class);
+ startActivity(i);
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onFragmentInteraction(TopicSummary topicSummary)
+ {
+ Intent i = new Intent(MainActivity.this, TopicActivity.class);
+ i.putExtra("TOPIC_URL", topicSummary.getTopicUrl());
+ i.putExtra("TOPIC_TITLE", topicSummary.getTitle());
+ startActivity(i);
+ }
+
+//---------------------------------FragmentPagerAdapter---------------------------------------------
+ /**
+ * A {@link FragmentPagerAdapter} that returns a fragment corresponding to
+ * one of the sections/tabs/pages. If it becomes too memory intensive,
+ * it may be best to switch to a
+ * {@link android.support.v4.app.FragmentStatePagerAdapter}.
+ */
+ public class SectionsPagerAdapter extends FragmentPagerAdapter {
+
+ SectionsPagerAdapter(FragmentManager fm) {
+ super(fm);
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ // getItem is called to instantiate the fragment for the given page.
+ // Return a PlaceholderFragment (defined as a static inner class below).
+ return RecentFragment.newInstance(position + 1);
+ }
+
+ @Override
+ public int getCount() {
+ // Show 1 total pages.
+ return 1;
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ switch (position) {
+ case 0:
+ return "RECENT";
+ }
+ return null;
+ }
+ }
+}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/activities/TopicActivity.java b/app/src/main/java/gr/thmmy/mthmmy/activities/TopicActivity.java
new file mode 100644
index 00000000..003ec965
--- /dev/null
+++ b/app/src/main/java/gr/thmmy/mthmmy/activities/TopicActivity.java
@@ -0,0 +1,181 @@
+package gr.thmmy.mthmmy.activities;
+
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+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.webkit.WebView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import gr.thmmy.mthmmy.R;
+import gr.thmmy.mthmmy.data.Post;
+import gr.thmmy.mthmmy.utils.CustomRecyclerView;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.net.ssl.SSLHandshakeException;
+
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+public class TopicActivity extends BaseActivity {
+
+ private CustomRecyclerView recyclerView;
+ private TopicAdapter topicAdapter;
+ private ProgressBar progressBar;
+ private OkHttpClient client;
+
+ private List postsList;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_topic);
+
+ progressBar = (ProgressBar) findViewById(R.id.progressBar);
+
+
+ Bundle extras = getIntent().getExtras();
+ ActionBar actionbar = getSupportActionBar();
+ if(actionbar!=null)
+ actionbar.setTitle(extras.getString("TOPIC_TITLE"));
+
+ postsList = new ArrayList<>();
+
+ topicAdapter = new TopicAdapter();
+
+ recyclerView = (CustomRecyclerView) findViewById(R.id.list);
+ recyclerView.setLayoutManager(new LinearLayoutManager(findViewById(R.id.list).getContext()));
+ recyclerView.setAdapter(topicAdapter);
+
+ client = new OkHttpClient.Builder().build();
+ new TopicTask().execute(extras.getString("TOPIC_URL"));
+
+ }
+
+
+ private class TopicAdapter extends RecyclerView.Adapter
+ {
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.activity_topic_post_row, parent, false);
+ return new ViewHolder(view);
+ }
+
+
+
+ @Override
+ public void onBindViewHolder(final ViewHolder holder, final int position) {
+
+ holder.mAuthorView.setText(postsList.get(position).getAuthor());
+ holder.mDateTimeView.setText(postsList.get(position).getDateTime());
+ holder.mContentView.loadData(postsList.get(position).getContent(), "text/html", null);
+
+// holder.topic = recentList.get(position);
+//
+// holder.mView.setOnClickListener(new View.OnClickListener() {
+// @Override
+// public void onClick(View v) {
+//
+// if (null != mListener) {
+// // Notify the active callbacks interface (the activity, if the
+// // fragment is attached to one) that an item has been selected.
+// mListener.onFragmentInteraction(holder.topic); //?
+//
+// }
+//
+// }
+// });
+ }
+
+ @Override
+ public int getItemCount() {
+ return postsList.size();
+ }
+
+ public class ViewHolder extends RecyclerView.ViewHolder {
+ public final View mView;
+ public final TextView mAuthorView;
+ public final WebView mContentView;
+ public final TextView mDateTimeView;
+
+ public ViewHolder(View view) {
+ super(view);
+ mView = view;
+ mAuthorView = (TextView) view.findViewById(R.id.author);
+ mContentView = (WebView) view.findViewById(R.id.content);
+ mDateTimeView = (TextView) view.findViewById(R.id.dateTime);
+ }
+
+ }
+ }
+
+
+
+//---------------------------------------TOPIC ASYNC TASK-------------------------------------------
+
+ public class TopicTask extends AsyncTask
+ {
+ private static final String TAG="TopicTask";
+ private String pageLink;
+
+ private Document document;
+
+
+ protected void onPreExecute() {
+ progressBar.setVisibility(ProgressBar.VISIBLE);
+ }
+
+ protected Boolean doInBackground(String... strings)
+ {
+ pageLink = strings[0];
+
+ Request request = new Request.Builder()
+ .url(pageLink)
+ .build();
+
+ try {
+ Response response = client.newCall(request).execute();
+ document = Jsoup.parse(response.body().string());
+ return parse(document);
+ } catch (SSLHandshakeException e) {
+ Log.w(TAG, "Certificate problem (please switch to unsafe connection).");
+ return false;
+
+ } catch (Exception e) {
+ Log.e("TAG", "ERROR", e);
+ return false;
+ }
+
+ }
+
+
+
+ protected void onPostExecute(Boolean result)
+ {
+ progressBar.setVisibility(ProgressBar.INVISIBLE);
+ topicAdapter.notifyDataSetChanged();
+ }
+
+ private boolean parse(Document document)
+ {
+ return true;
+
+ }
+
+
+ }
+}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/data/Post.java b/app/src/main/java/gr/thmmy/mthmmy/data/Post.java
new file mode 100644
index 00000000..c7eb6bee
--- /dev/null
+++ b/app/src/main/java/gr/thmmy/mthmmy/data/Post.java
@@ -0,0 +1,29 @@
+package gr.thmmy.mthmmy.data;
+
+/**
+ * Created by ezero on 14/9/2016.
+ */
+public class Post
+{
+ private final String author;
+ private final String dateTime;
+ private String content;
+
+ public Post(String author, String dateTime, String content) {
+ this.author = author;
+ this.dateTime = dateTime;
+ this.content = content;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public String getAuthor() {
+ return author;
+ }
+
+ public String getDateTime() {
+ return dateTime;
+ }
+}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/data/TopicSummary.java b/app/src/main/java/gr/thmmy/mthmmy/data/TopicSummary.java
new file mode 100644
index 00000000..0bb05c6d
--- /dev/null
+++ b/app/src/main/java/gr/thmmy/mthmmy/data/TopicSummary.java
@@ -0,0 +1,31 @@
+package gr.thmmy.mthmmy.data;
+
+public class TopicSummary
+{
+ private final String topicUrl;
+ private final String title;
+ private final String lastUser;
+ private final String dateTimeModified;
+
+
+ public TopicSummary(String topicUrl, String title, String lastUser, String dateTimeModified)
+ {
+ this.topicUrl = topicUrl;
+ this.title = title;
+ this.lastUser = lastUser;
+ this.dateTimeModified = dateTimeModified;
+ }
+
+ public String getTopicUrl() { return topicUrl; }
+ public String getTitle() {
+ return title;
+ }
+
+ public String getLastUser() {
+ return lastUser;
+ }
+
+ public String getDateTimeModified() {
+ return dateTimeModified;
+ }
+}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/sections/recent/RecentAdapter.java b/app/src/main/java/gr/thmmy/mthmmy/sections/recent/RecentAdapter.java
new file mode 100644
index 00000000..4a542cc5
--- /dev/null
+++ b/app/src/main/java/gr/thmmy/mthmmy/sections/recent/RecentAdapter.java
@@ -0,0 +1,88 @@
+package gr.thmmy.mthmmy.sections.recent;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import gr.thmmy.mthmmy.R;
+import gr.thmmy.mthmmy.data.TopicSummary;
+
+import java.util.List;
+
+
+/**
+ * {@link RecyclerView.Adapter} that can display a {@link TopicSummary} and makes a call to the
+ * specified {@link RecentFragment.OnListFragmentInteractionListener}.
+ */
+public class RecentAdapter extends RecyclerView.Adapter
+{
+ private final List recentList;
+ private final RecentFragment.OnListFragmentInteractionListener mListener;
+
+ public RecentAdapter(List topicSummaryList, RecentFragment.OnListFragmentInteractionListener listener) {
+ this.recentList = topicSummaryList;
+ mListener = listener;
+ }
+
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.fragment_recent_row, parent, false);
+ return new ViewHolder(view);
+ }
+
+
+
+ @Override
+ public void onBindViewHolder(final ViewHolder holder, final int position) {
+
+ holder.mTitleView.setText(recentList.get(position).getTitle());
+ holder.mDateTimeView.setText(recentList.get(position).getDateTimeModified());
+ holder.mUserView.setText("by " + recentList.get(position).getLastUser());
+
+ holder.topic = recentList.get(position);
+
+ holder.mView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+
+ if (null != mListener) {
+ // Notify the active callbacks interface (the activity, if the
+ // fragment is attached to one) that an item has been selected.
+ mListener.onFragmentInteraction(holder.topic); //?
+
+ }
+
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return recentList.size();
+ }
+
+ public class ViewHolder extends RecyclerView.ViewHolder {
+ public final View mView;
+ public final TextView mTitleView;
+ public final TextView mUserView;
+ public final TextView mDateTimeView;
+ public TopicSummary topic;
+
+ public ViewHolder(View view) {
+ super(view);
+ mView = view;
+ mTitleView = (TextView) view.findViewById(R.id.title);
+ mUserView = (TextView) view.findViewById(R.id.lastUser);
+ mDateTimeView = (TextView) view.findViewById(R.id.dateTime);
+ }
+
+// @Override
+// public String toString() {
+// return super.toString() + " '" + mContentView.getText() + "'";
+// }
+ }
+}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/sections/recent/RecentFragment.java b/app/src/main/java/gr/thmmy/mthmmy/sections/recent/RecentFragment.java
new file mode 100644
index 00000000..69f7e100
--- /dev/null
+++ b/app/src/main/java/gr/thmmy/mthmmy/sections/recent/RecentFragment.java
@@ -0,0 +1,363 @@
+package gr.thmmy.mthmmy.sections.recent;
+
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.widget.LinearLayoutManager;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ProgressBar;
+import android.widget.RelativeLayout;
+import android.widget.Toast;
+
+import gr.thmmy.mthmmy.R;
+import gr.thmmy.mthmmy.data.TopicSummary;
+import gr.thmmy.mthmmy.utils.CustomRecyclerView;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.select.Elements;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+/**
+ * A simple {@link Fragment} subclass.
+ * Activities that contain this fragment must implement the
+ * {@link RecentFragment.OnListFragmentInteractionListener} interface
+ * to handle interaction events.
+ * Use the {@link RecentFragment#newInstance} factory method to
+ * create an instance of this fragment.
+ */
+public class RecentFragment extends Fragment
+{
+ private static final String TAG = "RecentFragment";
+ // Fragment initialization parameters, e.g. ARG_SECTION_NUMBER
+ private static final String ARG_SECTION_NUMBER = "SectionNumber";
+ private int sectionNumber;
+
+ private ProgressBar progressBar;
+ private SwipeRefreshLayout swipeRefreshLayout;
+ private CustomRecyclerView recyclerView;
+ private RecentAdapter recentAdapter;
+
+ private List topicSummaries;
+
+ private OnListFragmentInteractionListener mListener;
+
+ OkHttpClient client;
+
+ // Required empty public constructor
+ public RecentFragment() {}
+
+ /**
+ * Use ONLY this factory method to create a new instance of
+ * this fragment using the provided parameters.
+ *
+ * @param sectionNumber
+ * @return A new instance of fragment Recent.
+ */
+ public static RecentFragment newInstance(int sectionNumber)
+ {
+ RecentFragment fragment = new RecentFragment();
+ Bundle args = new Bundle();
+ args.putInt(ARG_SECTION_NUMBER, sectionNumber);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ public int getSectionNumber() {
+ return sectionNumber;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+
+ sectionNumber = getArguments().getInt(ARG_SECTION_NUMBER);
+
+ client = new OkHttpClient.Builder().build();
+
+
+ topicSummaries = new ArrayList<>();
+
+
+ if(sectionNumber==1) //?
+ Log.d(TAG,"onCreate");
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState)
+ {
+ super.onActivityCreated(savedInstanceState);
+ if(sectionNumber==1)//temp
+ {
+ if(topicSummaries.isEmpty())
+ new RecentTask().execute();
+
+
+ }
+ Log.d(TAG,"onActivityCreated");
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ if(sectionNumber==1)
+ Log.d(TAG,"onStart");
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if(sectionNumber==1)
+ Log.d(TAG,"onResume");
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ if(sectionNumber==1)
+ Log.d(TAG,"onPause");
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if(sectionNumber==1)
+ Log.d(TAG,"onStop");
+ }
+
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState)
+ {
+ // Inflate the layout for this fragment
+ final View rootView = inflater.inflate(R.layout.fragment_recent, container, false);
+
+ // Set the adapter
+ if (rootView instanceof RelativeLayout)
+ {
+ progressBar = (ProgressBar) rootView.findViewById(R.id.progressBar);
+ recentAdapter = new RecentAdapter(topicSummaries, mListener);
+
+ recyclerView = (CustomRecyclerView) rootView.findViewById(R.id.list);
+ recyclerView.setLayoutManager(new LinearLayoutManager(rootView.findViewById(R.id.list).getContext()));
+ recyclerView.setAdapter(recentAdapter);
+
+ swipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swiperefresh);
+ swipeRefreshLayout.setOnRefreshListener(
+ new SwipeRefreshLayout.OnRefreshListener() {
+ @Override
+ public void onRefresh() {
+ new RecentTask().execute();
+
+ }
+
+ }
+ );
+
+
+ }
+
+ return rootView;
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ if (context instanceof OnListFragmentInteractionListener) {
+ mListener = (OnListFragmentInteractionListener) context;
+
+ } else {
+ throw new RuntimeException(context.toString()
+ + " must implement OnFragmentInteractionListener");
+ }
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ mListener = null;
+ }
+
+ /**
+ * This interface must be implemented by activities that contain this
+ * fragment to allow an interaction in this fragment to be communicated
+ * to the activity and potentially other fragments contained in that
+ * activity.
+ */
+ public interface OnListFragmentInteractionListener {
+ // TODO: Update argument type and name
+ void onFragmentInteraction(TopicSummary topicSummary);
+ }
+
+
+ int n=0;
+ long s=0;
+
+
+ //---------------------------------------ASYNC TASK-----------------------------------
+
+ public class RecentTask extends AsyncTask
+ {
+ private static final String TAG="RecentTask";
+ private final String thmmyUrl = "https://www.thmmy.gr/smf/index.php";
+
+ private Document document;
+
+
+ protected void onPreExecute() {
+
+ progressBar.setVisibility(ProgressBar.VISIBLE);
+ }
+
+ protected Integer doInBackground(Void... voids)
+ {
+
+ Request request = new Request.Builder()
+ .url(thmmyUrl)
+ .build();
+ try {
+ Response response = client.newCall(request).execute();
+ document = Jsoup.parse(response.body().string());
+ parse(document);
+ return 0;
+ } catch (IOException e) {
+ Log.d("DEB", "ERROR", e);
+ return 1;
+ } catch (Exception e) {
+ Log.d("DEB", "ERROR", e);
+ return 2;
+ }
+
+ }
+
+
+
+ protected void onPostExecute(Integer result)
+ {
+
+ if(result==0)
+ recentAdapter.notifyDataSetChanged();
+ else if (result==1)
+ Toast.makeText(getActivity(), "Network error", Toast.LENGTH_SHORT).show();
+
+ progressBar.setVisibility(ProgressBar.INVISIBLE);
+ swipeRefreshLayout.setRefreshing(false);
+ }
+
+ private boolean parse(Document document)
+ {
+ Elements recent = document.select("#block8 :first-child div");
+ if(recent.size()==30)
+ {
+ topicSummaries.clear();
+
+ for(int i=0; i0)
+ enableRefreshing=false;
+ super.onScrolled(dx, dy);
+ }
+
+
+ @Override
+ public void onScrollStateChanged(int state)
+ {
+ if((state!=SCROLL_STATE_DRAGGING)&&((LinearLayoutManager)getLayoutManager()).findFirstCompletelyVisibleItemPosition()==0)
+ enableRefreshing=true;
+ else if(getChildCount()==0)
+ enableRefreshing=true;
+ else if(((LinearLayoutManager)getLayoutManager()).findFirstCompletelyVisibleItemPosition()!=0)
+ enableRefreshing=false;
+
+
+
+ super.onScrollStateChanged(state);
+ }
+
+ @Override
+ public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
+ if(enableRefreshing)
+ return super.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
+ else
+ return super.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, 0, offsetInWindow);
+ }
+
+}
diff --git a/app/src/main/java/gr/thmmy/mthmmy/utils/Thmmy.java b/app/src/main/java/gr/thmmy/mthmmy/utils/Thmmy.java
new file mode 100644
index 00000000..acabca99
--- /dev/null
+++ b/app/src/main/java/gr/thmmy/mthmmy/utils/Thmmy.java
@@ -0,0 +1,309 @@
+package gr.thmmy.mthmmy.utils;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import com.franmontiel.persistentcookiejar.PersistentCookieJar;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.net.ssl.SSLHandshakeException;
+
+import gr.thmmy.mthmmy.activities.BaseActivity;
+import okhttp3.Cookie;
+import okhttp3.FormBody;
+import okhttp3.HttpUrl;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+
+
+public class Thmmy
+{
+ private static final HttpUrl loginUrl = HttpUrl.parse("https://www.thmmy.gr/smf/index.php?action=login2");
+ private static final HttpUrl indexUrl = HttpUrl.parse("https://www.thmmy.gr/smf/index.php");
+
+ public static final int OK = 1;
+ public static final int WRONG_USER= 2;
+ public static final int WRONG_PASSWORD= 3;
+ public static final int FAILED= 4;
+ public static final int CERTIFICATE_ERROR = 5;
+ public static final int OTHER_ERROR = 6;
+
+
+
+
+
+ public static int authenticate(String username, String password)
+ {
+ LoginData loginData = login(username, password, "60");
+
+ int status = loginData.getStatus();
+ if (status==OK)
+ {
+ logout(loginData.getLogoutLink());
+ return OK;
+ }
+ return status;
+
+ }
+
+
+ //-------------------------------------------LOGIN--------------------------------------------------
+ //Two options: (username, password, duration) or nothing - cookies
+ public static LoginData login(String... strings)
+ {
+ Log.d("Login","Logging in...");
+ LoginData loginData = new LoginData();
+ Request request;
+
+ if(strings.length==3)
+ {
+ String loginName = strings[0];
+ String password = strings[1];
+ String duration = strings[2];
+
+ ((PersistentCookieJar) BaseActivity.getCookieJar()).clear();
+
+ RequestBody formBody = new FormBody.Builder()
+ .add("user", loginName)
+ .add("passwrd", password)
+ .add("cookielength", duration) //Forever is -1
+ .build();
+ request = new Request.Builder()
+ .url(loginUrl)
+ .post(formBody)
+ .build();
+ }
+ else
+ {
+ request = new Request.Builder()
+ .url(loginUrl)
+ .build();
+ }
+
+ OkHttpClient client = new OkHttpClient.Builder()
+ .cookieJar(BaseActivity.getCookieJar())
+ .build();
+
+
+ try
+ {
+ Response response = client.newCall(request).execute();
+ Document document = Jsoup.parse(response.body().string());
+
+ Element logout = document.getElementById("logoutbtn");
+
+ if (logout != null)
+ {
+ Log.i("Login", "Login successful");
+ setPersistentCookieSession();
+ loginData.setUsername(extractUserName(document));
+ loginData.setLogoutLink(HttpUrl.parse(logout.attr("href")));
+ loginData.setStatus(OK);
+ }
+ else
+ {
+ Log.w("Login", "Login failed");
+ loginData.setStatus(FAILED);
+
+ //Making error more specific
+ Elements error = document.select("b:contains(That username does not exist.)");
+
+ if (error.size()==1)
+ {
+ loginData.setStatus(WRONG_USER);
+ Log.d("Login","Wrong Username");
+ }
+
+ error = document.select("body:contains(Password incorrect)");
+ if (error.size()==1)
+ {
+ Log.d("Login","Wrong Password");
+ loginData.setStatus(WRONG_PASSWORD);
+ }
+
+ ((PersistentCookieJar) BaseActivity.getCookieJar()).clear();
+
+ }
+ } catch (SSLHandshakeException e) {
+ Log.w("Login", "Certificate problem");
+ loginData.setStatus(CERTIFICATE_ERROR);
+
+ } catch (Exception e) {
+ Log.e("Login", "Error", e);
+ loginData.setStatus(OTHER_ERROR);
+ }
+
+
+
+ return loginData;
+
+
+ }
+
+ public static class LoginData implements Parcelable
+ {
+ private int status;
+ private String username;
+ private HttpUrl logoutLink;
+
+ public LoginData() {}
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+
+ public int getStatus() {
+ return status;
+ }
+
+ public void setStatus(int status) {
+ this.status = status;
+ }
+
+
+ public HttpUrl getLogoutLink() {
+ return logoutLink;
+ }
+
+ public void setLogoutLink(HttpUrl logoutLink) {
+ this.logoutLink = logoutLink;
+ }
+
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(status);
+ out.writeString(username);
+ out.writeString(logoutLink.toString());
+ }
+
+ public static final Parcelable.Creator CREATOR
+ = new Parcelable.Creator() {
+ public LoginData createFromParcel(Parcel in) {
+ return new LoginData(in);
+ }
+
+ public LoginData[] newArray(int size) {
+ return new LoginData[size];
+ }
+ };
+
+ private LoginData(Parcel in) {
+ status = in.readInt();
+ username=in.readString();
+ logoutLink=HttpUrl.parse(in.readString());
+ }
+ }
+
+ private static boolean setPersistentCookieSession()
+ {
+ List cookieList = BaseActivity.getCookieJar().loadForRequest(HttpUrl.parse("https://www.thmmy.gr"));
+
+ if (cookieList.size() == 2) {
+ if ((cookieList.get(0).name().equals("THMMYgrC00ki3")) && (cookieList.get(1).name().equals("PHPSESSID")))
+ {
+ Cookie.Builder builder = new Cookie.Builder();
+ builder.name(cookieList.get(1).name())
+ .value(cookieList.get(1).value())
+ .domain(cookieList.get(1).domain())
+ .expiresAt(cookieList.get(0).expiresAt());
+ cookieList.remove(1);
+ cookieList.add(builder.build());
+ BaseActivity.getSharedPrefsCookiePersistor().clear();
+ BaseActivity.getSharedPrefsCookiePersistor().saveAll(cookieList);
+ return true;
+ }
+ }
+ return false;
+ }
+ //-------------------------------------LOGIN ENDS-----------------------------------------------
+
+
+
+ //--------------------------------------LOGOUT--------------------------------------------------
+ //Two options: (username, password, duration) or nothing - cookies
+ public static int logout(HttpUrl logoutLink)
+ {
+ OkHttpClient client = new OkHttpClient.Builder()
+ .cookieJar(BaseActivity.getCookieJar()) //cookies will be deleted
+ .build();
+ Request request = new Request.Builder()
+ .url(logoutLink)
+ .build();
+
+ try {
+ Response response = client.newCall(request).execute();
+ Document document = Jsoup.parse(response.body().string());
+
+ Elements login = document.select("[value=Login]");
+ if(!login.isEmpty())
+ {
+ Log.i("Logout", "Logout successful");
+ return OK;
+ }
+ else
+ {
+ Log.w("Logout", "Logout failed");
+ return FAILED;
+ }
+ } catch (SSLHandshakeException e) {
+ Log.w("Logout", "Certificate problem (please switch to unsafe connection).");
+ return CERTIFICATE_ERROR;
+
+ } catch (Exception e) {
+ Log.d("Logout", "ERROR", e);
+ return OTHER_ERROR;
+ }
+
+
+ }
+
+
+
+
+//----------------------------------------LOGOUT ENDS-----------------------------------------------
+
+
+
+
+//-------------------------------------------MISC---------------------------------------------------
+ public static String extractUserName(Document doc)
+ {
+ if(doc!=null)
+ {
+ Elements user = doc.select("div[id=myuser] > h3");
+
+ if (user.size()==1)
+ {
+ String txt = user.first().ownText();
+
+ Pattern pattern = Pattern.compile(", (.*?),");
+ Matcher matcher = pattern.matcher(txt);
+ if (matcher.find())
+ return matcher.group(1);
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/app/src/main/res/drawable/row.xml b/app/src/main/res/drawable/row.xml
new file mode 100644
index 00000000..8b48a759
--- /dev/null
+++ b/app/src/main/res/drawable/row.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml
new file mode 100644
index 00000000..9501a130
--- /dev/null
+++ b/app/src/main/res/layout/activity_about.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 00000000..a471081e
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_topic.xml b/app/src/main/res/layout/activity_topic.xml
new file mode 100644
index 00000000..248bef4b
--- /dev/null
+++ b/app/src/main/res/layout/activity_topic.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_topic_post_row.xml b/app/src/main/res/layout/activity_topic_post_row.xml
new file mode 100644
index 00000000..d92c5ce8
--- /dev/null
+++ b/app/src/main/res/layout/activity_topic_post_row.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_recent.xml b/app/src/main/res/layout/fragment_recent.xml
new file mode 100644
index 00000000..41ac000f
--- /dev/null
+++ b/app/src/main/res/layout/fragment_recent.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_recent_row.xml b/app/src/main/res/layout/fragment_recent_row.xml
new file mode 100644
index 00000000..3d9e0420
--- /dev/null
+++ b/app/src/main/res/layout/fragment_recent_row.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
new file mode 100644
index 00000000..7a6c2d23
--- /dev/null
+++ b/app/src/main/res/menu/menu_main.xml
@@ -0,0 +1,15 @@
+
diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml
new file mode 100644
index 00000000..dbbdd40f
--- /dev/null
+++ b/app/src/main/res/values-v21/styles.xml
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 00000000..63fc8164
--- /dev/null
+++ b/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 3ab3e9cb..f1a0b98c 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -1,6 +1,19 @@
- #3F51B5
- #303F9F
- #FF4081
+ #313335
+ #2b2b2b
+ #616161
+ #2b2b2b
+
+ #00000000
+
+ #ffffff
+ #616161
+
+ #ffb74d
+ #1e88e5
+ #f57f17
+ #7e57c2
+ #388e3c
+ #d32f2f
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 00000000..cef3abc4
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,7 @@
+
+
+ 16dp
+ 16dp
+ 16dp
+ 8dp
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index f6c5c0d8..74f67206 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,11 @@
mTHMMY
+ Settings
+ Hello World from section: %1$d
+
+ About
+ v%1$s
+ logo
+ Name
+ Login
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 5885930d..ad3e53f3 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -8,4 +8,20 @@
- @color/colorAccent
+
+
+
+
+
+
+
diff --git a/app/src/test/java/gr/thmmy/mthmmy/ExampleUnitTest.java b/app/src/test/java/gr/thmmy/mthmmy/ExampleUnitTest.java
deleted file mode 100644
index c3e8ebcd..00000000
--- a/app/src/test/java/gr/thmmy/mthmmy/ExampleUnitTest.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package gr.thmmy.mthmmy;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * @see Testing documentation
- */
-public class ExampleUnitTest {
- @Test
- public void addition_isCorrect() throws Exception {
- assertEquals(4, 2 + 2);
- }
-}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index c20bca14..01b0b9a0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,6 +3,7 @@
buildscript {
repositories {
jcenter()
+ maven { url "https://jitpack.io" }
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.2'
@@ -15,6 +16,7 @@ buildscript {
allprojects {
repositories {
jcenter()
+ maven { url "https://jitpack.io" }
}
}