package ca.ualberta.cs.cmput301t03app.controllers;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import ca.ualberta.cs.cmput301t03app.datamanagers.LocalDataManager;
import ca.ualberta.cs.cmput301t03app.datamanagers.QuestionFilter;
import ca.ualberta.cs.cmput301t03app.datamanagers.ServerDataManager;
import ca.ualberta.cs.cmput301t03app.interfaces.IDataManager;
import ca.ualberta.cs.cmput301t03app.models.Answer;
import ca.ualberta.cs.cmput301t03app.models.Comment;
import ca.ualberta.cs.cmput301t03app.models.GeoLocation;
//import ca.ualberta.cs.cmput301t03app.models.Post;
import ca.ualberta.cs.cmput301t03app.models.Question;
//import ca.ualberta.cs.cmput301t03app.models.UpvoteTuple;
import ca.ualberta.cs.cmput301t03app.models.UserPostCollector;
import ca.ualberta.cs.cmput301t03app.models.Tuple;
import android.content.Context;
import android.location.Address;
import android.location.Geocoder;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
import android.widget.Toast;
/**
* Responsible for all of the model manipulation in the program.
* <p>
* This class uses a UserPostCollector which contains all of the local data
* information (including posted questions, favorites, to read, read, etc.). It
* also uses a DataManager which is used for loading and saving.
*
*
*/
public class PostController {
private static ArrayList<Question> subQuestions = null;
private static QuestionFilter qf = new QuestionFilter();
private static UserPostCollector upc = new UserPostCollector();
private static ServerDataManager sdm = new ServerDataManager();
private static LocalDataManager ldm;
private static int serverListIndex = 10;
private static ArrayList<Question> serverList = new ArrayList<Question>();
private Context context;
/**
*
* Constructs a {@link #PostController() PostController}
*
* @param context
* The context of the PostController
*/
public PostController(Context context) {
this.context = context;
this.ldm = new LocalDataManager(context);
}
/*
* #############################----START OF SORTING
* METHODS-----###################################
*/
/**
* Sorts the sub-question list by presence of picture, placing the posts
* with pictures at the top
*/
public void sortQuestionsByPic() {
qf.sortByPic(getQuestionsInstance());
}
/**
* Sort the sub-question list by rating, placing the most upvoted question
* at the top.
*/
public void sortQuestionsByUpvote() {
qf.sortByUpvote(getQuestionsInstance());
}
/**
* Sorts the sub-questions list by date, placing the most recent one on top.
*/
public void sortQuestionsByDate() {
qf.sortByDate(getQuestionsInstance());
}
/**
* Sorts the sub-question list by location, placing the closest location to
* the user at the top.
*
* @param location
*/
public void sortByLocation(GeoLocation location) {
ArrayList<Question> filtered = qf.sortByLocation(
getQuestionsInstance(), location);
getQuestionsInstance().clear();
getQuestionsInstance().addAll(filtered);
}
/*
* #############################----END OF SORTING
* METHODS-----###################################
*/
/**
* Upvote question method. Increments the Question rating by 1.
*
* @param questionId
* The ID of the question that is being upvoted
*/
public void upvoteQuestion(String questionId) {
getQuestion(questionId).upRating();
}
/**
* Upvote answer method. Increments the Answer rating by 1.
*
* @param answerId
* The ID of the answer being upvoted
* @param questionId
* The ID of the question the answer belongs to
*/
public void upvoteAnswer(String answerId, String questionId) {
getAnswer(answerId, questionId).upRating();
}
/*
* ############################----START OF GEOLOCATION
* METHODS----##################################
*/
/**
* Takes in a geolocation and returns a city name if available
*
* @param Geolocation
* location
* @return a string with the city name
*/
public String getCity(GeoLocation location) {
return location.getCityFromLoc(context);
}
public GeoLocation turnFromCity(String cityName) {
Geocoder gcd = new Geocoder(getContext(), Locale.getDefault());
List<Address> addresses;
GeoLocation location = new GeoLocation();
location.setCityName(cityName);
try {
addresses = gcd.getFromLocationName(cityName, 1);
if (addresses.size() > 0) {
location.setLatitude(addresses.get(0).getLatitude());
location.setLongitude(addresses.get(0).getLongitude());
}
} catch (Exception e) {
e.printStackTrace();
}
return location;
}
/*
* #################################------END OF GEOLOCATION
* METHODS----################################
*/
/*
* ################################-----START OF CHECKING
* METHODS----###################################
*/
/**
* Returns true if the application is connected to the internet
*
* @return A boolean stating if connected to internet
*/
public Boolean checkConnectivity() {
ConnectivityManager connMgr = (ConnectivityManager) getContext()
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnectedOrConnecting()) {
return true;
} else {
return false;
}
}
/**
* Returns true if a question is favorited by the user.
*
* @param questionID
* the question ID of the question being checked.
*
* */
public Boolean isQuestionInFavByID(String questionID) {
upc.initFavoriteID(getContext());
for (int i = 0; i < upc.getFavoriteQuestions().size(); i++) {
if (upc.getFavoriteQuestions().get(i).equals(questionID))
return true;
}
return false;
}
/**
* Returns true if a question is in the cache or has already been read by
* the user.
*
* @param questionID
* the question ID of the question being checked.
*
* */
public Boolean isQuestionInReadByID(String questionID) {
for (int i = 0; i < upc.getFavoriteQuestions().size(); i++) {
if (upc.getFavoriteQuestions().get(i).equals(questionID))
return true;
}
return false;
}
/**
*
* Saves a question to local memory given that, that question does not exist
* in local memory.
*
* @param q
* The question to be saved.
* @param local
* The datamanager that saves to local memory
*/
private void checkExistanceOfQuestion(Question q, LocalDataManager local) {
boolean found = false;
for (int i = 0; i < upc.getQuestionBank().size(); i++) {
if (upc.getQuestionBank().get(i).getId().equals(q.getId())) {
found = true;
break;
}
}
if (!found) {
ArrayList<Question> questionList = upc.getQuestionBank();
questionList.add(q);
local.saveToQuestionBank(questionList);
}
}
/*
* #############################-------END OF CHECKING
* METHODS----##################################
*/
/*
* ############################-------START OF LOCAL DB
* METHODS-----####################################
*/
/**
* This method replaces the old question with the updated question in the
* question bank and saves to local storage.
*
* @param qId
* A String representing a Question ID
*/
public void updateQuestionInBank(String qId) {
Question q = getQuestion(qId);
LocalDataManager local = new LocalDataManager(getContext());
ArrayList<Question> questionArray = upc.getQuestionBank();
for (int i = 0; i < questionArray.size(); i++) {
if (q.getId().equals(questionArray.get(i).getId())) {
questionArray.set(i, q);
}
}
local.saveToQuestionBank(questionArray);
}
/**
* Adds a question to the favorite questions list and saves it locally.
*
* @param q
* the Question object to be saved.
*/
public void addFavoriteQuestion(Question q) {
upc.initFavoriteID(getContext());
upc.initQuestionBank(getContext());
LocalDataManager local = new LocalDataManager(getContext());
ArrayList<String> idList = upc.getFavoriteQuestions();
String id = q.getId();
if (!idList.contains(id)) {
idList.add(id);
local.saveFavoritesID(idList);
checkExistanceOfQuestion(q, local);
}
}
/**
* Adds a question to the read questions list and saves it locally
*
* @param q
* the previously read question that must be saved.
*/
public void addReadQuestion(Question q) {
upc.initReadID(getContext()); // I need to load the lists if they
// haven't been loaded yet.
upc.initQuestionBank(getContext());
LocalDataManager local = new LocalDataManager(getContext());
ArrayList<String> idList = upc.getReadQuestions();
String id = q.getId();
if (!idList.contains(id)) {
idList.add(id);
local.saveReadID(idList);
checkExistanceOfQuestion(q, local);
}
}
/**
* Adds a question to the "to read" list and saves it locally
*
* @param q
* the question object that the user wants to read later.
*/
public void addToRead(Question q) {
upc.initToReadID(getContext()); // I need to load the lists if they
// haven't been loaded yet.
upc.initQuestionBank(getContext());
LocalDataManager local = new LocalDataManager(getContext());
ArrayList<String> idList = upc.getToReadQuestions();
String id = q.getId();
if (!idList.contains(id)) {
idList.add(id);
local.saveToReadID(idList);
checkExistanceOfQuestion(q, local);
}
}
/**
* Adds a question to the posted questions list and saves it locally
*
* @param q
* a Question object that the user wants to post
*/
public void addUserPost(Question q) {
upc.initPostedQuestionID(getContext()); // I need to load the lists if
// they haven't been loaded yet.
upc.initQuestionBank(getContext());
LocalDataManager local = new LocalDataManager(getContext());
ArrayList<String> idList = upc.getPostedQuestions();
ArrayList<Question> qList = upc.getQuestionBank();
String id = q.getId();
idList.add(id);
qList.add(q);
local.savePostedQuestionsID(idList);
local.saveToQuestionBank(qList);
upc.initQuestionBank(getContext()); // Need to refresh questionBank list
// in upc
}
/**
* Adds a question to the list of questions made while offline.
*
* @param q
* A question made offline.
*/
public void addPushQuestion(Question q) {
upc.initPushQuestionID(getContext());
upc.initQuestionBank(getContext());
LocalDataManager local = new LocalDataManager(getContext());
Toast.makeText(
this.getContext(),
"You are offline. Push your posts to the server when you regain connectivity with the 'sync' button",
Toast.LENGTH_LONG).show();
Log.d("Debug", "Question was asked offline");
ArrayList<String> idList = upc.getPushQuestions();
ArrayList<Question> qList = upc.getQuestionBank();
String id = q.getId();
idList.add(id);
qList.add(q);
local.savePushQuestionsID(idList);
local.saveToQuestionBank(qList);
}
/**
* Adds an Answer object to the question object
*
* @param answer
* the answer object that the user has made
* @param questionID
* The ID of the question that the answer will be added to
*/
public void addAnswer(Answer answer, String questionID) {
getQuestion(questionID).addAnswer(answer);
updateQuestionInBank(questionID);
}
/**
* Adds a comment object to a question object.
*
* @param comment
* A comment object that the user wants to be added to a
* question.
* @param parentId
* The ID of the question that the comment will be added to
*/
public void addCommentToQuestion(Comment comment, String questionId) {
for (int i = 0; i < subQuestions.size(); i++) {
if (subQuestions.get(i).getId().equals(questionId)) {
subQuestions.get(i).addComment(comment);
}
}
// TODO: pushNewPosts();
updateQuestionInBank(questionId);
}
/**
* Adds a comment object to an answer object.
*
* @param comment
* A comment object that will be added to the answer
* @param answerID
* The ID of the answer that the comment will be added to
* @param questionID
* the question that the answer is pertaining to.
*/
public void addCommentToAnswer(Comment comment, String questionID,
String answerID) {
Question q = getQuestion(questionID);
ArrayList<Answer> a = q.getAnswers();
for (int i = 0; i < a.size(); i++) {
if (a.get(i).getId().equals(answerID)) {
a.get(i).addComment(comment);
}
}
// TODO: pushNewPosts();
updateQuestionInBank(questionID);
}
/**
* Returns the list of favorited questions.
* <p>
* This method only pulls from the question bank of favorite IDs list .
*
* @return A list of the users favorited questions.
*/
public ArrayList<Question> getFavoriteQuestions() {
LocalDataManager local = new LocalDataManager(getContext());
ArrayList<String> idArray = local.loadFavorites();
ArrayList<Question> favoriteArray = getQuestionsFromID(idArray, local);
return favoriteArray;
}
/**
* Returns the list of read questions.
* <p>
* This method only pulls from the question bank of read IDs list .
*
* @return A list of the user's previously read Questions.
*/
public ArrayList<Question> getReadQuestions() {
LocalDataManager local = new LocalDataManager(getContext());
ArrayList<String> idArray = local.loadRead();
ArrayList<Question> readArray = getQuestionsFromID(idArray, local);
return readArray;
}
/**
* Returns the list of "To-read" questions.
* <p>
* This method only pulls from the question bank of "to-read" IDs list .
*
* @return A list of questions that the user has marked "To-Read".
*/
public ArrayList<Question> getToReadQuestions() {
LocalDataManager local = new LocalDataManager(getContext());
ArrayList<String> idArray = local.loadToRead();
ArrayList<Question> toReadArray = getQuestionsFromID(idArray, local);
return toReadArray;
}
/**
* Returns the list of posted questions. This method only pulls from the
* question bank of posted questions IDs list .
*
* @return A list of questions that the user has posted.
*/
public ArrayList<Question> getUserPostedQuestions() {
LocalDataManager local = new LocalDataManager(getContext());
ArrayList<String> idArray = local.loadPostedQuestions();
ArrayList<Question> postedArray = getQuestionsFromID(idArray, local);
return postedArray;
}
public Question getQuestionFromLocalSave(String qID) {
LocalDataManager local = new LocalDataManager(getContext());
ArrayList<Question> questionBank = local.loadQuestions();
for (int i = 0; i < questionBank.size(); i++) {
if (questionBank.get(i).getId().equals(qID)) {
return questionBank.get(i);
}
}
return null;
}
/*
* ##################################------END OF LOCAL DB
* METHODS------#####################################
*/
/**
* Adds and saves the question ID, answer ID and comment object needed to
* push offline posts to the server when the user re-syncs
*
* @param qID
* The ID of the question corresponding to the question object
* @param aID
* The ID of the answer corresponding to the answer object
* @param comment
* The comment object
*/
public void addPushAnsAndComm(String qID, String aID, Comment comment) {
upc.initPushAnsCommTuple(getContext());
LocalDataManager local = new LocalDataManager(getContext());
Toast.makeText(
this.getContext(),
"You are offline. Push your posts to the server when you regain connectivity with the 'sync' button",
Toast.LENGTH_LONG).show();
ArrayList<Tuple> tupleList = upc.getPushAnswersAndComments();
Tuple tuple = new Tuple(qID, aID, comment);
tupleList.add(tuple);
local.savePushAnsAndComm(tupleList);
}
/**
* Parses the sub-question list of the PC to load the next 10 questions in
* the list
*
* @param list
* The sub-question list to be parsed.
*/
public void loadMoreServerQuestions() {
// Log.d("Debug", "passed size:"+serverList.size());
// Log.d("Debug", "serverListIndex is at "+serverListIndex);
int checkListSize = serverList.size();
int increment = 10;
if (checkListSize - serverListIndex < 10) {
increment = checkListSize - serverListIndex;
}
if (checkListSize > 10) {
for (int i = serverListIndex; i < (serverListIndex + increment); i++) {
subQuestions.add(serverList.get(i));
}
}
serverListIndex = serverListIndex + increment;
}
// Pushes new posts to server, returns true if connectivity attained and
// pushed // returns false otherwise // This makes testing easier
/*
* ###############################-----START OF GETTER
* METHODS----################################
*/
public Context getContext() {
return this.context;
}
public UserPostCollector getUPC() {
return upc;
}
/**
* Returns the questions that the post controller contains.
*
* @return A list of Question objects
*/
public ArrayList<Question> getQuestionsInstance() {
if (subQuestions == null) {
subQuestions = new ArrayList<Question>();
}
return subQuestions;
}
/**
* Retrieves questions from the server and places the 10 most recent
* questions into the sub-questions list to display
*
* @return The sub-question list containing 10 questions from the server.
*/
public ArrayList<Question> getQuestionsFromServer() {
serverList = sdm.searchQuestions("", null);
serverList = qf.sortByDate(serverList);
subQuestions.clear();
for (int i = 0; i < serverList.size(); i++) {
if (i > 9) {
return subQuestions;
} else {
subQuestions.add(serverList.get(i));
}
}
return subQuestions;
}
/**
* Returns the list of comments to the specified question object
*
* @param questionID
* the question that the comments pertain to.
* @return A list of Comment objects
*/
public ArrayList<Comment> getCommentsToQuestion(String questionID) {
for (int i = 0; i < subQuestions.size(); i++) {
if (subQuestions.get(i).getId().equals(questionID)) {
return subQuestions.get(i).getComments();
}
}
return null;
}
/**
* Returns the list of comments to the specified answer object
*
* @param answerID
* @return ArrayList<Comment> A list of Comment objects
*/
public ArrayList<Comment> getCommentsToAnswer(String questionID,
String answerID) {
Question q = getQuestion(questionID);
ArrayList<Answer> a = q.getAnswers();
for (int i = 0; i < a.size(); i++) {
if (a.get(i).getId().equals(answerID)) {
return a.get(i).getComments();
}
}
return null;
}
/**
* Returns an answer object of a given answer id.
*
* @param answerId
* The ID of the answer object being returned.
* @param questionId
* The ID of the question that the answer pertains to.
* @return An Answer object
*/
public Answer getAnswer(String answerId, String questionId) {
Question q = getQuestion(questionId);
ArrayList<Answer> a = q.getAnswers();
for (int i = 0; i < a.size(); i++) {
if (a.get(i).getId().equals(answerId))
return a.get(i);
}
return null;
}
/**
* Returns the question object of a given question id from the sub-question
* list.
*
* @param qID
* The ID of the question object being returned.
* @return A Question Object
*/
public Question getQuestion(String qID) {
for (int i = 0; i < getQuestionsInstance().size(); i++) {
if (subQuestions.get(i).getId().equals(qID)) {
return subQuestions.get(i);
}
}
return null;
}
/**
* Returns an array Tuple object that contains the information needed to
* push Questions, Answers and Comments made while offline
*
* @return Array of Tuple objects
*/
public ArrayList<Tuple> getTupleForPush() {
LocalDataManager local = new LocalDataManager(getContext());
ArrayList<Tuple> tupleArray = local.loadTupleArray();
return tupleArray;
}
/**
*
* Returns an ArrayList of Question objects from local memory using a list
* of Question IDs.
*
* @param idArray
* An array of Question IDs
* @param local
* the LocalDataManager used to load questions
* @return returns an ArrayList of Questions
*/
private ArrayList<Question> getQuestionsFromID(ArrayList<String> idArray,
LocalDataManager local) {
ArrayList<Question> returnedArray = new ArrayList<Question>();
ArrayList<Question> questionArray = local.loadQuestions();
for (int i = 0; i < idArray.size(); i++) {
for (int j = 0; j < questionArray.size(); j++) {
if (idArray.get(i).equals(questionArray.get(j).getId())) {
returnedArray.add(questionArray.get(j));
}
}
}
return returnedArray;
}
/**
* Gets answer object corresponding to the answerID from the question object
* corresponding to the question ID
*
* @param questionID
* ID of the question object to find
* @param answerID
* ID of the answer object within the question object
* @return Answer object
*/
public Answer getAnswerToPush(String questionID, String answerID) {
LocalDataManager local = new LocalDataManager(getContext());
ArrayList<String> idArray = new ArrayList<String>();
idArray.add(questionID);
ArrayList<Question> associatedQuestion = getQuestionsFromID(idArray,
local);
Question unpackQuestion = associatedQuestion.get(0);
ArrayList<Answer> answerArray = unpackQuestion.getAnswers();
for (int i = 0; i < answerArray.size(); i++) {
if (answerArray.get(i).getId().equals(answerID)) {
return answerArray.get(i);
}
}
return null;
}
/*
* ##############################----END OF GETTER
* METHODS----################################
*/
/**
* Resets the counter for indexing the position of the list of questions
*/
public void resetServerListIndex() {
serverListIndex = 0;
}
public ArrayList<Question> executeSearch(String searchString) {
ServerDataManager sdm = new ServerDataManager();
ArrayList<Question> qList = sdm.searchQuestions(searchString, null);
getQuestionsInstance().clear();
getQuestionsInstance().addAll(qList);
return qList;
}
/**
* Error message to display when there is no connection and user attempts to
* upvote.
*/
public void cantUpvoteMsg() {
Toast.makeText(getContext(), "Please re-connect to upvote",
Toast.LENGTH_SHORT).show();
}
public int countAnswers(Question q) {
return q.countAnswers();
}
public int countComments(Question q) {
return q.countComments();
}
}