Browse Source

Moved Thmmy class to BaseActivity, replaced LoginData class functionality with SharedPreferences. Ran inspection tool on the whole project. COMMENTED!

pull/24/head
Apostolos Fanakis 8 years ago
parent
commit
9a548e5694
  1. 4
      app/build.gradle
  2. 4
      app/src/main/java/gr/thmmy/mthmmy/activities/AboutActivity.java
  3. 252
      app/src/main/java/gr/thmmy/mthmmy/activities/BaseActivity.java
  4. 66
      app/src/main/java/gr/thmmy/mthmmy/activities/LoginActivity.java
  5. 76
      app/src/main/java/gr/thmmy/mthmmy/activities/MainActivity.java
  6. 212
      app/src/main/java/gr/thmmy/mthmmy/activities/TopicActivity.java
  7. 11
      app/src/main/java/gr/thmmy/mthmmy/data/Post.java
  8. 11
      app/src/main/java/gr/thmmy/mthmmy/data/TopicSummary.java
  9. 8
      app/src/main/java/gr/thmmy/mthmmy/sections/recent/RecentAdapter.java
  10. 90
      app/src/main/java/gr/thmmy/mthmmy/sections/recent/RecentFragment.java
  11. 7
      app/src/main/java/gr/thmmy/mthmmy/utils/CircularNetworkImageView.java
  12. 24
      app/src/main/java/gr/thmmy/mthmmy/utils/CustomRecyclerView.java
  13. 2
      app/src/main/java/gr/thmmy/mthmmy/utils/ImageController.java
  14. 4
      app/src/main/java/gr/thmmy/mthmmy/utils/LruBitmapCache.java
  15. 249
      app/src/main/java/gr/thmmy/mthmmy/utils/Thmmy.java
  16. 8
      app/src/main/res/anim/push_left_in.xml
  17. 8
      app/src/main/res/anim/push_left_out.xml
  18. 2
      app/src/main/res/anim/push_right_in.xml
  19. 2
      app/src/main/res/anim/push_right_out.xml
  20. 4
      app/src/main/res/layout/activity_login.xml
  21. 15
      app/src/main/res/layout/activity_topic_post_row.xml
  22. 6
      app/src/main/res/layout/fragment_recent_row.xml
  23. 12
      app/src/main/res/menu/menu_main.xml
  24. 1
      app/src/main/res/values/dimens.xml
  25. 7
      app/src/main/res/values/strings.xml

4
app/build.gradle

@ -6,7 +6,7 @@ android {
defaultConfig {
applicationId "gr.thmmy.mthmmy"
minSdkVersion 16
minSdkVersion 19
targetSdkVersion 25
versionCode 2
versionName "0.16"
@ -22,7 +22,7 @@ android {
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:25.0.1'
compile 'com.android.support:design:25.0.1'
compile 'com.android.support:design:25.0.0'
compile 'com.squareup.okhttp3:okhttp:3.4.0'
compile 'com.jakewharton:butterknife:7.0.1'
compile 'org.jsoup:jsoup:1.10.1'

4
app/src/main/java/gr/thmmy/mthmmy/activities/AboutActivity.java

@ -13,8 +13,8 @@ public class AboutActivity extends BaseActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_about);
String versionName = BuildConfig.VERSION_NAME;
TextView tv= (TextView) findViewById(R.id.version);
if(tv!=null)
TextView tv = (TextView) findViewById(R.id.version);
if (tv != null)
tv.setText(getString(R.string.version, versionName));
//TODO: add licenses

252
app/src/main/java/gr/thmmy/mthmmy/activities/BaseActivity.java

@ -1,61 +1,281 @@
package gr.thmmy.mthmmy.activities;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import com.franmontiel.persistentcookiejar.PersistentCookieJar;
import com.franmontiel.persistentcookiejar.cache.SetCookieCache;
import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor;
import gr.thmmy.mthmmy.utils.Thmmy;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.net.ssl.SSLHandshakeException;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.FormBody;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class BaseActivity extends AppCompatActivity {
//Shared preferences
static final String SHARED_PREFS_NAME = "thmmySharedPrefs";
//----------------------------------------CLASS VARIABLES-----------------------------------------
/* --Response Codes-- */
static final int LOGGED_OUT = 0;
static final int LOGGED_IN = 1;
static final int WRONG_USER = 2;
static final int WRONG_PASSWORD = 3;
static final int FAILED = 4;
static final int CERTIFICATE_ERROR = 5;
static final int OTHER_ERROR = 6;
/* --Response Codes End-- */
/* --Shared Preferences-- */
static final String USER_NAME = "userNameKey";
static final String GUEST_PREF_USERNAME = "GUEST";
static final String IS_LOGGED_IN = "isLoggedIn";
static final String LOG_STATUS = "isLoggedIn";
private static final String SHARED_PREFS_NAME = "thmmySharedPrefs";
static SharedPreferences _prefs;
/* --Shared Preferences End-- */
/* --Client Stuff-- */
static OkHttpClient client;
static Thmmy.LoginData loginData;
private static CookieJar cookieJar;
private static SharedPrefsCookiePersistor sharedPrefsCookiePersistor;
private static boolean init =false; //To initialize stuff only once per app start
/* --Client Stuff End-- */
//Other variables
private static boolean init = false; //To initialize stuff only once per app start
private static final String TAG = "BaseActivity";
//--------------------------------------CLASS VARIABLES END---------------------------------------
public static CookieJar getCookieJar()
{
//-------------------------------------CLIENT AND COOKIES-----------------------------------------
private static CookieJar getCookieJar() {
return cookieJar;
}
public static SharedPrefsCookiePersistor getSharedPrefsCookiePersistor() {
private static SharedPrefsCookiePersistor getSharedPrefsCookiePersistor() {
return sharedPrefsCookiePersistor;
}
public static OkHttpClient getClient() {
return client;
}
//-----------------------------------CLIENT AND COOKIES END---------------------------------------
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(!init)
{
if (!init) {
_prefs = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE);
sharedPrefsCookiePersistor = new SharedPrefsCookiePersistor(BaseActivity.this);
cookieJar = new PersistentCookieJar(new SetCookieCache(), sharedPrefsCookiePersistor);
client = new OkHttpClient.Builder()
.cookieJar(cookieJar)
.build();
loginData = new Thmmy.LoginData();
loginData.setStatus(0);
init =true;
init = true;
}
}
void setLoginData(Thmmy.LoginData loginData) {
BaseActivity.loginData = loginData;
/*
THMMY CLASS
-- inner class of BaseActivity
This class handles all session related operations (e.g. login, logout)
Also stores data to SharedPreferences file.
*/
//---------------------------------------INNER CLASS BEGINS---------------------------------------
public static class Thmmy {
//Class variables
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");
//-------------------------------------------LOGIN------------------------------------------------
//Two options: (username, password, duration) or nothing - cookies
static void login(String... strings) {
Log.d("Login", "Logging in...");
Request request;
if (strings.length == 3) { //Actual login
String loginName = strings[0];
String password = strings[1];
String duration = strings[2];
((PersistentCookieJar) getCookieJar()).clear();
RequestBody formBody = new FormBody.Builder() //Build login form
.add("user", loginName)
.add("passwrd", password)
.add("cookielength", duration) //Forever is -1
.build();
request = new Request.Builder() //Build the request
.url(loginUrl)
.post(formBody)
.build();
} else { //Already logged in, just get cookies
request = new Request.Builder() //Build the request
.url(loginUrl)
.build();
}
try {
Response response = client.newCall(request).execute(); //Make the request
/* --Handle response-- */
Document document = Jsoup.parse(response.body().string());
Element logout = document.getElementById("logoutbtn"); //Get logout button
if (logout != null) { //If there is a logout button, then I successfully logged in
Log.i("Login", "Login successful");
setPersistentCookieSession();
//Edit SharedPreferences, save session's data
_prefs.edit().putString(USER_NAME, extractUserName(document)).apply();
_prefs.edit().putInt(LOG_STATUS, LOGGED_IN).apply();
} else { //I am not logged in, what went wrong?
Log.w("Login", "Login failed");
_prefs.edit().putInt(LOG_STATUS, FAILED).apply(); //Request failed
//Making error more specific
Elements error = document.select("b:contains(That username does not exist.)");
if (error.size() == 1) { //Wrong username
_prefs.edit().putInt(LOG_STATUS, WRONG_USER).apply();
Log.d("Login", "Wrong Username");
}
error = document.select("body:contains(Password incorrect)");
if (error.size() == 1) { //Wrong password
_prefs.edit().putInt(LOG_STATUS, WRONG_PASSWORD).apply();
Log.d("Login", "Wrong Password");
}
((PersistentCookieJar) getCookieJar()).clear();
}
//Request exception handling
} catch (SSLHandshakeException e) {
_prefs.edit().putInt(LOG_STATUS, CERTIFICATE_ERROR).apply();
Log.w("Login", "Certificate problem");
} catch (Exception e) {
_prefs.edit().putInt(LOG_STATUS, OTHER_ERROR).apply();
Log.e("Login", "Error", e);
}
}
//--------------------------------------LOGIN ENDS------------------------------------------------
//---------------------------------------LOGOUT---------------------------------------------------
static int logout() {
String _logout_link = "";
{ //Find current logout link
try {
//Build and make a request for the index (home) page
Request request = new Request.Builder()
.url(indexUrl)
.build();
Response response = client.newCall(request).execute();
Document document = Jsoup.parse(response.body().string());
Element logout = document.getElementById("logoutbtn"); //Find the logout button
_logout_link = HttpUrl.parse(logout.attr("href")).toString(); //Get the url
} catch (IOException e) {
e.printStackTrace();
}
}
if (Objects.equals(_logout_link, "")) { //If logout button wasn't found
return OTHER_ERROR; //Something went wrong
}
//Attempt logout
OkHttpClient client = getClient();
Request request = new Request.Builder()
.url(_logout_link)
.build();
try {
Response response = client.newCall(request).execute();
Document document = Jsoup.parse(response.body().string());
Elements login = document.select("[value=Login]"); //Find login button
((PersistentCookieJar) getCookieJar()).clear();
if (!login.isEmpty()) { //If found, logout was successful
Log.i("Logout", "Logout successful");
_prefs.edit().clear().apply(); //Clear session data
//User is now guest
_prefs.edit().putString(USER_NAME, GUEST_PREF_USERNAME).apply();
_prefs.edit().putInt(LOG_STATUS, LOGGED_IN).apply();
return LOGGED_OUT;
} 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-------------------------------------------------
private 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;
}
private static void setPersistentCookieSession() {
List<Cookie> cookieList = 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());
getSharedPrefsCookiePersistor().clear();
getSharedPrefsCookiePersistor().saveAll(cookieList);
}
}
}
}
//----------------------------------------INNER CLASS ENDS----------------------------------------
}

66
app/src/main/java/gr/thmmy/mthmmy/activities/LoginActivity.java

@ -2,7 +2,6 @@ package gr.thmmy.mthmmy.activities;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
@ -13,61 +12,62 @@ import android.widget.Toast;
import gr.thmmy.mthmmy.R;
import static gr.thmmy.mthmmy.utils.Thmmy.CERTIFICATE_ERROR;
import static gr.thmmy.mthmmy.utils.Thmmy.FAILED;
import static gr.thmmy.mthmmy.utils.Thmmy.LOGGED_IN;
import static gr.thmmy.mthmmy.utils.Thmmy.OTHER_ERROR;
import static gr.thmmy.mthmmy.utils.Thmmy.WRONG_PASSWORD;
import static gr.thmmy.mthmmy.utils.Thmmy.WRONG_USER;
import static gr.thmmy.mthmmy.utils.Thmmy.login;
public class LoginActivity extends BaseActivity {
private static final String TAG = "LoginActivity";
//-----------------------------------------CLASS VARIABLES------------------------------------------
/* --Graphics-- */
private Button btnLogin;
private EditText inputUsername;
private EditText inputPassword;
private String username;
private String password;
/* --Graphics End-- */
//Other variables
private static final String TAG = "LoginActivity";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
//Variables initialization
inputUsername = (EditText) findViewById(R.id.username);
inputPassword = (EditText) findViewById(R.id.password);
btnLogin = (Button) findViewById(R.id.btnLogin);
Button btnGuest = (Button) findViewById(R.id.btnContinueAsGuest);
// Login button Click Event
//Login button Click Event
btnLogin.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Log.d(TAG, "Login");
//Get username and password strings
username = inputUsername.getText().toString().trim();
password = inputPassword.getText().toString().trim();
// Check for empty data in the form
//Check for empty data in the form
if (!validate()) {
onLoginFailed();
return;
}
// login user
//Login user
new LoginTask().execute(username, password);
}
});
// Guest Button Action
//Guest Button Action
btnGuest.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
SharedPreferences.Editor editor = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE).edit();
editor.putString(USER_NAME, GUEST_PREF_USERNAME);
editor.putBoolean(IS_LOGGED_IN, true);
editor.apply();
//Session data update
_prefs.edit().putString(USER_NAME, GUEST_PREF_USERNAME).apply();
_prefs.edit().putInt(LOG_STATUS, LOGGED_IN).apply();
//Go to main
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);
finish();
@ -84,11 +84,11 @@ public class LoginActivity extends BaseActivity {
private void onLoginFailed() {
Toast.makeText(getBaseContext(), "Login failed", Toast.LENGTH_LONG).show();
btnLogin.setEnabled(true);
}
private boolean validate() {
//Handle empty text fields
boolean valid = true;
if (username.isEmpty()) {
@ -108,21 +108,23 @@ public class LoginActivity extends BaseActivity {
return valid;
}
//--------------------------------------------LOGIN-------------------------------------------------
private class LoginTask extends AsyncTask<String, Void, Integer> {
//Class variables
ProgressDialog progressDialog;
@Override
protected Integer doInBackground(String... params) {
setLoginData(login(params[0], params[1], "-1"));
return loginData.getStatus();
Thmmy.login(params[0], params[1], "-1"); //Attempt login
return _prefs.getInt(LOG_STATUS, OTHER_ERROR);
}
@Override
protected void onPreExecute() {
btnLogin.setEnabled(false);
protected void onPreExecute() { //Show a progress dialog until done
btnLogin.setEnabled(false); //Login button shouldn't be pressed during this
progressDialog = new ProgressDialog(LoginActivity.this,
R.style.AppTheme_Dark_Dialog);
R.style.AppTheme);
progressDialog.setIndeterminate(true);
progressDialog.setMessage("Authenticating...");
progressDialog.show();
@ -130,7 +132,7 @@ public class LoginActivity extends BaseActivity {
@Override
protected void onPostExecute(Integer result) {
protected void onPostExecute(Integer result) { //Handle attempt result
switch (result) {
case WRONG_USER:
Toast.makeText(getApplicationContext(),
@ -156,24 +158,22 @@ public class LoginActivity extends BaseActivity {
"Check your connection!", Toast.LENGTH_LONG)
.show();
break;
case LOGGED_IN:
SharedPreferences.Editor editor = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE).edit();
editor.putString(USER_NAME, username);
editor.putBoolean(IS_LOGGED_IN, true);
editor.apply();
case LOGGED_IN: //Successful login
Toast.makeText(getApplicationContext(),
"Login successful!", Toast.LENGTH_LONG)
.show();
//Go to main
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);
finish();
overridePendingTransition(R.anim.push_left_in, R.anim.push_left_out);
break;
}
btnLogin.setEnabled(true);
progressDialog.dismiss();
//Login failed
btnLogin.setEnabled(true); //Re-enable login button
progressDialog.dismiss(); //Hide progress dialog
}
}
//---------------------------------------LOGIN ENDS-------------------------------------------------
}

76
app/src/main/java/gr/thmmy/mthmmy/activities/MainActivity.java

@ -1,7 +1,7 @@
package gr.thmmy.mthmmy.activities;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
@ -14,16 +14,18 @@ import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import java.util.Objects;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.data.TopicSummary;
import gr.thmmy.mthmmy.sections.recent.RecentFragment;
import gr.thmmy.mthmmy.utils.Thmmy;
import static gr.thmmy.mthmmy.activities.BaseActivity.Thmmy.logout;
public class MainActivity extends BaseActivity implements RecentFragment.OnListFragmentInteractionListener {
/**
* The {@link ViewPager} that will host the section contents.
*/
//----------------------------------------CLASS VARIABLES-----------------------------------------
private static final String TAG = "MainActivity";
private Menu menu;
@Override
@ -31,21 +33,22 @@ public class MainActivity extends BaseActivity implements RecentFragment.OnListF
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SharedPreferences prefs = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE);
if (!prefs.getBoolean(IS_LOGGED_IN, false)) {
if (_prefs.getInt(LOG_STATUS, OTHER_ERROR) != LOGGED_IN) { //If not logged in
//Go to login
Intent intent = new Intent(MainActivity.this, LoginActivity.class);
startActivity(intent);
finish();
overridePendingTransition(R.anim.push_right_in, R.anim.push_right_out);
}
//Initialize toolbar
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// Create the adapter that will return a fragment for each section of the activity
//Create the adapter that will return a fragment for each section of the activity
SectionsPagerAdapter mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
//Set up the ViewPager with the sections adapter.
ViewPager mViewPager = (ViewPager) findViewById(R.id.container);
mViewPager.setAdapter(mSectionsPagerAdapter);
@ -62,14 +65,16 @@ public class MainActivity extends BaseActivity implements RecentFragment.OnListF
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
//Inflate the menu; this adds items to the action bar if it is present.
this.menu = menu;
getMenuInflater().inflate(R.menu.menu_main, menu);
SharedPreferences prefs = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE);
if (prefs.getBoolean(IS_LOGGED_IN, false)
&& prefs.getString(USER_NAME, null) != GUEST_PREF_USERNAME)
if (_prefs.getInt(LOG_STATUS, OTHER_ERROR) == LOGGED_IN
&& !Objects.equals(_prefs.getString(USER_NAME, null), GUEST_PREF_USERNAME)) {
//Will enter when logged out or if user is guest
hideLogin();
else
} else
//Will enter when logged in
hideLogout();
return true;
@ -80,12 +85,15 @@ public class MainActivity extends BaseActivity implements RecentFragment.OnListF
int id = item.getItemId();
if (id == R.id.action_about) {
//Go to about
Intent i = new Intent(MainActivity.this, AboutActivity.class);
startActivity(i);
return true;
} else if (id == R.id.action_logout)
//Attempt logout
new LogoutTask().execute();
else {
//Go to login
Intent intent = new Intent(MainActivity.this, LoginActivity.class);
startActivity(intent);
finish();
@ -95,14 +103,14 @@ public class MainActivity extends BaseActivity implements RecentFragment.OnListF
return super.onOptionsItemSelected(item);
}
private void hideLogin() {
private void hideLogin() { //Hide login AND show logout
MenuItem login = menu.findItem(R.id.action_login);
MenuItem logout = menu.findItem(R.id.action_logout);
login.setVisible(false);
logout.setVisible(true);
}
private void hideLogout() {
private void hideLogout() { //Hide logout AND show login
MenuItem login = menu.findItem(R.id.action_login);
MenuItem logout = menu.findItem(R.id.action_logout);
login.setVisible(true);
@ -156,27 +164,37 @@ public class MainActivity extends BaseActivity implements RecentFragment.OnListF
return null;
}
}
//-------------------------------FragmentPagerAdapter END-------------------------------------------
//-------------------------------------------LOGOUT-------------------------------------------------
private class LogoutTask extends AsyncTask<Void, Void, Integer> { //Attempt logout
ProgressDialog progressDialog;
private class LogoutTask extends AsyncTask<Void, Void, Integer> {
protected Integer doInBackground(Void... voids) {
return Thmmy.logout(loginData);
return logout();
}
protected void onPreExecute() {
//TODO: a progressbar maybe?
protected void onPreExecute() { //Show a progress dialog until done
progressDialog = new ProgressDialog(MainActivity.this,
R.style.AppTheme);
progressDialog.setIndeterminate(true);
progressDialog.setMessage("Logging out...");
progressDialog.show();
}
protected void onPostExecute(Integer result) {
if (result == Thmmy.LOGGED_OUT) {
SharedPreferences.Editor editor = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE).edit();
editor.putString(USER_NAME, null);
editor.putBoolean(IS_LOGGED_IN, false);
editor.apply();
protected void onPostExecute(Integer result) { //Handle attempt result
progressDialog.dismiss(); //Hide progress dialog
if (result == LOGGED_OUT) { //Successful logout
/*
At this point result is LOGGED_OUT
BUT pref's LOGIN_STATUS variable is LOGGED_IN!!
and USER_NAME is GUEST
*/
Toast.makeText(getBaseContext(), "Logged out successfully!", Toast.LENGTH_LONG).show();
hideLogout();
} else
} else //Logout failed
hideLogin();
}
}
}
//-----------------------------------------LOGOUT END-----------------------------------------------
}

212
app/src/main/java/gr/thmmy/mthmmy/activities/TopicActivity.java

@ -39,16 +39,29 @@ import okhttp3.Request;
import okhttp3.Response;
public class TopicActivity extends BaseActivity {
private static final int THUMBNAIL_SIZE = 80;
private final SparseArray<String> pagesUrls = new SparseArray<>();
private ImageLoader imageLoader = ImageController.getInstance().getImageLoader();
private ProgressBar progressBar;
//-----------------------------------------CLASS VARIABLES------------------------------------------
/* --Post-- */
private List<Post> postsList;
private EditText pageSelect;
private LinearLayout postsLinearLayout;
/* --Topic's page-- */
private int thisPage = 1;
private String base_url = "";
private int numberOfPages = 1;
private EditText pageSelect;
private final SparseArray<String> pagesUrls = new SparseArray<>();
/* --Topic's page end-- */
/* --Thumbnail-- */
private static final int THUMBNAIL_SIZE = 80;
private ImageLoader imageLoader = ImageController.getInstance().getImageLoader();
/* --Thumbnail end-- */
//Other variables
private ProgressBar progressBar;
private static final String TAG = "TopicActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -58,6 +71,7 @@ public class TopicActivity extends BaseActivity {
Bundle extras = getIntent().getExtras();
final String topicTitle = getIntent().getExtras().getString("TOPIC_TITLE");
//Variables initialization
postsLinearLayout = (LinearLayout) findViewById(R.id.posts_list);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
@ -65,32 +79,39 @@ public class TopicActivity extends BaseActivity {
if (imageLoader == null)
imageLoader = ImageController.getInstance().getImageLoader();
postsList = new ArrayList<>();
ActionBar actionbar = getSupportActionBar();
if (actionbar != null)
actionbar.setTitle(topicTitle);
/* Add page select field to the Action Bar */
//Edit Action Bar's parameters
ActionBar.LayoutParams lp = new ActionBar.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT
, LinearLayout.LayoutParams.WRAP_CONTENT
, Gravity.END | Gravity.CENTER_VERTICAL);
//Initialize layout to be added
View customNav = LayoutInflater.from(this).inflate(R.layout.topic_page_select, null);
//Initialize text field
pageSelect = (EditText) customNav.findViewById(R.id.select_page);
pageSelect.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { //When page is changed
if (actionId == EditorInfo.IME_ACTION_GO) {
//Get page requested by user
int pageRequested = Integer.parseInt(pageSelect.getText().toString());
if (pageRequested == thisPage) {
Toast.makeText(getBaseContext()
, "You already are here!", Toast.LENGTH_LONG).show();
} else if (pageRequested >= 1 && pageRequested <= numberOfPages) {
//Restart activity with new page
Intent intent = getIntent();
intent.putExtra("TOPIC_URL", pagesUrls.get(pageRequested - 1));
intent.putExtra("TOPIC_TITLE", topicTitle);
finish();
startActivity(intent);
} else {
} else { //Invalid page request
Toast.makeText(getBaseContext()
, "There is no such page!", Toast.LENGTH_LONG).show();
}
@ -99,171 +120,190 @@ public class TopicActivity extends BaseActivity {
}
});
assert actionbar != null;
actionbar.setCustomView(customNav, lp);
actionbar.setCustomView(customNav, lp); //Add feature to Action Bar
actionbar.setDisplayShowCustomEnabled(true);
postsList = new ArrayList<>();
new TopicTask().execute(extras.getString("TOPIC_URL"));
new TopicTask().execute(extras.getString("TOPIC_URL")); //Attempt data parsing
}
@Override
protected void onDestroy() {
protected void onDestroy() { //When finished cancel whatever request can still be canceled
super.onDestroy();
ImageController.getInstance().cancelPendingRequests();
}
//---------------------------------------TOPIC ASYNC TASK-------------------------------------------
private void populateLayout() {
LayoutInflater inflater = (LayoutInflater) getApplicationContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
for (Post item : postsList) {
View convertView = inflater.inflate(R.layout.activity_topic_post_row
, postsLinearLayout, false);
if (imageLoader == null)
imageLoader = ImageController.getInstance().getImageLoader();
CircularNetworkImageView thumbnail = (CircularNetworkImageView) convertView.findViewById(R.id.thumbnail);
TextView username = (TextView) convertView.findViewById(R.id.username);
TextView postNum = (TextView) convertView.findViewById(R.id.post_number);
TextView subject = (TextView) convertView.findViewById(R.id.subject);
WebView post = (WebView) convertView.findViewById(R.id.post);
//Avoiding errors about layout having 0 width/height
thumbnail.setMinimumWidth(1);
thumbnail.setMinimumHeight(1);
//Set thumbnail size
thumbnail.setMaxWidth(THUMBNAIL_SIZE);
thumbnail.setMaxHeight(THUMBNAIL_SIZE);
// thumbnail image
if (item.getThumbnailUrl() != null) {
thumbnail.setImageUrl(item.getThumbnailUrl(), imageLoader);
}
username.setText(item.getAuthor());
if (item.getPostNumber() != 0)
postNum.setText("#" + item.getPostNumber());
subject.setText(item.getSubject());
post.loadDataWithBaseURL("file:///android_asset/", item.getContent(), "text/html", "UTF-8", null);
post.setEnabled(false);
postsLinearLayout.addView(convertView);
}
}
public class TopicTask extends AsyncTask<String, Void, Boolean> {
private static final String TAG = "TopicTask";
private String pageLink;
private Document document;
//Class variables
private static final String TAG = "TopicTask"; //Separate tag for AsyncTask
//Show a progress bar until done
protected void onPreExecute() {
progressBar.setVisibility(ProgressBar.VISIBLE);
}
protected Boolean doInBackground(String... strings) {
base_url = strings[0].substring(0, strings[0].lastIndexOf("."));
pageLink = strings[0];
Document document;
base_url = strings[0].substring(0, strings[0].lastIndexOf(".")); //This topic's base url
String pageLink = strings[0]; //This page's url
Request request = new Request.Builder()
.url(pageLink)
.build();
try {
Response response = client.newCall(request).execute();
document = Jsoup.parse(response.body().string());
return parse(document);
parse(document); //Parse data
return true;
} 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;
}
return false;
}
protected void onPostExecute(Boolean result) {
progressBar.setVisibility(ProgressBar.INVISIBLE);
populateLayout();
if (!result) { //Parse failed!
//Should never happen
Toast.makeText(getBaseContext()
, "Fatal error!\n Aborting...", Toast.LENGTH_LONG).show();
finish();
}
//Parse was successful
progressBar.setVisibility(ProgressBar.INVISIBLE); //Hide progress bar
populateLayout(); //Show parsed data
//Set current page
pageSelect.setHint(String.valueOf(thisPage) + "/" + String.valueOf(numberOfPages));
}
private boolean parse(Document document) {
{
Elements findCurrentPage = document.select("td:contains(Pages:)>b");
/* Parse method */
private void parse(Document document) {
{ //Find current page's index
Elements findCurrentPage = document.select("td:contains(Pages:)>b"); //Contains pages
for (Element item : findCurrentPage) {
if (!item.text().contains("...")
&& !item.text().contains("Pages")) {
if (!item.text().contains("...") //It's not "..."
&& !item.text().contains("Pages")) { //Nor "Pages"
thisPage = Integer.parseInt(item.text());
break;
}
}
}
{
Elements footer_pages = document.select("td:contains(Pages:)>a.navPages");
if (footer_pages.size() != 0) {
numberOfPages = thisPage;
for (Element item : footer_pages) {
{ //Find number of pages
Elements pages = document.select("td:contains(Pages:)>a.navPages"); //Contains all pages
if (pages.size() != 0) {
numberOfPages = thisPage; //Initialize the number
for (Element item : pages) { //Just a max
if (Integer.parseInt(item.text()) > numberOfPages)
numberOfPages = Integer.parseInt(item.text());
}
}
for (int i = 0; i < numberOfPages; i++) {
//Generate each page's url from topic's base url +".15*numberOfPage"
pagesUrls.put(i, base_url + "." + String.valueOf(i * 15));
}
}
//Each element is a post row
//Each element is a post's row
Elements rows = document.select("form[id=quickModForm]>table>tbody>tr:matches(on)");
for (Element item: rows) { //For every post
for (Element item : rows) { //For every post
//Variables to pass
String p_userName, p_thumbnailUrl, p_subject, p_post;
int p_postNum;
//Find the Username
Element userName = item.select("a[title^=View the profile of]").first();
if(userName == null){ //Deleted profile
if (userName == null) { //Deleted profile
p_userName = item
.select("td:has(div.smalltext:containsOwn(Guest))[style^=overflow]")
.first().text();
p_userName = p_userName.substring(0, p_userName.indexOf(" Guest"));
}
else
} else
p_userName = userName.html();
//Find thumbnail url
Element thumbnailUrl = item.select("img.avatar").first();
p_thumbnailUrl = null; //In case user doesn't have an avatar
if(thumbnailUrl != null){
if (thumbnailUrl != null) {
p_thumbnailUrl = thumbnailUrl.attr("abs:src");
}
//Find subject
p_subject = item.select("div[id^=subject_]").first().select("a").first().text();
//Find post's text
p_post = item.select("div").select(".post").first().html();
//Add stuff to make it work in WebView
p_post = ("<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />"
+ p_post); //style.css
//Find post's index number
Element postNum = item.select("div.smalltext:matches(Reply #)").first();
if(postNum == null){ //Topic starter
if (postNum == null) { //Topic starter
p_postNum = 0;
}
else{
} else {
String tmp_str = postNum.text().substring(9);
p_postNum = Integer.parseInt(tmp_str.substring(0, tmp_str.indexOf(" on")));
}
//Add new post in postsList
postsList.add(new Post(p_thumbnailUrl, p_userName, p_subject
, p_post, p_postNum));
}
return true;
}
/* Parse method end */
}
//-------------------------------------TOPIC ASYNC TASK END-----------------------------------------
private void populateLayout() { //Show parsed data
//Initialize an inflater
LayoutInflater inflater = (LayoutInflater) getApplicationContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
for (Post item : postsList) {
//Inflate a topic post row layout
View convertView = inflater.inflate(R.layout.activity_topic_post_row
, postsLinearLayout, false);
if (imageLoader == null)
imageLoader = ImageController.getInstance().getImageLoader();
//Initialize layout's graphic elements
CircularNetworkImageView thumbnail = (CircularNetworkImageView) convertView.findViewById(R.id.thumbnail);
TextView username = (TextView) convertView.findViewById(R.id.username);
TextView postNum = (TextView) convertView.findViewById(R.id.post_number);
TextView subject = (TextView) convertView.findViewById(R.id.subject);
WebView post = (WebView) convertView.findViewById(R.id.post);
//Avoiding errors about layout having 0 width/height
thumbnail.setMinimumWidth(1);
thumbnail.setMinimumHeight(1);
//Set thumbnail size
thumbnail.setMaxWidth(THUMBNAIL_SIZE);
thumbnail.setMaxHeight(THUMBNAIL_SIZE);
//Thumbnail image set
if (item.getThumbnailUrl() != null) {
thumbnail.setImageUrl(item.getThumbnailUrl(), imageLoader);
}
//Username set
username.setText(item.getAuthor());
//Post's index number set
if (item.getPostNumber() != 0)
postNum.setText("#" + item.getPostNumber());
//Subject set
subject.setText(item.getSubject());
//Post's text set
post.loadDataWithBaseURL("file:///android_asset/", item.getContent(), "text/html", "UTF-8", null);
post.setEnabled(false);
//Add view to the linear layout that holds all posts
postsLinearLayout.addView(convertView);
}
}
}

11
app/src/main/java/gr/thmmy/mthmmy/data/Post.java

@ -1,7 +1,6 @@
package gr.thmmy.mthmmy.data;
public class Post
{
public class Post {
private final String thumbnailUrl;
private final String author;
private final String subject;
@ -16,7 +15,9 @@ public class Post
this.postNumber = postNumber;
}
public String getThumbnailUrl() { return thumbnailUrl;}
public String getThumbnailUrl() {
return thumbnailUrl;
}
public String getContent() {
return content;
@ -30,5 +31,7 @@ public class Post
return subject;
}
public int getPostNumber(){ return postNumber;}
public int getPostNumber() {
return postNumber;
}
}

11
app/src/main/java/gr/thmmy/mthmmy/data/TopicSummary.java

@ -1,22 +1,23 @@
package gr.thmmy.mthmmy.data;
public class TopicSummary
{
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)
{
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 getTopicUrl() {
return topicUrl;
}
public String getTitle() {
return title;
}

8
app/src/main/java/gr/thmmy/mthmmy/sections/recent/RecentAdapter.java

@ -6,18 +6,17 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.List;
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>
{
public class RecentAdapter extends RecyclerView.Adapter<RecentAdapter.ViewHolder> {
private final List<TopicSummary> recentList;
private final RecentFragment.OnListFragmentInteractionListener mListener;
@ -35,7 +34,6 @@ public class RecentAdapter extends RecyclerView.Adapter<RecentAdapter.ViewHolder
}
@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {

90
app/src/main/java/gr/thmmy/mthmmy/sections/recent/RecentFragment.java

@ -14,11 +14,6 @@ import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.Toast;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.BaseActivity;
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;
@ -29,6 +24,10 @@ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.BaseActivity;
import gr.thmmy.mthmmy.data.TopicSummary;
import gr.thmmy.mthmmy.utils.CustomRecyclerView;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
@ -41,8 +40,7 @@ import okhttp3.Response;
* Use the {@link RecentFragment#newInstance} factory method to
* create an instance of this fragment.
*/
public class RecentFragment extends 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";
@ -59,7 +57,8 @@ public class RecentFragment extends Fragment
private OkHttpClient client;
// Required empty public constructor
public RecentFragment() {}
public RecentFragment() {
}
/**
* Use ONLY this factory method to create a new instance of
@ -68,8 +67,7 @@ public class RecentFragment extends Fragment
* @param sectionNumber
* @return A new instance of fragment Recent.
*/
public static RecentFragment newInstance(int sectionNumber)
{
public static RecentFragment newInstance(int sectionNumber) {
RecentFragment fragment = new RecentFragment();
Bundle args = new Bundle();
args.putInt(ARG_SECTION_NUMBER, sectionNumber);
@ -82,8 +80,7 @@ public class RecentFragment extends Fragment
}
@Override
public void onCreate(Bundle savedInstanceState)
{
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sectionNumber = getArguments().getInt(ARG_SECTION_NUMBER);
@ -93,63 +90,60 @@ public class RecentFragment extends Fragment
topicSummaries = new ArrayList<>();
if(sectionNumber==1) //?
Log.d(TAG,"onCreate");
if (sectionNumber == 1) //?
Log.d(TAG, "onCreate");
}
@Override
public void onActivityCreated(Bundle savedInstanceState)
{
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if(sectionNumber==1)//temp
if (sectionNumber == 1)//temp
{
if(topicSummaries.isEmpty())
if (topicSummaries.isEmpty())
new RecentTask().execute();
}
Log.d(TAG,"onActivityCreated");
Log.d(TAG, "onActivityCreated");
}
@Override
public void onStart() {
super.onStart();
if(sectionNumber==1)
Log.d(TAG,"onStart");
if (sectionNumber == 1)
Log.d(TAG, "onStart");
}
@Override
public void onResume() {
super.onResume();
if(sectionNumber==1)
Log.d(TAG,"onResume");
if (sectionNumber == 1)
Log.d(TAG, "onResume");
}
@Override
public void onPause() {
super.onPause();
if(sectionNumber==1)
Log.d(TAG,"onPause");
if (sectionNumber == 1)
Log.d(TAG, "onPause");
}
@Override
public void onStop() {
super.onStop();
if(sectionNumber==1)
Log.d(TAG,"onStop");
if (sectionNumber == 1)
Log.d(TAG, "onStop");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
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)
{
if (rootView instanceof RelativeLayout) {
progressBar = (ProgressBar) rootView.findViewById(R.id.progressBar);
recentAdapter = new RecentAdapter(topicSummaries, mListener);
@ -206,9 +200,8 @@ public class RecentFragment extends Fragment
//---------------------------------------ASYNC TASK-----------------------------------
public class RecentTask extends AsyncTask<Void, Void, Integer>
{
private static final String TAG="RecentTask";
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;
@ -219,8 +212,7 @@ public class RecentFragment extends Fragment
progressBar.setVisibility(ProgressBar.VISIBLE);
}
protected Integer doInBackground(Void... voids)
{
protected Integer doInBackground(Void... voids) {
Request request = new Request.Builder()
.url(thmmyUrl)
@ -231,7 +223,7 @@ public class RecentFragment extends Fragment
parse(document);
return 0;
} catch (IOException e) {
Log.d("DEB", "ERROR", e);
Log.d("DEB", "ERROR", e);
return 1;
} catch (Exception e) {
Log.d("DEB", "ERROR", e);
@ -241,27 +233,23 @@ public class RecentFragment extends Fragment
}
protected void onPostExecute(Integer result) {
protected void onPostExecute(Integer result)
{
if(result==0)
if (result == 0)
recentAdapter.notifyDataSetChanged();
else if (result==1)
else if (result == 1)
Toast.makeText(getActivity(), "Network error", Toast.LENGTH_SHORT).show();
progressBar.setVisibility(ProgressBar.INVISIBLE);
swipeRefreshLayout.setRefreshing(false);
}
private void parse(Document document)
{
private void parse(Document document) {
Elements recent = document.select("#block8 :first-child div");
if(recent.size()==30)
{
if (recent.size() == 30) {
topicSummaries.clear();
for(int i=0; i<recent.size(); i+=3) {
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");
@ -270,8 +258,7 @@ public class RecentFragment extends Fragment
Matcher matcher = pattern.matcher(lastUser);
if (matcher.find())
lastUser = matcher.group(1);
else
{
else {
Log.e(TAG, "Parsing failed (lastUser)!");
return;
}
@ -281,8 +268,7 @@ public class RecentFragment extends Fragment
matcher = pattern.matcher(dateTime);
if (matcher.find())
dateTime = matcher.group(1);
else
{
else {
Log.e(TAG, "Parsing failed (dateTime)!");
return;
}

7
app/src/main/java/gr/thmmy/mthmmy/utils/CircularNetworkImageView.java

@ -36,7 +36,7 @@ public class CircularNetworkImageView extends NetworkImageView {
@Override
public void setImageBitmap(Bitmap bm) {
if(bm==null) return;
if (bm == null) return;
setImageDrawable(new BitmapDrawable(mContext.getResources(),
getCircularBitmap(bm)));
}
@ -44,9 +44,6 @@ public class CircularNetworkImageView extends NetworkImageView {
/**
* Creates a circular bitmap and uses whichever dimension is smaller to determine the width
* <br/>Also constrains the circle to the leftmost part of the image
*
* @param bitmap
* @return bitmap
*/
private Bitmap getCircularBitmap(Bitmap bitmap) {
bitmap = Bitmap.createScaledBitmap(bitmap, THUMBNAIL_SIZE, THUMBNAIL_SIZE, false);
@ -54,7 +51,7 @@ public class CircularNetworkImageView extends NetworkImageView {
bitmap.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(output);
int size = bitmap.getWidth();
if(bitmap.getWidth()>bitmap.getHeight())
if (bitmap.getWidth() > bitmap.getHeight())
size = bitmap.getHeight();
final int color = 0xff424242;
final Paint paint = new Paint();

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

@ -8,7 +8,7 @@ import android.util.AttributeSet;
//Custom RecyclerView, so EdgeEffect and SwipeRefresh both work
public class CustomRecyclerView extends RecyclerView {
private volatile boolean enableRefreshing=true;
private volatile boolean enableRefreshing = true;
public CustomRecyclerView(Context context) {
super(context);
@ -24,22 +24,20 @@ public class CustomRecyclerView extends RecyclerView {
@Override
public void onScrolled(int dx, int dy) {
if(dy>0)
enableRefreshing=false;
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;
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);
@ -47,7 +45,7 @@ public class CustomRecyclerView extends RecyclerView {
@Override
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {
if(enableRefreshing)
if (enableRefreshing)
return super.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);
else
return super.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, 0, offsetInWindow);

2
app/src/main/java/gr/thmmy/mthmmy/utils/ImageController.java

@ -53,7 +53,7 @@ public class ImageController extends Application {
getRequestQueue().add(req);
}
public void cancelPendingRequests(){
public void cancelPendingRequests() {
mRequestQueue.cancelAll(new RequestQueue.RequestFilter() {
@Override
public boolean apply(Request<?> request) {

4
app/src/main/java/gr/thmmy/mthmmy/utils/LruBitmapCache.java

@ -9,11 +9,11 @@ class LruBitmapCache extends LruCache<String, Bitmap> implements
ImageCache {
private static final int CACHE_SIZE_DIVIDER = 8;
LruBitmapCache() {
LruBitmapCache() {
this(getDefaultLruCacheSize());
}
private LruBitmapCache(int sizeInKiloBytes) {
private LruBitmapCache(int sizeInKiloBytes) {
super(sizeInKiloBytes);
}

249
app/src/main/java/gr/thmmy/mthmmy/utils/Thmmy.java

@ -1,249 +0,0 @@
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 {
public static final int LOGGED_OUT = 0;
public static final int LOGGED_IN = 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;
private static final HttpUrl loginUrl = HttpUrl.parse("https://www.thmmy.gr/smf/index.php?action=login2");
//-------------------------------------------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 = BaseActivity.getClient();
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(LOGGED_IN);
} 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;
}
private static void 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);
}
}
}
//--------------------------------------LOGOUT--------------------------------------------------
public static int logout(LoginData loginData) {
OkHttpClient client = BaseActivity.getClient();
Request request = new Request.Builder()
.url(loginData.getLogoutLink())
.build();
try {
Response response = client.newCall(request).execute();
Document document = Jsoup.parse(response.body().string());
Elements login = document.select("[value=Login]");
((PersistentCookieJar) BaseActivity.getCookieJar()).clear();
if (!login.isEmpty()) {
Log.i("Logout", "Logout successful");
loginData.setStatus(LOGGED_OUT);
return LOGGED_OUT;
} 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;
}
}
//-------------------------------------LOGIN ENDS-----------------------------------------------
//-------------------------------------------MISC---------------------------------------------------
private 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;
}
//----------------------------------------LOGOUT ENDS-----------------------------------------------
//To maintain data between activities/ between activity state change (possibly temporary solution)
public static class LoginData implements Parcelable {
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 int status;
private String username;
private HttpUrl logoutLink;
public LoginData() {
}
private LoginData(Parcel in) {
status = in.readInt();
username = in.readString();
logoutLink = HttpUrl.parse(in.readString());
}
public String getUsername() {
return username;
}
void setUsername(String username) {
this.username = username;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
HttpUrl getLogoutLink() {
return logoutLink;
}
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());
}
}
}

8
app/src/main/res/anim/push_left_in.xml

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="500"
android:fromXDelta="100%p"
android:toXDelta="0" />
<translate
android:duration="500"
android:fromXDelta="100%p"
android:toXDelta="0"/>
</set>

8
app/src/main/res/anim/push_left_out.xml

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="500"
android:fromXDelta="0"
android:toXDelta="-100%p" />
<translate
android:duration="500"
android:fromXDelta="0"
android:toXDelta="-100%p"/>
</set>

2
app/src/main/res/anim/push_right_in.xml

@ -3,6 +3,6 @@
<translate
android:duration="500"
android:fromXDelta="-100%p"
android:toXDelta="0" />
android:toXDelta="0"/>
</set>

2
app/src/main/res/anim/push_right_out.xml

@ -3,6 +3,6 @@
<translate
android:duration="500"
android:fromXDelta="0"
android:toXDelta="100%p" />
android:toXDelta="100%p"/>
</set>

4
app/src/main/res/layout/activity_login.xml

@ -14,7 +14,7 @@
<Space
android:layout_width="match_parent"
android:layout_height="25dp" />
android:layout_height="25dp"/>
<ImageView
android:id="@+id/thmmyLogo"
@ -36,7 +36,7 @@
<Space
android:layout_width="match_parent"
android:layout_height="10dp" />
android:layout_height="10dp"/>
<!-- Username Label -->
<android.support.design.widget.TextInputLayout

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

@ -38,7 +38,6 @@
android:layout_centerVertical="true"
android:layout_marginBottom="3dp"
android:layout_marginEnd="3dp"
android:layout_marginRight="3dp"
android:layout_marginTop="3dp">
<gr.thmmy.mthmmy.utils.CircularNetworkImageView
@ -49,8 +48,6 @@
android:maxHeight="@dimen/thumbnail_size"
android:maxWidth="@dimen/thumbnail_size"
android:paddingEnd="2dp"
android:paddingLeft="2dp"
android:paddingRight="5dp"
android:paddingStart="2dp"/>
</FrameLayout>
@ -60,12 +57,10 @@
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginEnd="5dp"
android:layout_marginRight="5dp"
android:layout_toEndOf="@+id/thumbnail_holder"
android:layout_toRightOf="@+id/thumbnail_holder"
android:ellipsize="end"
android:maxLines="1"
android:text="sometext"
android:text="@string/username"
android:textColor="@color/black"
android:textStyle="bold"/>
@ -74,10 +69,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="100dp"
android:layout_marginStart="100dp"
android:text=""/>
<TextView
@ -86,8 +78,7 @@
android:layout_height="wrap_content"
android:layout_below="@+id/username"
android:layout_toEndOf="@+id/thumbnail_holder"
android:layout_toRightOf="@+id/thumbnail_holder"
android:text="sometext"
android:text="@string/subject"
/>
</RelativeLayout>
@ -114,7 +105,7 @@
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:background="@color/white"
android:text="sometext"
android:text="@string/post"
/>
</FrameLayout>
</LinearLayout>

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

@ -31,7 +31,6 @@
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:textAppearance="?attr/textAppearanceListItem"
@ -43,14 +42,12 @@
android:layout_height="20dp"
android:layout_alignParentBottom="@+id/title"
android:layout_below="@+id/title"
android:layout_toEndOf="@+id/dateTime"
android:layout_toRightOf="@+id/dateTime"/>
android:layout_toEndOf="@+id/dateTime"/>
<TextView
android:id="@+id/dateTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/spacer"/>
@ -61,7 +58,6 @@
android:layout_alignBaseline="@+id/dateTime"
android:layout_alignBottom="@+id/dateTime"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:textStyle="italic"/>
</RelativeLayout>
</android.support.v7.widget.CardView>

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

@ -1,22 +1,22 @@
<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">
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"
android:visible="false"
app:showAsAction="never" />
app:showAsAction="never"/>
<item
android:id="@+id/action_logout"
android:orderInCategory="200"
android:title="@string/logout"
android:visible="false"
app:showAsAction="never" />
app:showAsAction="never"/>
<item
android:id="@+id/action_about"
android:orderInCategory="300"
android:title="@string/about"
app:showAsAction="never" />
app:showAsAction="never"/>
</menu>

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

@ -2,7 +2,6 @@
<!-- 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>
<dimen name="thumbnail_size">80dp</dimen>
</resources>

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

@ -1,7 +1,5 @@
<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="thmmy_img_description">thmmy.gr</string>
<string name="hint_username">Username</string>
@ -12,7 +10,10 @@
<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>
<string name="logout">Logout</string>
<string name="username">Username should be here...</string>
<string name="subject">Subject should be here...</string>
<string name="post">Post should be here...</string>
</resources>

Loading…
Cancel
Save