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. 248
      app/src/main/java/gr/thmmy/mthmmy/activities/BaseActivity.java
  3. 60
      app/src/main/java/gr/thmmy/mthmmy/activities/LoginActivity.java
  4. 68
      app/src/main/java/gr/thmmy/mthmmy/activities/MainActivity.java
  5. 204
      app/src/main/java/gr/thmmy/mthmmy/activities/TopicActivity.java
  6. 11
      app/src/main/java/gr/thmmy/mthmmy/data/Post.java
  7. 11
      app/src/main/java/gr/thmmy/mthmmy/data/TopicSummary.java
  8. 8
      app/src/main/java/gr/thmmy/mthmmy/sections/recent/RecentAdapter.java
  9. 52
      app/src/main/java/gr/thmmy/mthmmy/sections/recent/RecentFragment.java
  10. 3
      app/src/main/java/gr/thmmy/mthmmy/utils/CircularNetworkImageView.java
  11. 4
      app/src/main/java/gr/thmmy/mthmmy/utils/CustomRecyclerView.java
  12. 249
      app/src/main/java/gr/thmmy/mthmmy/utils/Thmmy.java
  13. 15
      app/src/main/res/layout/activity_topic_post_row.xml
  14. 6
      app/src/main/res/layout/fragment_recent_row.xml
  15. 1
      app/src/main/res/values/dimens.xml
  16. 7
      app/src/main/res/values/strings.xml

4
app/build.gradle

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

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

@ -1,61 +1,281 @@
package gr.thmmy.mthmmy.activities; package gr.thmmy.mthmmy.activities;
import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import com.franmontiel.persistentcookiejar.PersistentCookieJar; import com.franmontiel.persistentcookiejar.PersistentCookieJar;
import com.franmontiel.persistentcookiejar.cache.SetCookieCache; import com.franmontiel.persistentcookiejar.cache.SetCookieCache;
import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor; 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.CookieJar;
import okhttp3.FormBody;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class BaseActivity extends AppCompatActivity { public class BaseActivity extends AppCompatActivity {
//Shared preferences //----------------------------------------CLASS VARIABLES-----------------------------------------
static final String SHARED_PREFS_NAME = "thmmySharedPrefs"; /* --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 USER_NAME = "userNameKey";
static final String GUEST_PREF_USERNAME = "GUEST"; 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 OkHttpClient client;
static Thmmy.LoginData loginData;
private static CookieJar cookieJar; private static CookieJar cookieJar;
private static SharedPrefsCookiePersistor sharedPrefsCookiePersistor; private static SharedPrefsCookiePersistor sharedPrefsCookiePersistor;
/* --Client Stuff End-- */
//Other variables
private static boolean init = false; //To initialize stuff only once per app start 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; return cookieJar;
} }
public static SharedPrefsCookiePersistor getSharedPrefsCookiePersistor() { private static SharedPrefsCookiePersistor getSharedPrefsCookiePersistor() {
return sharedPrefsCookiePersistor; return sharedPrefsCookiePersistor;
} }
public static OkHttpClient getClient() { public static OkHttpClient getClient() {
return client; return client;
} }
//-----------------------------------CLIENT AND COOKIES END---------------------------------------
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if(!init) if (!init) {
{ _prefs = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE);
sharedPrefsCookiePersistor = new SharedPrefsCookiePersistor(BaseActivity.this); sharedPrefsCookiePersistor = new SharedPrefsCookiePersistor(BaseActivity.this);
cookieJar = new PersistentCookieJar(new SetCookieCache(), sharedPrefsCookiePersistor); cookieJar = new PersistentCookieJar(new SetCookieCache(), sharedPrefsCookiePersistor);
client = new OkHttpClient.Builder() client = new OkHttpClient.Builder()
.cookieJar(cookieJar) .cookieJar(cookieJar)
.build(); .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----------------------------------------
} }

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

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

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

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

@ -39,16 +39,29 @@ import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
public class TopicActivity extends BaseActivity { public class TopicActivity extends BaseActivity {
private static final int THUMBNAIL_SIZE = 80;
private final SparseArray<String> pagesUrls = new SparseArray<>(); //-----------------------------------------CLASS VARIABLES------------------------------------------
private ImageLoader imageLoader = ImageController.getInstance().getImageLoader();
private ProgressBar progressBar; /* --Post-- */
private List<Post> postsList; private List<Post> postsList;
private EditText pageSelect;
private LinearLayout postsLinearLayout; private LinearLayout postsLinearLayout;
/* --Topic's page-- */
private int thisPage = 1; private int thisPage = 1;
private String base_url = ""; private String base_url = "";
private int numberOfPages = 1; 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 @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -58,6 +71,7 @@ public class TopicActivity extends BaseActivity {
Bundle extras = getIntent().getExtras(); Bundle extras = getIntent().getExtras();
final String topicTitle = getIntent().getExtras().getString("TOPIC_TITLE"); final String topicTitle = getIntent().getExtras().getString("TOPIC_TITLE");
//Variables initialization
postsLinearLayout = (LinearLayout) findViewById(R.id.posts_list); postsLinearLayout = (LinearLayout) findViewById(R.id.posts_list);
progressBar = (ProgressBar) findViewById(R.id.progressBar); progressBar = (ProgressBar) findViewById(R.id.progressBar);
@ -65,32 +79,39 @@ public class TopicActivity extends BaseActivity {
if (imageLoader == null) if (imageLoader == null)
imageLoader = ImageController.getInstance().getImageLoader(); imageLoader = ImageController.getInstance().getImageLoader();
postsList = new ArrayList<>();
ActionBar actionbar = getSupportActionBar(); ActionBar actionbar = getSupportActionBar();
if (actionbar != null) if (actionbar != null)
actionbar.setTitle(topicTitle); actionbar.setTitle(topicTitle);
/* Add page select field to the Action Bar */
//Edit Action Bar's parameters
ActionBar.LayoutParams lp = new ActionBar.LayoutParams( ActionBar.LayoutParams lp = new ActionBar.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT LinearLayout.LayoutParams.WRAP_CONTENT
, LinearLayout.LayoutParams.WRAP_CONTENT , LinearLayout.LayoutParams.WRAP_CONTENT
, Gravity.END | Gravity.CENTER_VERTICAL); , Gravity.END | Gravity.CENTER_VERTICAL);
//Initialize layout to be added
View customNav = LayoutInflater.from(this).inflate(R.layout.topic_page_select, null); View customNav = LayoutInflater.from(this).inflate(R.layout.topic_page_select, null);
//Initialize text field
pageSelect = (EditText) customNav.findViewById(R.id.select_page); pageSelect = (EditText) customNav.findViewById(R.id.select_page);
pageSelect.setOnEditorActionListener(new TextView.OnEditorActionListener() { pageSelect.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override @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) { if (actionId == EditorInfo.IME_ACTION_GO) {
//Get page requested by user
int pageRequested = Integer.parseInt(pageSelect.getText().toString()); int pageRequested = Integer.parseInt(pageSelect.getText().toString());
if (pageRequested == thisPage) { if (pageRequested == thisPage) {
Toast.makeText(getBaseContext() Toast.makeText(getBaseContext()
, "You already are here!", Toast.LENGTH_LONG).show(); , "You already are here!", Toast.LENGTH_LONG).show();
} else if (pageRequested >= 1 && pageRequested <= numberOfPages) { } else if (pageRequested >= 1 && pageRequested <= numberOfPages) {
//Restart activity with new page
Intent intent = getIntent(); Intent intent = getIntent();
intent.putExtra("TOPIC_URL", pagesUrls.get(pageRequested - 1)); intent.putExtra("TOPIC_URL", pagesUrls.get(pageRequested - 1));
intent.putExtra("TOPIC_TITLE", topicTitle); intent.putExtra("TOPIC_TITLE", topicTitle);
finish(); finish();
startActivity(intent); startActivity(intent);
} else { } else { //Invalid page request
Toast.makeText(getBaseContext() Toast.makeText(getBaseContext()
, "There is no such page!", Toast.LENGTH_LONG).show(); , "There is no such page!", Toast.LENGTH_LONG).show();
} }
@ -99,127 +120,95 @@ public class TopicActivity extends BaseActivity {
} }
}); });
assert actionbar != null; assert actionbar != null;
actionbar.setCustomView(customNav, lp); actionbar.setCustomView(customNav, lp); //Add feature to Action Bar
actionbar.setDisplayShowCustomEnabled(true); actionbar.setDisplayShowCustomEnabled(true);
postsList = new ArrayList<>(); new TopicTask().execute(extras.getString("TOPIC_URL")); //Attempt data parsing
new TopicTask().execute(extras.getString("TOPIC_URL"));
} }
@Override @Override
protected void onDestroy() { protected void onDestroy() { //When finished cancel whatever request can still be canceled
super.onDestroy(); super.onDestroy();
ImageController.getInstance().cancelPendingRequests(); ImageController.getInstance().cancelPendingRequests();
} }
//---------------------------------------TOPIC ASYNC TASK------------------------------------------- //---------------------------------------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> { public class TopicTask extends AsyncTask<String, Void, Boolean> {
private static final String TAG = "TopicTask"; //Class variables
private String pageLink; private static final String TAG = "TopicTask"; //Separate tag for AsyncTask
private Document document;
//Show a progress bar until done
protected void onPreExecute() { protected void onPreExecute() {
progressBar.setVisibility(ProgressBar.VISIBLE); progressBar.setVisibility(ProgressBar.VISIBLE);
} }
protected Boolean doInBackground(String... strings) { protected Boolean doInBackground(String... strings) {
base_url = strings[0].substring(0, strings[0].lastIndexOf(".")); Document document;
base_url = strings[0].substring(0, strings[0].lastIndexOf(".")); //This topic's base url
pageLink = strings[0]; String pageLink = strings[0]; //This page's url
Request request = new Request.Builder() Request request = new Request.Builder()
.url(pageLink) .url(pageLink)
.build(); .build();
try { try {
Response response = client.newCall(request).execute(); Response response = client.newCall(request).execute();
document = Jsoup.parse(response.body().string()); document = Jsoup.parse(response.body().string());
return parse(document); parse(document); //Parse data
return true;
} catch (SSLHandshakeException e) { } catch (SSLHandshakeException e) {
Log.w(TAG, "Certificate problem (please switch to unsafe connection)."); Log.w(TAG, "Certificate problem (please switch to unsafe connection).");
return false;
} catch (Exception e) { } catch (Exception e) {
Log.e("TAG", "ERROR", e); Log.e("TAG", "ERROR", e);
return false;
} }
return false;
} }
protected void onPostExecute(Boolean result) { protected void onPostExecute(Boolean result) {
progressBar.setVisibility(ProgressBar.INVISIBLE); if (!result) { //Parse failed!
populateLayout(); //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)); pageSelect.setHint(String.valueOf(thisPage) + "/" + String.valueOf(numberOfPages));
} }
private boolean parse(Document document) { /* Parse method */
{ private void parse(Document document) {
Elements findCurrentPage = document.select("td:contains(Pages:)>b"); { //Find current page's index
Elements findCurrentPage = document.select("td:contains(Pages:)>b"); //Contains pages
for (Element item : findCurrentPage) { for (Element item : findCurrentPage) {
if (!item.text().contains("...") if (!item.text().contains("...") //It's not "..."
&& !item.text().contains("Pages")) { && !item.text().contains("Pages")) { //Nor "Pages"
thisPage = Integer.parseInt(item.text()); thisPage = Integer.parseInt(item.text());
break; break;
} }
} }
} }
{ { //Find number of pages
Elements footer_pages = document.select("td:contains(Pages:)>a.navPages"); Elements pages = document.select("td:contains(Pages:)>a.navPages"); //Contains all pages
if (footer_pages.size() != 0) { if (pages.size() != 0) {
numberOfPages = thisPage; numberOfPages = thisPage; //Initialize the number
for (Element item : footer_pages) { for (Element item : pages) { //Just a max
if (Integer.parseInt(item.text()) > numberOfPages) if (Integer.parseInt(item.text()) > numberOfPages)
numberOfPages = Integer.parseInt(item.text()); numberOfPages = Integer.parseInt(item.text());
} }
} }
for (int i = 0; i < numberOfPages; i++) { 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)); 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)"); 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; String p_userName, p_thumbnailUrl, p_subject, p_post;
int p_postNum; int p_postNum;
@ -230,8 +219,7 @@ public class TopicActivity extends BaseActivity {
.select("td:has(div.smalltext:containsOwn(Guest))[style^=overflow]") .select("td:has(div.smalltext:containsOwn(Guest))[style^=overflow]")
.first().text(); .first().text();
p_userName = p_userName.substring(0, p_userName.indexOf(" Guest")); p_userName = p_userName.substring(0, p_userName.indexOf(" Guest"));
} } else
else
p_userName = userName.html(); p_userName = userName.html();
//Find thumbnail url //Find thumbnail url
@ -244,9 +232,9 @@ public class TopicActivity extends BaseActivity {
//Find subject //Find subject
p_subject = item.select("div[id^=subject_]").first().select("a").first().text(); p_subject = item.select("div[id^=subject_]").first().select("a").first().text();
//Find post's text //Find post's text
p_post = item.select("div").select(".post").first().html(); 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 = ("<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\" />"
+ p_post); //style.css + p_post); //style.css
@ -254,16 +242,68 @@ public class TopicActivity extends BaseActivity {
Element postNum = item.select("div.smalltext:matches(Reply #)").first(); Element postNum = item.select("div.smalltext:matches(Reply #)").first();
if (postNum == null) { //Topic starter if (postNum == null) { //Topic starter
p_postNum = 0; p_postNum = 0;
} } else {
else{
String tmp_str = postNum.text().substring(9); String tmp_str = postNum.text().substring(9);
p_postNum = Integer.parseInt(tmp_str.substring(0, tmp_str.indexOf(" on"))); 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 postsList.add(new Post(p_thumbnailUrl, p_userName, p_subject
, p_post, p_postNum)); , 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; package gr.thmmy.mthmmy.data;
public class Post public class Post {
{
private final String thumbnailUrl; private final String thumbnailUrl;
private final String author; private final String author;
private final String subject; private final String subject;
@ -16,7 +15,9 @@ public class Post
this.postNumber = postNumber; this.postNumber = postNumber;
} }
public String getThumbnailUrl() { return thumbnailUrl;} public String getThumbnailUrl() {
return thumbnailUrl;
}
public String getContent() { public String getContent() {
return content; return content;
@ -30,5 +31,7 @@ public class Post
return subject; 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; package gr.thmmy.mthmmy.data;
public class TopicSummary public class TopicSummary {
{
private final String topicUrl; private final String topicUrl;
private final String title; private final String title;
private final String lastUser; private final String lastUser;
private final String dateTimeModified; 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.topicUrl = topicUrl;
this.title = title; this.title = title;
this.lastUser = lastUser; this.lastUser = lastUser;
this.dateTimeModified = dateTimeModified; this.dateTimeModified = dateTimeModified;
} }
public String getTopicUrl() { return topicUrl; } public String getTopicUrl() {
return topicUrl;
}
public String getTitle() { public String getTitle() {
return title; 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.view.ViewGroup;
import android.widget.TextView; import android.widget.TextView;
import java.util.List;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.data.TopicSummary; 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 * {@link RecyclerView.Adapter} that can display a {@link TopicSummary} and makes a call to the
* specified {@link RecentFragment.OnListFragmentInteractionListener}. * 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 List<TopicSummary> recentList;
private final RecentFragment.OnListFragmentInteractionListener mListener; private final RecentFragment.OnListFragmentInteractionListener mListener;
@ -35,7 +34,6 @@ public class RecentAdapter extends RecyclerView.Adapter<RecentAdapter.ViewHolder
} }
@Override @Override
public void onBindViewHolder(final ViewHolder holder, final int position) { public void onBindViewHolder(final ViewHolder holder, final int position) {

52
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.RelativeLayout;
import android.widget.Toast; 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.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
@ -29,6 +24,10 @@ import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; 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.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
@ -41,8 +40,7 @@ import okhttp3.Response;
* Use the {@link RecentFragment#newInstance} factory method to * Use the {@link RecentFragment#newInstance} factory method to
* create an instance of this fragment. * create an instance of this fragment.
*/ */
public class RecentFragment extends Fragment public class RecentFragment extends Fragment {
{
private static final String TAG = "RecentFragment"; private static final String TAG = "RecentFragment";
// Fragment initialization parameters, e.g. ARG_SECTION_NUMBER // Fragment initialization parameters, e.g. ARG_SECTION_NUMBER
private static final String ARG_SECTION_NUMBER = "SectionNumber"; private static final String ARG_SECTION_NUMBER = "SectionNumber";
@ -59,7 +57,8 @@ public class RecentFragment extends Fragment
private OkHttpClient client; private OkHttpClient client;
// Required empty public constructor // Required empty public constructor
public RecentFragment() {} public RecentFragment() {
}
/** /**
* Use ONLY this factory method to create a new instance of * Use ONLY this factory method to create a new instance of
@ -68,8 +67,7 @@ public class RecentFragment extends Fragment
* @param sectionNumber * @param sectionNumber
* @return A new instance of fragment Recent. * @return A new instance of fragment Recent.
*/ */
public static RecentFragment newInstance(int sectionNumber) public static RecentFragment newInstance(int sectionNumber) {
{
RecentFragment fragment = new RecentFragment(); RecentFragment fragment = new RecentFragment();
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putInt(ARG_SECTION_NUMBER, sectionNumber); args.putInt(ARG_SECTION_NUMBER, sectionNumber);
@ -82,8 +80,7 @@ public class RecentFragment extends Fragment
} }
@Override @Override
public void onCreate(Bundle savedInstanceState) public void onCreate(Bundle savedInstanceState) {
{
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
sectionNumber = getArguments().getInt(ARG_SECTION_NUMBER); sectionNumber = getArguments().getInt(ARG_SECTION_NUMBER);
@ -98,8 +95,7 @@ public class RecentFragment extends Fragment
} }
@Override @Override
public void onActivityCreated(Bundle savedInstanceState) public void onActivityCreated(Bundle savedInstanceState) {
{
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
if (sectionNumber == 1)//temp if (sectionNumber == 1)//temp
{ {
@ -142,14 +138,12 @@ public class RecentFragment extends Fragment
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) Bundle savedInstanceState) {
{
// Inflate the layout for this fragment // Inflate the layout for this fragment
final View rootView = inflater.inflate(R.layout.fragment_recent, container, false); final View rootView = inflater.inflate(R.layout.fragment_recent, container, false);
// Set the adapter // Set the adapter
if (rootView instanceof RelativeLayout) if (rootView instanceof RelativeLayout) {
{
progressBar = (ProgressBar) rootView.findViewById(R.id.progressBar); progressBar = (ProgressBar) rootView.findViewById(R.id.progressBar);
recentAdapter = new RecentAdapter(topicSummaries, mListener); recentAdapter = new RecentAdapter(topicSummaries, mListener);
@ -206,8 +200,7 @@ public class RecentFragment extends Fragment
//---------------------------------------ASYNC TASK----------------------------------- //---------------------------------------ASYNC TASK-----------------------------------
public class RecentTask extends AsyncTask<Void, Void, Integer> public class RecentTask extends AsyncTask<Void, Void, Integer> {
{
private static final String TAG = "RecentTask"; private static final String TAG = "RecentTask";
private final String thmmyUrl = "https://www.thmmy.gr/smf/index.php"; private final String thmmyUrl = "https://www.thmmy.gr/smf/index.php";
@ -219,8 +212,7 @@ public class RecentFragment extends Fragment
progressBar.setVisibility(ProgressBar.VISIBLE); progressBar.setVisibility(ProgressBar.VISIBLE);
} }
protected Integer doInBackground(Void... voids) protected Integer doInBackground(Void... voids) {
{
Request request = new Request.Builder() Request request = new Request.Builder()
.url(thmmyUrl) .url(thmmyUrl)
@ -241,9 +233,7 @@ public class RecentFragment extends Fragment
} }
protected void onPostExecute(Integer result) {
protected void onPostExecute(Integer result)
{
if (result == 0) if (result == 0)
recentAdapter.notifyDataSetChanged(); recentAdapter.notifyDataSetChanged();
@ -254,11 +244,9 @@ public class RecentFragment extends Fragment
swipeRefreshLayout.setRefreshing(false); swipeRefreshLayout.setRefreshing(false);
} }
private void parse(Document document) private void parse(Document document) {
{
Elements recent = document.select("#block8 :first-child div"); Elements recent = document.select("#block8 :first-child div");
if(recent.size()==30) if (recent.size() == 30) {
{
topicSummaries.clear(); topicSummaries.clear();
for (int i = 0; i < recent.size(); i += 3) { for (int i = 0; i < recent.size(); i += 3) {
@ -270,8 +258,7 @@ public class RecentFragment extends Fragment
Matcher matcher = pattern.matcher(lastUser); Matcher matcher = pattern.matcher(lastUser);
if (matcher.find()) if (matcher.find())
lastUser = matcher.group(1); lastUser = matcher.group(1);
else else {
{
Log.e(TAG, "Parsing failed (lastUser)!"); Log.e(TAG, "Parsing failed (lastUser)!");
return; return;
} }
@ -281,8 +268,7 @@ public class RecentFragment extends Fragment
matcher = pattern.matcher(dateTime); matcher = pattern.matcher(dateTime);
if (matcher.find()) if (matcher.find())
dateTime = matcher.group(1); dateTime = matcher.group(1);
else else {
{
Log.e(TAG, "Parsing failed (dateTime)!"); Log.e(TAG, "Parsing failed (dateTime)!");
return; return;
} }

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

@ -44,9 +44,6 @@ public class CircularNetworkImageView extends NetworkImageView {
/** /**
* Creates a circular bitmap and uses whichever dimension is smaller to determine the width * 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 * <br/>Also constrains the circle to the leftmost part of the image
*
* @param bitmap
* @return bitmap
*/ */
private Bitmap getCircularBitmap(Bitmap bitmap) { private Bitmap getCircularBitmap(Bitmap bitmap) {
bitmap = Bitmap.createScaledBitmap(bitmap, THUMBNAIL_SIZE, THUMBNAIL_SIZE, false); bitmap = Bitmap.createScaledBitmap(bitmap, THUMBNAIL_SIZE, THUMBNAIL_SIZE, false);

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

@ -31,8 +31,7 @@ public class CustomRecyclerView extends RecyclerView {
@Override @Override
public void onScrollStateChanged(int state) public void onScrollStateChanged(int state) {
{
if ((state != SCROLL_STATE_DRAGGING) && ((LinearLayoutManager) getLayoutManager()).findFirstCompletelyVisibleItemPosition() == 0) if ((state != SCROLL_STATE_DRAGGING) && ((LinearLayoutManager) getLayoutManager()).findFirstCompletelyVisibleItemPosition() == 0)
enableRefreshing = true; enableRefreshing = true;
else if (getChildCount() == 0) else if (getChildCount() == 0)
@ -41,7 +40,6 @@ public class CustomRecyclerView extends RecyclerView {
enableRefreshing = false; enableRefreshing = false;
super.onScrollStateChanged(state); super.onScrollStateChanged(state);
} }

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

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

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

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

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

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

@ -2,7 +2,6 @@
<!-- Default screen margins, per the Android Design guidelines. --> <!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen> <dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_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="appbar_padding_top">8dp</dimen>
<dimen name="thumbnail_size">80dp</dimen> <dimen name="thumbnail_size">80dp</dimen>
</resources> </resources>

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

@ -1,7 +1,5 @@
<resources> <resources>
<string name="app_name">mTHMMY</string> <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="thmmy_img_description">thmmy.gr</string>
<string name="hint_username">Username</string> <string name="hint_username">Username</string>
@ -12,7 +10,10 @@
<string name="about">About</string> <string name="about">About</string>
<string name="version">v%1$s</string> <string name="version">v%1$s</string>
<string name="logo">logo</string> <string name="logo">logo</string>
<string name="profile_name">Name</string>
<string name="login">Login</string> <string name="login">Login</string>
<string name="logout">Logout</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> </resources>

Loading…
Cancel
Save