mirror of https://github.com/ThmmyNoLife/mTHMMY
29 changed files with 1627 additions and 54 deletions
@ -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 <a href="http://d.android.com/tools/testing">Testing documentation</a> |
|||
*/ |
|||
@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()); |
|||
} |
|||
} |
@ -1,13 +1,44 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
|||
package="gr.thmmy.mthmmy"> |
|||
|
|||
<uses-permission android:name="android.permission.INTERNET" /> |
|||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> |
|||
|
|||
<application |
|||
android:allowBackup="true" |
|||
android:icon="@mipmap/ic_launcher" |
|||
android:label="@string/app_name" |
|||
android:supportsRtl="true" |
|||
android:theme="@style/AppTheme"> |
|||
<activity |
|||
android:name=".activities.MainActivity" |
|||
android:configChanges="orientation|screenSize" |
|||
android:label="@string/app_name" |
|||
android:launchMode="singleTop" |
|||
android:theme="@style/AppTheme.NoActionBar"> |
|||
<intent-filter> |
|||
<action android:name="android.intent.action.MAIN" /> |
|||
|
|||
<category android:name="android.intent.category.LAUNCHER" /> |
|||
</intent-filter> |
|||
</activity> |
|||
<activity |
|||
android:name=".activities.AboutActivity" |
|||
android:parentActivityName=".activities.MainActivity"> |
|||
<meta-data |
|||
android:name="android.support.PARENT_ACTIVITY" |
|||
android:value=".activities.MainActivity" /> |
|||
</activity> |
|||
<activity |
|||
android:name=".activities.TopicActivity" |
|||
android:configChanges="orientation|screenSize" |
|||
android:parentActivityName=".activities.MainActivity"> |
|||
<meta-data |
|||
android:name="android.support.PARENT_ACTIVITY" |
|||
android:value=".activities.MainActivity" /> |
|||
</activity> |
|||
<activity android:name=".activities.BaseActivity"></activity> |
|||
</application> |
|||
|
|||
</manifest> |
|||
</manifest> |
@ -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
|
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
|||
} |
@ -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<Post> 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<TopicAdapter.ViewHolder> |
|||
{ |
|||
@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<String, Void, Boolean> |
|||
{ |
|||
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; |
|||
|
|||
} |
|||
|
|||
|
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -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<RecentAdapter.ViewHolder> |
|||
{ |
|||
private final List<TopicSummary> recentList; |
|||
private final RecentFragment.OnListFragmentInteractionListener mListener; |
|||
|
|||
public RecentAdapter(List<TopicSummary> 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() + "'";
|
|||
// }
|
|||
} |
|||
} |
@ -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<TopicSummary> 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<Void, Void, Integer> |
|||
{ |
|||
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; i<recent.size(); i+=3) { |
|||
String link = recent.get(i).child(0).attr("href"); |
|||
String title = recent.get(i).child(0).attr("title"); |
|||
|
|||
String lastUser = recent.get(i + 1).text(); |
|||
Pattern pattern = Pattern.compile("by (.*)"); |
|||
Matcher matcher = pattern.matcher(lastUser); |
|||
if (matcher.find()) |
|||
lastUser = matcher.group(1); |
|||
else |
|||
{ |
|||
Log.e(TAG, "Parsing failed (lastUser)!"); |
|||
return false; |
|||
} |
|||
|
|||
String dateTime = recent.get(i + 2).text(); |
|||
pattern = Pattern.compile("\\[(.*)\\]"); |
|||
matcher = pattern.matcher(dateTime); |
|||
if (matcher.find()) |
|||
dateTime = matcher.group(1); |
|||
else |
|||
{ |
|||
Log.e(TAG, "Parsing failed (dateTime)!"); |
|||
return false; |
|||
} |
|||
|
|||
|
|||
topicSummaries.add(new TopicSummary(link, title, lastUser, dateTime)); |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
Log.e(TAG, "Parsing failed!"); |
|||
return false; |
|||
} |
|||
|
|||
// TODO: replace parse function with this when a method to get recent TOPICS and not POSTS becomes available
|
|||
// private boolean parse(String document) throws XmlPullParserException, IOException {
|
|||
// String text = null, tagName, link = null, title = null, poster = null,dateTime = null;
|
|||
// boolean posterFlag = false;
|
|||
// XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
|
|||
// factory.setNamespaceAware(true);
|
|||
// XmlPullParser xpp = factory.newPullParser();
|
|||
//
|
|||
// xpp.setInput( new StringReader(document) );
|
|||
// int eventType = xpp.getEventType();
|
|||
// topicSummaries.clear();
|
|||
//
|
|||
// while (eventType != XmlPullParser.END_DOCUMENT) {
|
|||
// tagName = xpp.getName();
|
|||
// switch (eventType) {
|
|||
// case XmlPullParser.START_TAG:
|
|||
// if (tagName.equals("poster"))
|
|||
// posterFlag=true;
|
|||
// break;
|
|||
//
|
|||
// case XmlPullParser.TEXT:
|
|||
// text = xpp.getText();
|
|||
// break;
|
|||
//
|
|||
// case XmlPullParser.END_TAG:
|
|||
// switch (tagName)
|
|||
// {
|
|||
// case "recent-post":
|
|||
// topicSummaries.add(new TopicSummary(link, title, poster, dateTime));
|
|||
// break;
|
|||
// case "name":
|
|||
// if(posterFlag) {
|
|||
// poster = text;
|
|||
// posterFlag = false;
|
|||
// }
|
|||
// break;
|
|||
// case "link":
|
|||
// link = text;
|
|||
// break;
|
|||
// case "time":
|
|||
// dateTime = text;
|
|||
// break;
|
|||
// case "subject":
|
|||
// title = text;
|
|||
// break;
|
|||
// }
|
|||
// break;
|
|||
//
|
|||
// default:
|
|||
// break;
|
|||
// }
|
|||
// eventType = xpp.next();
|
|||
// }
|
|||
// return true;
|
|||
// }
|
|||
} |
|||
|
|||
} |
@ -0,0 +1,56 @@ |
|||
package gr.thmmy.mthmmy.utils; |
|||
|
|||
import android.content.Context; |
|||
import android.support.annotation.Nullable; |
|||
import android.support.v7.widget.LinearLayoutManager; |
|||
import android.support.v7.widget.RecyclerView; |
|||
import android.util.AttributeSet; |
|||
|
|||
//Custom RecyclerView, so EdgeEffect and SwipeRefresh both work
|
|||
public class CustomRecyclerView extends RecyclerView { |
|||
private volatile boolean enableRefreshing=true; |
|||
|
|||
public CustomRecyclerView(Context context) { |
|||
super(context); |
|||
} |
|||
|
|||
public CustomRecyclerView(Context context, @Nullable AttributeSet attrs) { |
|||
super(context, attrs); |
|||
} |
|||
|
|||
public CustomRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { |
|||
super(context, attrs, defStyle); |
|||
} |
|||
|
|||
@Override |
|||
public void onScrolled(int dx, int dy) { |
|||
if(dy>0) |
|||
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); |
|||
} |
|||
|
|||
} |
@ -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<LoginData> CREATOR |
|||
= new Parcelable.Creator<LoginData>() { |
|||
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<Cookie> 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; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,6 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<shape xmlns:android="http://schemas.android.com/apk/res/android" |
|||
android:shape="rectangle"> |
|||
<solid android:color="@color/white" /> |
|||
<corners android:radius="2dp" /> |
|||
</shape> |
@ -0,0 +1,48 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" |
|||
android:id="@+id/scrollview" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent"> |
|||
|
|||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:tools="http://schemas.android.com/tools" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:paddingLeft="@dimen/activity_horizontal_margin" |
|||
android:paddingRight="@dimen/activity_horizontal_margin" |
|||
android:paddingTop="@dimen/activity_vertical_margin" |
|||
android:paddingBottom="@dimen/activity_vertical_margin" |
|||
tools:context="com.eternalpixels.toinfinity.Info"> |
|||
|
|||
<TextView android:id="@+id/appName" |
|||
android:text="@string/app_name" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:textAppearance="?android:attr/textAppearanceLarge" |
|||
android:layout_alignParentTop="true" |
|||
android:layout_centerHorizontal="true" |
|||
android:textStyle="bold" /> |
|||
|
|||
<TextView |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:textAppearance="?android:attr/textAppearanceSmall" |
|||
android:id="@+id/version" |
|||
android:layout_below="@+id/appName" |
|||
android:layout_centerHorizontal="true" |
|||
android:textStyle="italic" /> |
|||
|
|||
|
|||
<ImageView |
|||
android:contentDescription="@string/logo" |
|||
android:layout_margin="15dp" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:id="@+id/logoView" |
|||
android:src="@mipmap/ic_launcher" |
|||
android:layout_below="@id/version" |
|||
android:layout_centerHorizontal="true" /> |
|||
|
|||
|
|||
</RelativeLayout> |
|||
</ScrollView> |
@ -0,0 +1,42 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:app="http://schemas.android.com/apk/res-auto" |
|||
xmlns:tools="http://schemas.android.com/tools" |
|||
android:id="@+id/main_content" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:fitsSystemWindows="true" |
|||
tools:context=".activities.MainActivity"> |
|||
|
|||
<android.support.design.widget.AppBarLayout |
|||
android:id="@+id/appbar" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:paddingTop="@dimen/appbar_padding_top" |
|||
android:theme="@style/ToolbarTheme"> |
|||
|
|||
<android.support.v7.widget.Toolbar |
|||
android:id="@+id/toolbar" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="?attr/actionBarSize" |
|||
android:background="?attr/colorPrimary" |
|||
app:popupTheme="@style/popupTheme"> |
|||
|
|||
</android.support.v7.widget.Toolbar> |
|||
|
|||
<android.support.design.widget.TabLayout |
|||
android:id="@+id/tabs" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" /> |
|||
|
|||
</android.support.design.widget.AppBarLayout> |
|||
|
|||
<android.support.v4.view.ViewPager |
|||
android:id="@+id/container" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
app:layout_behavior="@string/appbar_scrolling_view_behavior" /> |
|||
|
|||
|
|||
|
|||
</android.support.design.widget.CoordinatorLayout> |
@ -0,0 +1,35 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:tools="http://schemas.android.com/tools" |
|||
xmlns:app="http://schemas.android.com/apk/res-auto" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:paddingBottom="@dimen/activity_vertical_margin" |
|||
android:paddingLeft="@dimen/activity_horizontal_margin" |
|||
android:paddingRight="@dimen/activity_horizontal_margin" |
|||
android:paddingTop="@dimen/activity_vertical_margin" |
|||
tools:context="gr.thmmy.mthmmy.activities.TopicActivity"> |
|||
|
|||
<view |
|||
class="gr.thmmy.mthmmy.utils.CustomRecyclerView" |
|||
android:id="@+id/list" |
|||
android:name="gr.thmmy.mthmmy.activities.Topic" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:paddingTop="4dp" |
|||
android:paddingBottom="4dp" |
|||
android:clipToPadding="false" |
|||
android:background="@color/background" |
|||
app:layoutManager="LinearLayoutManager" |
|||
tools:context="gr.thmmy.mthmmy.activities.TopicActivity" |
|||
tools:listitem="@layout/fragment_recent_row" /> |
|||
|
|||
<ProgressBar |
|||
android:visibility="invisible" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:id="@+id/progressBar" |
|||
android:layout_centerVertical="true" |
|||
android:layout_centerHorizontal="true" /> |
|||
|
|||
</RelativeLayout> |
@ -0,0 +1,32 @@ |
|||
<?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="match_parent" |
|||
android:layout_marginBottom="5dp"> |
|||
|
|||
<TextView |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_alignParentTop="true" |
|||
android:layout_alignParentLeft="true" |
|||
android:layout_alignParentStart="true" |
|||
android:text="Author" |
|||
android:id="@+id/author" /> |
|||
|
|||
<TextView |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_alignBaseline="@+id/author" |
|||
android:layout_alignParentRight="true" |
|||
android:layout_alignParentEnd="true" |
|||
android:text="DateTime" |
|||
android:id="@+id/dateTime" /> |
|||
|
|||
<WebView |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:id="@+id/content" |
|||
android:layout_below="@+id/author" |
|||
android:layout_alignRight="@+id/dateTime" |
|||
android:layout_alignEnd="@+id/dateTime" /> |
|||
</RelativeLayout> |
@ -0,0 +1,38 @@ |
|||
<?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" |
|||
xmlns:tools="http://schemas.android.com/tools" |
|||
android:layout_height="match_parent" |
|||
android:layout_width="match_parent"> |
|||
|
|||
<android.support.v4.widget.SwipeRefreshLayout |
|||
android:id="@+id/swiperefresh" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent"> |
|||
|
|||
<view |
|||
class="gr.thmmy.mthmmy.utils.CustomRecyclerView" |
|||
android:id="@+id/list" |
|||
android:name="gr.thmmy.mthmmy.sections.recent.RecentFragment" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent" |
|||
android:paddingTop="4dp" |
|||
android:paddingBottom="4dp" |
|||
android:clipToPadding="false" |
|||
android:background="@color/background" |
|||
app:layoutManager="LinearLayoutManager" |
|||
tools:context="gr.thmmy.mthmmy.sections.recent.RecentFragment" |
|||
tools:listitem="@layout/fragment_recent_row" /> |
|||
|
|||
</android.support.v4.widget.SwipeRefreshLayout> |
|||
|
|||
<ProgressBar |
|||
android:visibility="invisible" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:id="@+id/progressBar" |
|||
android:layout_centerVertical="true" |
|||
android:layout_centerHorizontal="true" /> |
|||
|
|||
</RelativeLayout> |
@ -0,0 +1,66 @@ |
|||
<?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="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:paddingEnd="4dp" |
|||
android:paddingStart="4dp" |
|||
android:orientation="vertical"> |
|||
|
|||
<android.support.v7.widget.CardView |
|||
xmlns:card_view="http://schemas.android.com/apk/res-auto" |
|||
android:id="@+id/card_view" |
|||
android:layout_gravity="center" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:foreground="?android:attr/selectableItemBackground" |
|||
card_view:cardPreventCornerOverlap="false" |
|||
card_view:cardUseCompatPadding="true" |
|||
card_view:cardCornerRadius="4dp" |
|||
card_view:cardBackgroundColor="@color/colorPrimary"> |
|||
|
|||
<RelativeLayout |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:paddingTop="6dp" |
|||
android:paddingBottom="6dp" |
|||
android:paddingLeft="10dp" |
|||
android:paddingRight="10dp" |
|||
android:layout_alignParentTop="true"> |
|||
|
|||
<TextView |
|||
android:id="@+id/title" |
|||
android:layout_width="match_parent" |
|||
android:layout_height="wrap_content" |
|||
android:layout_alignParentTop="true" |
|||
android:layout_alignParentLeft="true" |
|||
android:layout_alignParentStart="true" |
|||
android:textAppearance="?attr/textAppearanceListItem" |
|||
android:textColor="@color/myColor1" /> |
|||
|
|||
<TextView |
|||
android:id="@+id/dateTime" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_below="@+id/title" |
|||
android:layout_alignParentLeft="true" |
|||
android:layout_alignParentStart="true" |
|||
android:textColor="@color/myColor2"/> |
|||
|
|||
<TextView |
|||
android:id="@+id/lastUser" |
|||
android:textStyle="italic" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_alignBaseline="@+id/dateTime" |
|||
android:layout_alignBottom="@+id/dateTime" |
|||
android:layout_alignParentRight="true" |
|||
android:layout_alignParentEnd="true" |
|||
android:textColor="@color/myColor2"/> |
|||
|
|||
</RelativeLayout> |
|||
|
|||
</android.support.v7.widget.CardView> |
|||
|
|||
|
|||
</LinearLayout> |
@ -0,0 +1,15 @@ |
|||
<menu xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:app="http://schemas.android.com/apk/res-auto" |
|||
xmlns:tools="http://schemas.android.com/tools" |
|||
tools:context=".activities.MainActivity"> |
|||
<item |
|||
android:id="@+id/action_login" |
|||
android:orderInCategory="100" |
|||
android:title="@string/login" |
|||
app:showAsAction="never" /> |
|||
<item |
|||
android:id="@+id/action_about" |
|||
android:orderInCategory="200" |
|||
android:title="@string/about" |
|||
app:showAsAction="never" /> |
|||
</menu> |
@ -0,0 +1,9 @@ |
|||
<resources> |
|||
|
|||
<style name="AppTheme.NoActionBar"> |
|||
<item name="windowActionBar">false</item> |
|||
<item name="windowNoTitle">true</item> |
|||
<item name="android:windowDrawsSystemBarBackgrounds">true</item> |
|||
<item name="android:statusBarColor">@android:color/transparent</item> |
|||
</style> |
|||
</resources> |
@ -0,0 +1,6 @@ |
|||
<resources> |
|||
<!-- Example customization of dimensions originally defined in res/values/dimens.xml |
|||
(such as screen margins) for screens with more than 820dp of available width. This |
|||
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). --> |
|||
<dimen name="activity_horizontal_margin">64dp</dimen> |
|||
</resources> |
@ -1,6 +1,19 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<resources> |
|||
<color name="colorPrimary">#3F51B5</color> |
|||
<color name="colorPrimaryDark">#303F9F</color> |
|||
<color name="colorAccent">#FF4081</color> |
|||
<color name="colorPrimary">#313335</color> |
|||
<color name="colorPrimaryDark">#2b2b2b</color> |
|||
<color name="colorPrimaryLight">#616161</color> |
|||
<color name="colorAccent">#2b2b2b</color> |
|||
|
|||
<color name="transparent">#00000000</color> |
|||
|
|||
<color name="white">#ffffff</color> |
|||
<color name="background">#616161</color> |
|||
|
|||
<color name="myColor1">#ffb74d</color> |
|||
<color name="myColor2">#1e88e5</color> |
|||
<color name="myColor3">#f57f17</color> |
|||
<color name="myColor4">#7e57c2</color> |
|||
<color name="myColor5">#388e3c</color> |
|||
<color name="myColor6">#d32f2f</color> |
|||
</resources> |
|||
|
@ -0,0 +1,7 @@ |
|||
<resources> |
|||
<!-- Default screen margins, per the Android Design guidelines. --> |
|||
<dimen name="activity_horizontal_margin">16dp</dimen> |
|||
<dimen name="activity_vertical_margin">16dp</dimen> |
|||
<dimen name="fab_margin">16dp</dimen> |
|||
<dimen name="appbar_padding_top">8dp</dimen> |
|||
</resources> |
@ -1,3 +1,11 @@ |
|||
<resources> |
|||
<string name="app_name">mTHMMY</string> |
|||
<string name="action_settings">Settings</string> |
|||
<string name="section_format">Hello World from section: %1$d</string> |
|||
|
|||
<string name="about">About</string> |
|||
<string name="version">v%1$s</string> |
|||
<string name="logo">logo</string> |
|||
<string name="profile_name">Name</string> |
|||
<string name="login">Login</string> |
|||
</resources> |
|||
|
@ -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 <a href="http://d.android.com/tools/testing">Testing documentation</a> |
|||
*/ |
|||
public class ExampleUnitTest { |
|||
@Test |
|||
public void addition_isCorrect() throws Exception { |
|||
assertEquals(4, 2 + 2); |
|||
} |
|||
} |
Loading…
Reference in new issue