Browse Source

First actual commit

pull/24/head
Ezerous 9 years ago
parent
commit
0c47909b49
  1. 22
      app/build.gradle
  2. 26
      app/src/androidTest/java/gr/thmmy/mthmmy/ExampleInstrumentedTest.java
  3. 31
      app/src/main/AndroidManifest.xml
  4. 23
      app/src/main/java/gr/thmmy/mthmmy/activities/AboutActivity.java
  5. 39
      app/src/main/java/gr/thmmy/mthmmy/activities/BaseActivity.java
  6. 115
      app/src/main/java/gr/thmmy/mthmmy/activities/MainActivity.java
  7. 181
      app/src/main/java/gr/thmmy/mthmmy/activities/TopicActivity.java
  8. 29
      app/src/main/java/gr/thmmy/mthmmy/data/Post.java
  9. 31
      app/src/main/java/gr/thmmy/mthmmy/data/TopicSummary.java
  10. 88
      app/src/main/java/gr/thmmy/mthmmy/sections/recent/RecentAdapter.java
  11. 363
      app/src/main/java/gr/thmmy/mthmmy/sections/recent/RecentFragment.java
  12. 56
      app/src/main/java/gr/thmmy/mthmmy/utils/CustomRecyclerView.java
  13. 309
      app/src/main/java/gr/thmmy/mthmmy/utils/Thmmy.java
  14. 6
      app/src/main/res/drawable/row.xml
  15. 48
      app/src/main/res/layout/activity_about.xml
  16. 42
      app/src/main/res/layout/activity_main.xml
  17. 35
      app/src/main/res/layout/activity_topic.xml
  18. 32
      app/src/main/res/layout/activity_topic_post_row.xml
  19. 38
      app/src/main/res/layout/fragment_recent.xml
  20. 66
      app/src/main/res/layout/fragment_recent_row.xml
  21. 15
      app/src/main/res/menu/menu_main.xml
  22. 9
      app/src/main/res/values-v21/styles.xml
  23. 6
      app/src/main/res/values-w820dp/dimens.xml
  24. 19
      app/src/main/res/values/colors.xml
  25. 7
      app/src/main/res/values/dimens.xml
  26. 8
      app/src/main/res/values/strings.xml
  27. 16
      app/src/main/res/values/styles.xml
  28. 17
      app/src/test/java/gr/thmmy/mthmmy/ExampleUnitTest.java
  29. 2
      build.gradle

22
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
}
}

26
app/src/androidTest/java/gr/thmmy/mthmmy/ExampleInstrumentedTest.java

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

31
app/src/main/AndroidManifest.xml

@ -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>

23
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
}
}

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

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

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

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

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

88
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<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() + "'";
// }
}
}

363
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<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;
// }
}
}

56
app/src/main/java/gr/thmmy/mthmmy/utils/CustomRecyclerView.java

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

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

6
app/src/main/res/drawable/row.xml

@ -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>

48
app/src/main/res/layout/activity_about.xml

@ -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>

42
app/src/main/res/layout/activity_main.xml

@ -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>

35
app/src/main/res/layout/activity_topic.xml

@ -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>

32
app/src/main/res/layout/activity_topic_post_row.xml

@ -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>

38
app/src/main/res/layout/fragment_recent.xml

@ -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>

66
app/src/main/res/layout/fragment_recent_row.xml

@ -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>

15
app/src/main/res/menu/menu_main.xml

@ -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>

9
app/src/main/res/values-v21/styles.xml

@ -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>

6
app/src/main/res/values-w820dp/dimens.xml

@ -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>

19
app/src/main/res/values/colors.xml

@ -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>

7
app/src/main/res/values/dimens.xml

@ -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>

8
app/src/main/res/values/strings.xml

@ -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>

16
app/src/main/res/values/styles.xml

@ -8,4 +8,20 @@
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<!-- Toolbar style. -->
<style name="ToolbarTheme" parent="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<!-- android:textColorPrimary is the color of the title text in the Toolbar -->
<item name="android:textColorPrimary">@android:color/white</item>
</style>
<style name="popupTheme" parent="ThemeOverlay.AppCompat.Light">
<item name="android:textColorPrimary">@android:color/white</item>
<item name="android:colorBackground">@color/colorPrimary</item>
</style>
</resources>

17
app/src/test/java/gr/thmmy/mthmmy/ExampleUnitTest.java

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

2
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" }
}
}

Loading…
Cancel
Save