package ca.ualberta.cs.cmput301f14t14.questionapp.data; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.UUID; import android.content.Context; import android.content.Intent; import android.location.Address; import android.location.Geocoder; import android.location.Location; import ca.ualberta.cs.cmput301f14t14.questionapp.data.eventbus.EventBus; import ca.ualberta.cs.cmput301f14t14.questionapp.data.threading.AddAnswerCommentTask; import ca.ualberta.cs.cmput301f14t14.questionapp.data.threading.AddAnswerTask; import ca.ualberta.cs.cmput301f14t14.questionapp.data.threading.AddQuestionCommentTask; import ca.ualberta.cs.cmput301f14t14.questionapp.data.threading.AddQuestionTask; import ca.ualberta.cs.cmput301f14t14.questionapp.data.threading.GetAnswerCommentTask; import ca.ualberta.cs.cmput301f14t14.questionapp.data.threading.GetAnswerListTask; import ca.ualberta.cs.cmput301f14t14.questionapp.data.threading.GetAnswerTask; import ca.ualberta.cs.cmput301f14t14.questionapp.data.threading.GetCommentListTask; import ca.ualberta.cs.cmput301f14t14.questionapp.data.threading.GetQuestionCommentTask; import ca.ualberta.cs.cmput301f14t14.questionapp.data.threading.GetQuestionListTask; import ca.ualberta.cs.cmput301f14t14.questionapp.data.threading.GetQuestionTask; import ca.ualberta.cs.cmput301f14t14.questionapp.data.threading.UploaderService; import ca.ualberta.cs.cmput301f14t14.questionapp.model.Answer; import ca.ualberta.cs.cmput301f14t14.questionapp.model.Comment; import ca.ualberta.cs.cmput301f14t14.questionapp.model.Question; /** * DataManager is a singleton that talks to local and remote data sources */ public class DataManager { private static DataManager instance; private IDataStore localDataStore; private IDataStore remoteDataStore; private List<UUID> recentVisit; private List<UUID> readLater; private Context context; String Username; static final String favQ = "fav_Que"; static final String favA = "fav_Ans"; static final String recV = "rec_Vis"; static final String redL = "red_Lat"; static final String pusO = "pus_Onl"; static final String upvO = "upv_Onl"; private EventBus eventbus = EventBus.getInstance(); private DataManager(Context context) { this.context = context; } /** * Create data stores * * This must be done after the constructor, because some data stores * refer back to DataManager, and cannot do so until it is constructed. */ private void initDataStores() { this.localDataStore = new LocalDataStore(context); this.remoteDataStore = new RemoteDataStore(context); } /** * Singleton getter * @param context * @return Instance of DataManager */ public static DataManager getInstance(Context context){ if (instance == null){ instance = new DataManager(context.getApplicationContext()); instance.initDataStores(); } return instance; } /** * Starts the uploader service that copies cached creations in local to remote. */ public void startUploaderService() { //Hackery. No idea how to start a service only once and not get into threading problems. if (UploaderService.isServiceAlreadyRunning == false) { Intent i = new Intent(context, UploaderService.class); context.startService(i); } } //View Interface Begins public void addQuestion(Question validQ, Callback<Void> c) { AddQuestionTask aqt = new AddQuestionTask(context); aqt.setCallBack(c); aqt.execute(validQ); } /** * Get a question by its UUID * @param id * @return */ public Question getQuestion(UUID id, Callback<Question> c) { GetQuestionTask task = new GetQuestionTask(context); if (c == null) { return task.blockingRun(id); } task.setCallBack(c); task.execute(id); return null; } /** * Add an answer record * @param A Answer to add */ public void addAnswer(Answer A){ AddAnswerTask aat = new AddAnswerTask(context); aat.execute(A); } /** * Get answer record * @param Aid Answer ID * @return */ public Answer getAnswer(UUID Aid, Callback<Answer> c) { //Add this answer to the recentVisit list GetAnswerTask gat = new GetAnswerTask(context); Answer anull = null; if (c == null) { //User wants an answer within a thread, or doesn't care about blocking. return gat.blockingRun(Aid); } gat.setCallBack(c); gat.execute(Aid); return anull; //Hopefully eclipse will warn users this method always returns null } /** * Add comment record to question * @param C */ public void addQuestionComment(Comment<Question> C){ AddQuestionCommentTask aqct = new AddQuestionCommentTask(context); aqct.execute(C); //May have a problem here. Look here first if crashing. } /** * Get comment record from question * @param cid * @return */ //Wtf, when I added a Callback parameter, nothing broke... Is this //method actually called anywhere in the app? public Comment<Question> getQuestionComment(UUID cid, Callback<Comment<? extends ICommentable>> c) { GetQuestionCommentTask gqct = new GetQuestionCommentTask(context); if (c == null){ //User does not care about blocking return (Comment<Question>) gqct.blockingRun(cid); } gqct.setCallBack(c); gqct.execute(cid); //If the user is using threading, they will care to extract their result from the callback return null; } /** * Add comment record for answer * @param C */ public void addAnswerComment(Comment<Answer> C){ AddAnswerCommentTask aact = new AddAnswerCommentTask(context); aact.execute(C); //Possibly trouble here. } /** * Get comment record from answer * @param Cid * @return */ //Another case where adding a callback to the function signature didn't break the app //Are we using this? public Comment<Answer> getAnswerComment(UUID Cid, Callback<Comment<? extends ICommentable>> c){ GetAnswerCommentTask gact = new GetAnswerCommentTask(context); if (c == null) { //User doesn't care about threading and expects this to be blocking. return (Comment<Answer>) gact.blockingRun(Cid); } gact.setCallBack(c); gact.execute(Cid); //The user, by not setting a null callback, should know to fetch the result //out of the callback, and should not be surprised at an NPE. return null; } /** * Get a list of all existing questions. * * This list is not returned with any particular order. * @return */ public List<Question> getQuestionList(Callback callback) { GetQuestionListTask task = new GetQuestionListTask(context); if (callback == null) { //User doesn't care this is blocking return task.blockingRun(); } task.setCallBack(callback); task.execute(); //User should expect this to be null, since the result should be pulled out of the callback return null; } /** * Get a list of comments from an answer asynchronously * @param a * @param c * @return */ public List<Comment<Answer>> getCommentList(Answer a, Callback<List<Comment<Answer>>> c){ GetCommentListTask<Answer> gclat = new GetCommentListTask<Answer>(context); if (c == null) { //User doesn't care this is blocking return gclat.blockingRun(a); } gclat.setCallBack(c); gclat.execute(a); //User should expect this to be null, since the result should be pulled out of the callback return null; } /** * Get a list of comments from a question asynchronously * @param q * @param c * @return */ public List<Comment<Question>> getCommentList(Question q, Callback<List<Comment<Question>>> c){ GetCommentListTask<Question> gclqt = new GetCommentListTask<Question>(context); if (c == null) { //User doesn't care this is blocking return gclqt.blockingRun(q); } gclqt.setCallBack(c); gclqt.execute(q); //User should pull result out of callback return null; } public List<Answer> getAnswerList(Question q, Callback<List<Answer>> c){ GetAnswerListTask galt = new GetAnswerListTask(context); if (c == null) { //User does not care this is blocking return galt.blockingRun(q); } galt.setCallBack(c); galt.execute(q); //User should pull result out of callback return null; } public IDataStore getLocalDataStore() { return localDataStore; } public IDataStore getRemoteDataStore() { return remoteDataStore; } // Cache old location lookups for speed private HashMap<Location, String> oldloclookups = new HashMap<Location, String>(); public String getCityFromLocation(Location l) { //Go to here: http://stackoverflow.com/questions/2296377/how-to-get-city-name-from-latitude-and-longitude-coordinates-in-google-maps //Cache old locations for speed if (oldloclookups.containsKey(l)) { return oldloclookups.get(l); } Geocoder g = new Geocoder(context, Locale.getDefault()); if (Geocoder.isPresent()){ //ROck and roll bitches we can find the place! List<Address> la = null; try { la = g.getFromLocation(l.getLatitude(), l.getLongitude(), 1); } catch (Exception e) { return "a Universe"; } if (la != null && la.size() > 0){ String city = la.get(0).getLocality(); oldloclookups.put(l, city); return city; } } return "a Universe"; } }