Browse Source

More code cleanup and (javadoc style) commenting, FileManager begin

pull/24/head
Apostolos Fanakis 8 years ago
parent
commit
be5e8cc868
  1. 6
      app/src/main/java/gr/thmmy/mthmmy/activities/main/MainActivity.java
  2. 26
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileActivity.java
  3. 291
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java
  4. 48
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java
  5. 43
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicParser.java
  6. 10
      app/src/main/java/gr/thmmy/mthmmy/data/Post.java
  7. 183
      app/src/main/java/gr/thmmy/mthmmy/utils/FileManager/ThmmyFile.java

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

@ -18,6 +18,8 @@ import gr.thmmy.mthmmy.activities.main.recent.RecentFragment;
import gr.thmmy.mthmmy.activities.topic.TopicActivity;
import gr.thmmy.mthmmy.data.TopicSummary;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.EXTRAS_TOPIC_TITLE;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.EXTRAS_TOPIC_URL;
import static gr.thmmy.mthmmy.session.SessionManager.LOGGED_OUT;
public class MainActivity extends BaseActivity implements RecentFragment.RecentFragmentInteractionListener, ForumFragment.ForumFragmentInteractionListener {
@ -83,8 +85,8 @@ public class MainActivity extends BaseActivity implements RecentFragment.RecentF
@Override
public void onFragmentInteraction(TopicSummary topicSummary) {
Intent i = new Intent(MainActivity.this, TopicActivity.class);
i.putExtra("TOPIC_URL", topicSummary.getTopicUrl());
i.putExtra("TOPIC_TITLE", topicSummary.getTitle());
i.putExtra(EXTRAS_TOPIC_URL, topicSummary.getTopicUrl());
i.putExtra(EXTRAS_TOPIC_TITLE, topicSummary.getTitle());
startActivity(i);
}

26
app/src/main/java/gr/thmmy/mthmmy/activities/profile/ProfileActivity.java

@ -72,6 +72,7 @@ public class ProfileActivity extends BaseActivity {
* {@link ArrayList} of Strings used to hold profile's information. Data are added in {@link ProfileTask}.
*/
private ArrayList<String> parsedProfileData;
private ProfileTask profileTask;
private static final int THUMBNAIL_SIZE = 200;
@Override
@ -82,7 +83,7 @@ public class ProfileActivity extends BaseActivity {
PACKAGE_NAME = getApplicationContext().getPackageName();
Bundle extras = getIntent().getExtras();
//Initialize graphic elements
//Initializes graphic elements
toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitle(null);
setSupportActionBar(toolbar);
@ -90,7 +91,9 @@ public class ProfileActivity extends BaseActivity {
getSupportActionBar().setDisplayShowTitleEnabled(false);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
createDrawer();
progressBar = (ProgressBar) findViewById(R.id.progressBar);
userThumbnail = (ImageView) findViewById(R.id.user_thumbnail);
@ -100,24 +103,19 @@ public class ProfileActivity extends BaseActivity {
replyFAB = (FloatingActionButton) findViewById(R.id.profile_fab);
replyFAB.setEnabled(false);
replyFAB.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SharedPreferences sharedPrefs = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE);
int tmp_curr_status = sharedPrefs.getInt(LOGIN_STATUS, -1);
if (tmp_curr_status == -1) {
Report.e(TAG, "Error while getting LOGIN_STATUS");
new AlertDialog.Builder(ProfileActivity.this)
.setTitle("ERROR!")
.setMessage("An error occurred while trying to find your LOGIN_STATUS.\n" +
"Please sent below info to developers:\n"
+ getLocalClassName() + "." + "l"
+ Thread.currentThread().getStackTrace()[1].getLineNumber())
.setMessage("An error occurred while trying to find your LOGIN_STATUS.")
.setNeutralButton("Dismiss", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
//Todo
//Maybe sent info back to developers?
}
})
.show();
@ -146,7 +144,17 @@ public class ProfileActivity extends BaseActivity {
}
});
new ProfileTask().execute(extras.getString(EXTRAS_PROFILE_URL)); //Attempt data parsing
//Gets info
parsedProfileData = new ArrayList<>();
profileTask = new ProfileTask();
profileTask.execute(extras.getString(EXTRAS_PROFILE_URL)); //Attempt data parsing
}
@Override
protected void onDestroy() {
super.onDestroy();
if (profileTask != null && profileTask.getStatus() != AsyncTask.Status.RUNNING)
profileTask.cancel(true);
}
/**

291
app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicActivity.java

@ -1,25 +1,19 @@
package gr.thmmy.mthmmy.activities.topic;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.View;
import android.webkit.MimeTypeMap;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.TextView;
@ -28,8 +22,6 @@ import android.widget.Toast;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@ -40,34 +32,47 @@ import gr.thmmy.mthmmy.activities.LoginActivity;
import gr.thmmy.mthmmy.activities.base.BaseActivity;
import gr.thmmy.mthmmy.data.Post;
import mthmmy.utils.Report;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Request;
import okhttp3.Response;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static gr.thmmy.mthmmy.session.SessionManager.LOGGED_IN;
import static gr.thmmy.mthmmy.session.SessionManager.LOGIN_STATUS;
/**
* Activity for topics. When creating an Intent of this activity you need to bundle a <b>String</b>
* containing this topics's url using the key {@link #EXTRAS_TOPIC_URL} and a <b>String</b> containing
* this topic's title using the key {@link #EXTRAS_TOPIC_TITLE}.
*/
@SuppressWarnings("unchecked")
public class TopicActivity extends BaseActivity {
//-----------------------------------------CLASS VARIABLES------------------------------------------
//Class variables
/**
* Debug Tag for logging debug output to LogCat
*/
@SuppressWarnings("unused")
private static final String TAG = "TopicActivity";
/**
* The key to use when putting topic's url String to {@link TopicActivity}'s Bundle.
*/
public static final String EXTRAS_TOPIC_URL = "TOPIC_URL";
/**
* The key to use when putting topic's title String to {@link TopicActivity}'s Bundle.
*/
public static final String EXTRAS_TOPIC_TITLE = "TOPIC_TITLE";
static String PACKAGE_NAME;
private TopicTask topicTask;
/* --Posts-- */
//About posts
private List<Post> postsList;
static final int NO_POST_FOCUS = -1;
static int postFocus = NO_POST_FOCUS;
//Quote
//Quotes
public static final ArrayList<Integer> toQuoteList = new ArrayList<>();
/* --Topic's pages-- */
//Topic's pages
private int thisPage = 1;
public static String base_url = "";
private int numberOfPages = 1;
private final SparseArray<String> pagesUrls = new SparseArray<>();
//Page select
private TextView pageIndicator;
private final Handler repeatUpdateHandler = new Handler();
private final long INITIAL_DELAY = 500;
private boolean autoIncrement = false;
@ -75,22 +80,20 @@ public class TopicActivity extends BaseActivity {
private static final int SMALL_STEP = 1;
private static final int LARGE_STEP = 10;
private Integer pageRequestValue;
//Bottom navigation graphics
private ImageButton firstPage;
private ImageButton previousPage;
private TextView pageIndicator;
private ImageButton nextPage;
private ImageButton lastPage;
//Other variables
private ProgressBar progressBar;
@SuppressWarnings("unused")
private static final String TAG = "TopicActivity";
private String topicTitle;
private FloatingActionButton replyFAB;
private String parsedTitle;
private RecyclerView recyclerView;
private RecyclerView.LayoutManager layoutManager;
static String PACKAGE_NAME;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -98,11 +101,10 @@ public class TopicActivity extends BaseActivity {
setContentView(R.layout.activity_topic);
PACKAGE_NAME = getApplicationContext().getPackageName();
Bundle extras = getIntent().getExtras();
topicTitle = getIntent().getExtras().getString("TOPIC_TITLE");
topicTitle = extras.getString("TOPIC_TITLE");
//Initialize toolbar, drawer and ProgressBar
//Initializes graphics
toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitle(topicTitle);
setSupportActionBar(toolbar);
@ -117,42 +119,27 @@ public class TopicActivity extends BaseActivity {
postsList = new ArrayList<>();
firstPage = (ImageButton) findViewById(R.id.page_first_button);
previousPage = (ImageButton) findViewById(R.id.page_previous_button);
pageIndicator = (TextView) findViewById(R.id.page_indicator);
nextPage = (ImageButton) findViewById(R.id.page_next_button);
lastPage = (ImageButton) findViewById(R.id.page_last_button);
initDecrementButton(firstPage, LARGE_STEP);
initDecrementButton(previousPage, SMALL_STEP);
initIncrementButton(nextPage, SMALL_STEP);
initIncrementButton(lastPage, LARGE_STEP);
firstPage.setEnabled(false);
previousPage.setEnabled(false);
nextPage.setEnabled(false);
lastPage.setEnabled(false);
recyclerView = (RecyclerView) findViewById(R.id.topic_recycler_view);
recyclerView.setHasFixedSize(true);
layoutManager = new LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(new TopicAdapter(getApplicationContext(), postsList));
replyFAB = (FloatingActionButton) findViewById(R.id.topic_fab);
replyFAB.setEnabled(false);
replyFAB.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SharedPreferences sharedPrefs = getSharedPreferences(SHARED_PREFS_NAME, MODE_PRIVATE);
int tmp_curr_status = sharedPrefs.getInt(LOGIN_STATUS, -1);
if (tmp_curr_status == -1) {
Report.e(TAG, "Error while getting LOGIN_STATUS");
new AlertDialog.Builder(TopicActivity.this)
.setTitle("ERROR!")
.setMessage("An error occurred while trying to find your LOGIN_STATUS.\n" +
"Please sent below info to developers:\n"
+ getLocalClassName() + "." + "l"
+ Thread.currentThread().getStackTrace()[1].getLineNumber())
.setMessage("An error occurred while trying to find your LOGIN_STATUS.")
.setNeutralButton("Dismiss", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
//Todo
//Maybe sent info back to developers?
}
})
.show();
@ -181,14 +168,26 @@ public class TopicActivity extends BaseActivity {
}
});
recyclerView = (RecyclerView) findViewById(R.id.topic_recycler_view);
recyclerView.setHasFixedSize(true);
layoutManager = new LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(new TopicAdapter(getApplicationContext(), postsList));
//Sets bottom navigation bar
firstPage = (ImageButton) findViewById(R.id.page_first_button);
previousPage = (ImageButton) findViewById(R.id.page_previous_button);
pageIndicator = (TextView) findViewById(R.id.page_indicator);
nextPage = (ImageButton) findViewById(R.id.page_next_button);
lastPage = (ImageButton) findViewById(R.id.page_last_button);
initDecrementButton(firstPage, LARGE_STEP);
initDecrementButton(previousPage, SMALL_STEP);
initIncrementButton(nextPage, SMALL_STEP);
initIncrementButton(lastPage, LARGE_STEP);
firstPage.setEnabled(false);
previousPage.setEnabled(false);
nextPage.setEnabled(false);
lastPage.setEnabled(false);
//Gets posts
topicTask = new TopicTask();
topicTask.execute(extras.getString("TOPIC_URL")); //Attempt data parsing
topicTask.execute(extras.getString(EXTRAS_TOPIC_URL)); //Attempt data parsing
}
@Override
@ -260,8 +259,8 @@ public class TopicActivity extends BaseActivity {
changePage(0);
return;
}
//Clicked and holden
autoDecrement = false; //Stop incrementing
//Clicked and hold
autoDecrement = false; //Stop decrementing
decrementPageRequestValue(step);
changePage(pageRequestValue - 1);
}
@ -296,10 +295,6 @@ public class TopicActivity extends BaseActivity {
} else
pageRequestValue = numberOfPages;
pageIndicator.setText(pageRequestValue + "/" + String.valueOf(numberOfPages));
if (pageRequestValue >= 1000)
pageIndicator.setTextSize(16);
else
pageIndicator.setTextSize(20);
}
private void decrementPageRequestValue(int step) {
@ -308,10 +303,6 @@ public class TopicActivity extends BaseActivity {
else
pageRequestValue = 1;
pageIndicator.setText(pageRequestValue + "/" + String.valueOf(numberOfPages));
if (numberOfPages >= 1000)
pageIndicator.setTextSize(16);
else
pageIndicator.setTextSize(20);
}
private void changePage(int pageRequested) {
@ -326,15 +317,24 @@ public class TopicActivity extends BaseActivity {
}
//------------------------------------BOTTOM NAV BAR METHODS END------------------------------------
//---------------------------------------TOPIC ASYNC TASK-------------------------------------------
/**
* An {@link AsyncTask} that handles asynchronous fetching of a topic page and parsing it's
* data. {@link AsyncTask#onPostExecute(Object) OnPostExecute} method calls {@link RecyclerView#swapAdapter}
* to build graphics.
* <p>
* <p>Calling ProfileTask's {@link AsyncTask#execute execute} method needs to have profile's url
* as String parameter!</p>
*/
public class TopicTask extends AsyncTask<String, Void, Integer> {
//Class variables
/**
* Debug Tag for logging debug output to LogCat
*/
private static final String TAG = "TopicTask"; //Separate tag for AsyncTask
private static final int SUCCESS = 0;
private static final int NETWORK_ERROR = 1;
private static final int OTHER_ERROR = 2;
//Show a progress bar until done
protected void onPreExecute() {
progressBar.setVisibility(ProgressBar.VISIBLE);
replyFAB.setEnabled(false);
@ -343,9 +343,9 @@ public class TopicActivity extends BaseActivity {
protected Integer doInBackground(String... strings) {
Document document;
base_url = strings[0].substring(0, strings[0].lastIndexOf(".")); //This topic's base url
String pageUrl = strings[0]; //This page's url
String pageUrl = strings[0];
//Find message focus if present
//Finds message focus if present
{
postFocus = NO_POST_FOCUS;
if (pageUrl.contains("msg")) {
@ -363,7 +363,7 @@ public class TopicActivity extends BaseActivity {
try {
Response response = client.newCall(request).execute();
document = Jsoup.parse(response.body().string());
parse(document); //Parse data
parse(document);
return SUCCESS;
} catch (IOException e) {
Report.i(TAG, "IO Exception", e);
@ -374,18 +374,26 @@ public class TopicActivity extends BaseActivity {
}
}
protected void onPostExecute(Integer result) {
switch (result) {
protected void onPostExecute(Integer parseResult) {
switch (parseResult) {
case SUCCESS:
//Parse was successful
progressBar.setVisibility(ProgressBar.INVISIBLE); //Hide progress bar
populateLayout(); //Show parsed data
progressBar.setVisibility(ProgressBar.INVISIBLE);
recyclerView.swapAdapter(new TopicAdapter(getApplicationContext(), postsList), false);
//Set post focus
if (postFocus != NO_POST_FOCUS) {
for (int i = postsList.size() - 1; i >= 0; --i) {
int currentPostIndex = postsList.get(i).getPostIndex();
if (currentPostIndex == postFocus) {
layoutManager.scrollToPosition(i);
}
}
}
replyFAB.setEnabled(true);
//Set current page
pageIndicator.setText(String.valueOf(thisPage) + "/" + String.valueOf(numberOfPages));
pageRequestValue = thisPage;
if (numberOfPages >= 1000)
pageIndicator.setTextSize(16);
firstPage.setEnabled(true);
previousPage.setEnabled(true);
@ -406,13 +414,18 @@ public class TopicActivity extends BaseActivity {
}
}
/* Parse method */
private void parse(Document document) {
String language = TopicParser.defineLanguage(document);
/**
* All the parsing a topic needs.
*
* @param topic {@link Document} object containing this topic's source code
* @see org.jsoup.Jsoup Jsoup
*/
private void parse(Document topic) {
String language = TopicParser.defineLanguage(topic);
//Find topic title if missing
//Finds topic title if missing
if (topicTitle == null || Objects.equals(topicTitle, "")) {
parsedTitle = document.select("td[id=top_subject]").first().text();
parsedTitle = topic.select("td[id=top_subject]").first().text();
if (parsedTitle.contains("Topic:")) {
parsedTitle = parsedTitle.substring(parsedTitle.indexOf("Topic:") + 7
, parsedTitle.indexOf("(Read") - 2);
@ -423,11 +436,11 @@ public class TopicActivity extends BaseActivity {
}
}
{ //Find current page's index
thisPage = TopicParser.parseCurrentPageIndex(document, language);
{ //Finds current page's index
thisPage = TopicParser.parseCurrentPageIndex(topic, language);
}
{ //Find number of pages
numberOfPages = TopicParser.parseTopicNumberOfPages(document, thisPage, language);
{ //Finds number of pages
numberOfPages = TopicParser.parseTopicNumberOfPages(topic, thisPage, language);
for (int i = 0; i < numberOfPages; i++) {
//Generate each page's url from topic's base url +".15*numberOfPage"
@ -435,44 +448,20 @@ public class TopicActivity extends BaseActivity {
}
}
postsList = TopicParser.parseTopic(document, language);
}
/* Parse method end */
}
//-------------------------------------TOPIC ASYNC TASK END-----------------------------------------
//----------------------------------------POPULATE UI METHOD----------------------------------------
/**
* This method runs on the main thread. It reads from the postsList and dynamically
* adds a card for each post to the ScrollView.
*/
private void populateLayout() {
recyclerView.swapAdapter(new TopicAdapter(getApplicationContext(), postsList), false);
//Set post focus
if (postFocus != NO_POST_FOCUS) {
for (int i = postsList.size() - 1; i >= 0; --i) {
int currentPostIndex = postsList.get(i).getPostIndex();
if (currentPostIndex == postFocus) {
layoutManager.scrollToPosition(i);
}
postsList = TopicParser.parseTopic(topic, language);
}
}
replyFAB.setEnabled(true);
}
//--------------------------------------POPULATE UI METHOD END--------------------------------------
//----------------------------------------REPETITIVE UPDATER----------------------------------------
/**
* This class is used to implement the repetitive incrementPageRequestValue/decrementPageRequestValue of page value
* when long pressing one of the page navigation buttons.
* This class is used to implement the repetitive incrementPageRequestValue/decrementPageRequestValue
* of page value when long pressing one of the page navigation buttons.
*/
class RepetitiveUpdater implements Runnable {
private final int step;
/**
* @param step number of pages to add/subtract on each repetition
*/
RepetitiveUpdater(int step) {
this.step = step;
}
@ -488,76 +477,4 @@ public class TopicActivity extends BaseActivity {
}
}
}
//--------------------------------------REPETITIVE UPDATER END--------------------------------------
//------------------------------METHODS FOR DOWNLOADING ATTACHED FILES------------------------------
/**
* Create a File
*/
static void downloadFileAsync(final String downloadUrl, final String fileName, final Context context) {
Request request = new Request.Builder().url(downloadUrl).build();
getClient().newCall(request).enqueue(new Callback() {
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful()) {
throw new IOException("Failed to download file: " + response);
}
File tmpFile = getOutputMediaFile(PACKAGE_NAME, fileName);
if (tmpFile == null) {
Report.d(TAG
, "Error creating media file, check storage permissions!");
} else {
FileOutputStream fos = new FileOutputStream(tmpFile);
fos.write(response.body().bytes());
fos.close();
String filePath = tmpFile.getAbsolutePath();
String extension = MimeTypeMap.getFileExtensionFromUrl(
filePath.substring(filePath.lastIndexOf("/")));
String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(tmpFile), mime);
intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}
});
}
/**
* Create a File
*/
@Nullable
private static File getOutputMediaFile(String packageName, String fileName) {
// To be safe, you should check that the SDCard is mounted
// using Environment.getExternalStorageState() before doing this.
File mediaStorageDir = new File(Environment.getExternalStorageDirectory()
+ "/Android/data/"
+ packageName
+ "/Downloads");
// This location works best if you want the created files to be shared
// between applications and persist after your app has been uninstalled.
// Create the storage directory if it does not exist
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
Log.d(TAG, "problem!");
return null;
}
}
// Create a media file name
File mediaFile;
mediaFile = new File(mediaStorageDir.getPath() + File.separator + fileName);
return mediaFile;
}
//----------------------------METHODS FOR DOWNLOADING ATTACHED FILES END----------------------------
}

48
app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicAdapter.java

@ -7,6 +7,7 @@ import android.content.Intent;
import android.graphics.Color;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@ -19,6 +20,7 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.MimeTypeMap;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
@ -31,6 +33,8 @@ import android.widget.TextView;
import com.squareup.picasso.Picasso;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@ -39,13 +43,14 @@ import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.activities.profile.ProfileActivity;
import gr.thmmy.mthmmy.data.Post;
import gr.thmmy.mthmmy.utils.CircleTransform;
import gr.thmmy.mthmmy.utils.FileManager.ThmmyFile;
import mthmmy.utils.Report;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static gr.thmmy.mthmmy.activities.profile.ProfileActivity.EXTRAS_PROFILE_URL;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.NO_POST_FOCUS;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.PACKAGE_NAME;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.base_url;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.downloadFileAsync;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.postFocus;
import static gr.thmmy.mthmmy.activities.topic.TopicActivity.toQuoteList;
@ -197,30 +202,28 @@ class TopicAdapter extends RecyclerView.Adapter<TopicAdapter.MyViewHolder> {
if (currentPost.getAttachedFiles().size() != 0) {
holder.bodyFooterDivider.setVisibility(View.VISIBLE);
int filesTextColor = 0;
int filesTextColor;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
filesTextColor = context.getResources().getColor(R.color.accent, null);
} else //noinspection deprecation
filesTextColor = context.getResources().getColor(R.color.accent);
for (final String[] attachedFile : currentPost.getAttachedFiles()) {
for (final ThmmyFile attachedFile : currentPost.getAttachedFiles()) {
final TextView attached = new TextView(context);
attached.setTextSize(10f);
attached.setClickable(true);
attached.setTypeface(Typeface.createFromAsset(context.getAssets()
, "fonts/fontawesome-webfont.ttf"));
attached.setText(faIconFromExtension(attachedFile[1]) + " " + attachedFile[1] + attachedFile[2]);
attached.setText(faIconFromExtension(attachedFile.getFilename()) + " "
+ attachedFile.getFilename() + attachedFile.getFileInfo());
attached.setTextColor(filesTextColor);
attached.setPadding(0, 3, 0, 3);
attached.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
try {
downloadFileAsync(attachedFile[0], attachedFile[1], context);
} catch (Exception e) {
e.printStackTrace();
}
DownloadTask downloadTask = new DownloadTask();
downloadTask.execute(attachedFile);
}
});
@ -578,4 +581,31 @@ class TopicAdapter extends RecyclerView.Adapter<TopicAdapter.MyViewHolder> {
return context.getResources().getString(R.string.fa_file);
}
private class DownloadTask extends AsyncTask<ThmmyFile, Void, Void> {
//Class variables
/**
* Debug Tag for logging debug output to LogCat
*/
private static final String TAG = "DownloadTask"; //Separate tag for AsyncTask
protected Void doInBackground(ThmmyFile... files) {
try {
File tmpFile = files[0].download(PACKAGE_NAME);
if (tmpFile != null) {
String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
files[0].getExtension());
Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(tmpFile), mime);
intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
} catch (IOException e) {
Report.e(TAG, "Error while trying to download a file", e);
}
return null;
}
}
}

43
app/src/main/java/gr/thmmy/mthmmy/activities/topic/TopicParser.java

@ -7,12 +7,16 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import gr.thmmy.mthmmy.data.Post;
import gr.thmmy.mthmmy.utils.FileManager.ThmmyFile;
import mthmmy.utils.Report;
/**
* Singleton used for parsing a topic.
@ -20,8 +24,7 @@ import gr.thmmy.mthmmy.data.Post;
* <li>{@link #parseCurrentPageIndex(Document, String)}</li>
* <li>{@link #parseTopicNumberOfPages(Document, int, String)}</li>
* <li>{@link #parseTopic(Document, String)}</li>
* <li>{@link #defineLanguage(Document)}</li>
* <li>(private) {@link #colorPicker(String)}</li></ul></p>
* <li>{@link #defineLanguage(Document)}</li></ul></p>
*/
class TopicParser {
//Languages supported
@ -165,7 +168,7 @@ class TopicParser {
p_specialRank, p_gender, p_personalText, p_numberOfPosts;
int p_postNum, p_postIndex, p_numberOfStars, p_userColor;
boolean p_isDeleted = false;
ArrayList<String[]> p_attachedFiles;
ArrayList<ThmmyFile> p_attachedFiles;
//Initialize variables
p_profileURL = null;
@ -274,21 +277,26 @@ class TopicParser {
String postAttachmentsText = postAttachments.text();
for (int i = 0; i < attachedFiles.size(); ++i) {
String[] attachedArray = new String[3];
URL attachedUrl;
//Gets file's url and filename
Element tmpAttachedFileUrlAndName = attachedFiles.get(i);
attachedArray[0] = tmpAttachedFileUrlAndName.attr("href");
attachedArray[1] = tmpAttachedFileUrlAndName.text().substring(1);
try {
attachedUrl = new URL(tmpAttachedFileUrlAndName.attr("href"));
} catch (MalformedURLException e) {
Report.e(TAG,"Attached file malformed url", e);
break;
}
String attachedFileName = tmpAttachedFileUrlAndName.text().substring(1);
//Gets file's info (size and download count)
String postAttachmentsTextSbstr = postAttachmentsText.substring(
postAttachmentsText.indexOf(attachedArray[1]));
postAttachmentsText.indexOf(attachedFileName));
attachedArray[2] = postAttachmentsTextSbstr.substring(attachedArray[1]
String attachedFileInfo = postAttachmentsTextSbstr.substring(attachedFileName
.length(), postAttachmentsTextSbstr.indexOf("φορές.")) + "φορές.)";
p_attachedFiles.add(attachedArray);
p_attachedFiles.add(new ThmmyFile(attachedUrl,attachedFileName,attachedFileInfo));
}
}
} else {
@ -329,21 +337,26 @@ class TopicParser {
String postAttachmentsText = postAttachments.text();
for (int i = 0; i < attachedFiles.size(); ++i) {
String[] attachedArray = new String[3];
URL attachedUrl;
//Gets file's url and filename
Element tmpAttachedFileUrlAndName = attachedFiles.get(i);
attachedArray[0] = tmpAttachedFileUrlAndName.attr("href");
attachedArray[1] = tmpAttachedFileUrlAndName.text().substring(1);
try {
attachedUrl = new URL(tmpAttachedFileUrlAndName.attr("href"));
} catch (MalformedURLException e) {
Report.e(TAG,"Attached file malformed url", e);
break;
}
String attachedFileName = tmpAttachedFileUrlAndName.text().substring(1);
//Gets file's info (size and download count)
String postAttachmentsTextSbstr = postAttachmentsText.substring(
postAttachmentsText.indexOf(attachedArray[1]));
postAttachmentsText.indexOf(attachedFileName));
attachedArray[2] = postAttachmentsTextSbstr.substring(attachedArray[1]
String attachedFileInfo = postAttachmentsTextSbstr.substring(attachedFileName
.length(), postAttachmentsTextSbstr.indexOf("times.")) + "times.)";
p_attachedFiles.add(attachedArray);
p_attachedFiles.add(new ThmmyFile(attachedUrl,attachedFileName,attachedFileInfo));
}
}
}

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

@ -2,6 +2,8 @@ package gr.thmmy.mthmmy.data;
import java.util.ArrayList;
import gr.thmmy.mthmmy.utils.FileManager.ThmmyFile;
public class Post {
//Standard info (exists in every post)
private final String thumbnailUrl;
@ -22,13 +24,13 @@ public class Post {
private final String personalText;
private final int numberOfStars;
private final int userColor;
private final ArrayList<String[]> attachedFiles;
private final ArrayList<ThmmyFile> attachedFiles;
public Post(String thumbnailUrl, String author, String subject, String content
, int postIndex, int postNumber, String postDate, String profileURl, String rank
, String special_rank, String gender, String numberOfPosts
, String personalText, int numberOfStars, int userColor
, ArrayList<String[]> attachedFiles) {
, ArrayList<ThmmyFile> attachedFiles) {
this.thumbnailUrl = thumbnailUrl;
this.author = author;
this.subject = subject;
@ -50,7 +52,7 @@ public class Post {
public Post(String thumbnailUrl, String author, String subject, String content
, int postIndex, int postNumber, String postDate, int userColor
, ArrayList<String[]> attachedFiles) {
, ArrayList<ThmmyFile> attachedFiles) {
this.thumbnailUrl = thumbnailUrl;
this.author = author;
this.subject = subject;
@ -135,7 +137,7 @@ public class Post {
return userColor;
}
public ArrayList<String[]> getAttachedFiles() {
public ArrayList<ThmmyFile> getAttachedFiles() {
return attachedFiles;
}
}

183
app/src/main/java/gr/thmmy/mthmmy/utils/FileManager/ThmmyFile.java

@ -0,0 +1,183 @@
package gr.thmmy.mthmmy.utils.FileManager;
import android.os.Environment;
import android.support.annotation.Nullable;
import android.webkit.MimeTypeMap;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Objects;
import mthmmy.utils.Report;
import okhttp3.Request;
import okhttp3.Response;
import static gr.thmmy.mthmmy.activities.base.BaseActivity.getClient;
/**
* Used for downloading and storing a file from the forum using {@link okhttp3}.
* <p>Class has one constructor: <ul><li>{@link #ThmmyFile(URL, String, String)}</li></ul>
* and the methods:<ul><li>getters</li>
* <li>{@link #download()}</li>
* <li>{@link #download(String)}</li></ul></p>
*/
public class ThmmyFile {
/**
* Debug Tag for logging debug output to LogCat
*/
private static final String TAG = "ThmmyFile";
/**
* Folder name used when downloading files without a package name.
*/
private static final String NO_PACKAGE_FOLDER_NAME = "Other";
private final URL fileUrl;
private final String filename, fileInfo;
private String extension, filePath;
private File file;
/**
* This constructor only creates a ThmmyFile object and <b>does not download</b> the file. To download
* the file use {@link #download(String)} or {@link #download()}!
*
* @param fileUrl {@link URL} object with file's url
* @param filename {@link String} with desired file name
* @param fileInfo {@link String} with any extra information (like number of downloads)
*/
public ThmmyFile(URL fileUrl, String filename, String fileInfo) {
this.fileUrl = fileUrl;
this.filename = filename;
this.fileInfo = fileInfo;
this.extension = null;
this.filePath = null;
this.file = null;
}
public URL getFileUrl() {
return fileUrl;
}
public String getFilename() {
return filename;
}
public String getFileInfo() {
return fileInfo;
}
/**
* This is null until {@link #download(String)} or {@link #download()} is called and has succeeded.
*
* @return String with file's extension or null
*/
@Nullable
public String getExtension() {
return extension;
}
/**
* This is null until {@link #download(String)} or {@link #download()} is called and has succeeded.
*
* @return String with file's path or null
*/
@Nullable
public String getFilePath() {
return filePath;
}
/**
* This is null until {@link #download(String)} or {@link #download()} is called and has succeeded.
*
* @return {@link File} or null
*/
@Nullable
public File getFile() {
return file;
}
private void setExtension(String extension) {
this.extension = extension;
}
private void setFilePath(String filePath) {
this.filePath = filePath;
}
/**
* Used to download the file. If download is successful file's extension and path will be assigned
* to object's fields and can be accessed using getter methods.
* <p>File is stored in sdcard1/Android/data/Downloads/{@link #NO_PACKAGE_FOLDER_NAME}</p>
*
* @return the {@link File} if successful, null otherwise
* @throws IOException
* @throws SecurityException
*/
@Nullable
public File download() throws IOException, SecurityException {
return download(NO_PACKAGE_FOLDER_NAME);
}
/**
* Used to download the file. If download is successful file's extension and path will be assigned
* to object's fields and can be accessed using getter methods.
* <p>File is stored in sdcard1/Android/data/Downloads/packageName</p>
*
* @param packageName package's name to use as folder name for file's path
* @return the {@link File} if successful, null otherwise
* @throws IOException if the request could not be executed due to cancellation, a connectivity
* problem or timeout. Because networks can fail during an exchange, it is possible that the
* remote server accepted the request before the failure.
* @throws SecurityException if the requested file is not hosted by the forum.
*/
@Nullable
public File download(final String packageName) throws IOException, SecurityException {
if (!Objects.equals(fileUrl.getHost(), "www.thmmy.gr"))
throw new SecurityException("Downloading files from other sources is not supported");
Request request = new Request.Builder().url(fileUrl).build();
Response response = getClient().newCall(request).execute();
if (!response.isSuccessful()) {
throw new IOException("Failed to download file: " + response);
}
file = getOutputMediaFile(packageName, filename);
if (file == null) {
Report.d(TAG, "Error creating media file, check storage permissions!");
} else {
FileOutputStream fos = new FileOutputStream(file);
fos.write(response.body().bytes());
fos.close();
filePath = file.getAbsolutePath();
extension = MimeTypeMap.getFileExtensionFromUrl(
filePath.substring(filePath.lastIndexOf("/")));
}
return file;
}
@Nullable
private static File getOutputMediaFile(String packageName, String fileName) {
// To be safe, you should check that the SDCard is mounted
// using Environment.getExternalStorageState() before doing this.
File mediaStorageDir = new File(Environment.getExternalStorageDirectory()
+ "/Android/data/gr.thmmy.mthmmy/"
+ "Downloads/"
+ packageName);
// This location works best if you want the created files to be shared
// between applications and persist after your app has been uninstalled.
// Create the storage directory if it does not exist
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
Report.d(TAG, "problem!");
return null;
}
}
// Create a media file name
File mediaFile;
mediaFile = new File(mediaStorageDir.getPath() + File.separator + fileName);
return mediaFile;
}
}
Loading…
Cancel
Save