package gov.nasa.jpl.hi.marsimages; import android.app.Activity; import android.app.AlertDialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.AsyncTask; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import android.widget.Toast; import com.evernote.edam.error.EDAMNotFoundException; import com.evernote.edam.error.EDAMSystemException; import com.evernote.edam.error.EDAMUserException; import com.evernote.edam.notestore.NoteFilter; import com.evernote.edam.notestore.NoteList; import com.evernote.edam.notestore.NoteStore; import com.evernote.edam.type.Note; import com.evernote.edam.type.NoteSortOrder; import com.evernote.edam.type.Resource; import com.evernote.edam.userstore.PublicUserInfo; import com.evernote.edam.userstore.UserStore; import com.evernote.thrift.TException; import com.evernote.thrift.protocol.TBinaryProtocol; import com.evernote.thrift.transport.THttpClient; import com.google.common.base.Stopwatch; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import gov.nasa.jpl.hi.marsimages.rovers.Rover; import static gov.nasa.jpl.hi.marsimages.MarsImagesApp.MARS_IMAGES; /** * Created by mpowell on 4/12/14. */ public class EvernoteMars { public static final EvernoteMars EVERNOTE = new EvernoteMars(); private final WifiStateReceiver mWifiStateReceiver = new WifiStateReceiver(); private static final int TIMEOUT = 15000; private static final String BEGIN_NOTE_LOADING = "beginNoteLoading"; public static final String END_NOTE_LOADING = "endNoteLoading"; public static final String NUM_NOTES_RETURNED = "numNotesReturned"; private static final String OPPY_NOTEBOOK_ID = "a7271bf8-0b06-495a-bb48-7c0c7af29f70"; private static final String MSL_NOTEBOOK_ID = "0296f732-694d-4ccd-9f5b-5983dc98b9e0"; private static final String SPIRIT_NOTEBOOK_ID = "f1a72415-56e7-4244-8e12-def9be9c512b"; public static final int NOTE_PAGE_SIZE = 15; private NoteStore.Client noteStore; private UserStore.Client userStore; private PublicUserInfo userInfo; private String uriPrefix; private static final List<Note> notesArray = Lists.newArrayList(); private static final Map<String, String> notebookIDs = Maps.newHashMap(); private static String searchWords = null; static { notebookIDs.put(Rover.CURIOSITY, MSL_NOTEBOOK_ID); notebookIDs.put(Rover.OPPORTUNITY, OPPY_NOTEBOOK_ID); notebookIDs.put(Rover.SPIRIT, SPIRIT_NOTEBOOK_ID); } public boolean hasNotesRemaining = true; private EvernoteMars() { IntentFilter mIntentFilter = new IntentFilter(MarsImagesApp.MISSION_CHANGED); BroadcastReceiver mMessageReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(MarsImagesApp.MISSION_CHANGED)) { searchWords = null; EvernoteMars.reloadNotes(context, true); //reset connection for new mission notebook } } }; LocalBroadcastManager.getInstance(MARS_IMAGES.getApplicationContext()).registerReceiver(mMessageReceiver, mIntentFilter); } public void loadMoreImages(Context context) { loadMoreImages(context, false); } public void loadMoreImages(Context context, boolean clearFirst) { ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); boolean isConnected = activeNetwork != null && activeNetwork.isConnectedOrConnecting(); if (!isConnected) { IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); intentFilter.setPriority(100); LocalBroadcastManager.getInstance(MARS_IMAGES.getApplicationContext()).registerReceiver(mWifiStateReceiver, intentFilter); Toast.makeText(context, "Unable to connect to the network.", Toast.LENGTH_SHORT).show(); return; } EVERNOTE.loadMoreNotes(context, clearFirst); } public int getNotesCount() { return notesArray.size(); } public Note getNote(int noteIndex) { if (noteIndex >= 0 && noteIndex < getNotesCount()) return notesArray.get(noteIndex); else return null; } public List<Note> getNotes() { return notesArray; } public void loadMoreNotes(Context context, boolean clearNotes) { new NoteLoaderTask().executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, context, getNotesCount(), clearNotes); } public int[] getNearestRMC() { int user_site = 0; int user_drive = 0; //find the RMC of the newest image/note for (Note note : notesArray) { String title = note.getTitle(); if (!title.contains("RMC")) { continue; } String rmcString = title.substring(title.length()-13); String siteString = rmcString.substring(0,6); String driveString = rmcString.substring(7,13); user_site = Integer.parseInt(siteString); user_drive = Integer.parseInt(driveString); break; } //find the RMC in locations closest to the RMC of the newest image List<int[]> locations = MARS_IMAGES.getLocations(MARS_IMAGES.getApplicationContext()); int[] lastLocation = locations.get(locations.size()-1); int siteIndex = lastLocation[0]; int driveIndex = lastLocation[1]; if (user_site != 0 || user_drive != 0) { for (int i = locations.size()-1; i >=0; i--) { int[] location = locations.get(i); int aSiteIndex = location[0]; int aDriveIndex = location[1]; if (aSiteIndex*100000+aDriveIndex < user_site*100000+user_drive) break; siteIndex = aSiteIndex; driveIndex = aDriveIndex; } } return new int[] { siteIndex, driveIndex }; } private void connect() throws TException, EDAMSystemException, EDAMUserException, EDAMNotFoundException { if (noteStore == null) { Stopwatch watch = Stopwatch.createStarted(); String userStoreUrl = "https://www.evernote.com/edam/user"; THttpClient userStoreHttpClient = new THttpClient(userStoreUrl); TBinaryProtocol userStoreProtocol = new TBinaryProtocol(userStoreHttpClient); userStore = new UserStore.Client(userStoreProtocol, userStoreProtocol); String user = MARS_IMAGES.getMission().getUser(); userInfo = userStore.getPublicUserInfo(user); uriPrefix = userInfo.getWebApiUrlPrefix(); String noteStoreUrl = userInfo.getNoteStoreUrl(); THttpClient noteStoreHttpClient = new THttpClient(noteStoreUrl); noteStoreHttpClient.setConnectTimeout(TIMEOUT); TBinaryProtocol noteStoreProtocol = new TBinaryProtocol(noteStoreHttpClient); noteStore = new NoteStore.Client(noteStoreProtocol, noteStoreProtocol); Log.d("evernote connect", "Time to connect to Evernote: " + watch.elapsed(TimeUnit.MILLISECONDS) + " ms"); } } public String getNoteUrl(int position) { String imageUrl = null; if (position >= 0 && position < notesArray.size()) { Note note = notesArray.get(position); imageUrl = getUri(note.getResources().get(0)); } return imageUrl; } public String getUri(Resource resource) { return EVERNOTE.getUriPrefix() + "res/" + resource.getGuid(); } String getUriPrefix() { return uriPrefix; } public String getThumbnailURL(int position) { String thumbnailUrl = null; if (position >= 0 && position < notesArray.size()) { Note note = notesArray.get(position); if (note.getResources().isEmpty()) return null; String guid = note.getResources().get(0).getGuid(); thumbnailUrl = getUriPrefix() + "thm/res/" + guid + "?size=" + gov.nasa.jpl.hi.marsimages.ui.ImageListFragment.THUMBNAIL_IMAGE_WIDTH; } return thumbnailUrl; } public class NoteLoaderTask extends AsyncTask<Object, Void, String> { private Context context; @Override protected String doInBackground(Object... params) { if (!(params[0] instanceof Context) || !(params[1] instanceof Integer) || !(params[2] instanceof Boolean)) { Log.e(getClass().toString(), "Unexpected input parameters"); } context = (Context) params[0]; Integer startIndex = (Integer) params[1]; final Boolean clearNotes = (Boolean) params[2]; if (clearNotes) { notesArray.clear(); startIndex = 0; hasNotesRemaining = true; Intent intent = new Intent(MarsImagesApp.NOTES_CLEARED); LocalBroadcastManager.getInstance(context).sendBroadcast(intent); } if (!hasNotesRemaining) { Intent intent = new Intent(END_NOTE_LOADING); intent.putExtra(NUM_NOTES_RETURNED, 0); LocalBroadcastManager.getInstance(context).sendBroadcast(intent); return null; } ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); boolean isConnected = activeNetwork != null && activeNetwork.isConnectedOrConnecting(); if (!isConnected) { hasNotesRemaining = false; Intent intent = new Intent(END_NOTE_LOADING); intent.putExtra(NUM_NOTES_RETURNED, 0); LocalBroadcastManager.getInstance(context).sendBroadcast(intent); return "Unable to connect to the network."; } int notesReturned = 0; try { if (noteStore == null || userStore == null) connect(); } catch (Exception e) { Log.w("service error", "Error connecting to Evernote: " + e); suspendEvernoteQueries(); Intent intent = new Intent(END_NOTE_LOADING); intent.putExtra(NUM_NOTES_RETURNED, 0); LocalBroadcastManager.getInstance(context).sendBroadcast(intent); return "The Mars image service is currently unavailable. Please try again later."; } if (notesArray.size() > startIndex) { return null; } try { Intent intent = new Intent(BEGIN_NOTE_LOADING); LocalBroadcastManager.getInstance(context).sendBroadcast(intent); NoteFilter filter = new NoteFilter(); filter.setNotebookGuid(notebookIDs.get(MARS_IMAGES.getMissionName())); filter.setOrder(NoteSortOrder.TITLE.getValue()); filter.setAscending(false); if (searchWords != null && !searchWords.isEmpty()) { filter.setWords(formatSearch(searchWords)); } NoteList notelist = noteStore.findNotes(null, filter, startIndex, NOTE_PAGE_SIZE); notesReturned = notelist.getNotes().size(); hasNotesRemaining = notelist.getTotalNotes() - (startIndex + notesReturned) > 0; for (Note note : notelist.getNotes()) { Note orderedNote = reorderResources(note); notesArray.add(orderedNote); } } catch (Exception e) { Log.w("service error", "Error querying Evernote: " + e); suspendEvernoteQueries(); Intent intent = new Intent(END_NOTE_LOADING); intent.putExtra(NUM_NOTES_RETURNED, notesReturned); LocalBroadcastManager.getInstance(context).sendBroadcast(intent); return "The Mars image service is currently unavailable. Please try again later."; } Intent intent = new Intent(END_NOTE_LOADING); intent.putExtra(NUM_NOTES_RETURNED, notesReturned); LocalBroadcastManager.getInstance(context).sendBroadcast(intent); return null; } @Override protected void onPostExecute(String message) { if (message != null && context instanceof Activity) { Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); } } } private void suspendEvernoteQueries() { hasNotesRemaining = false; new Thread() { @Override public void run() { try { sleep(30 * 1000); } catch (InterruptedException e) { //no need to handle } finally { hasNotesRemaining = true; } } }.start(); } private Note reorderResources(Note note) { List<String> resourceFilenames = Lists.newArrayList(); Map<String, Resource> resourcesByFile = Maps.newHashMap(); for (Resource resource : note.getResources()) { Rover mission = MARS_IMAGES.getMission(); String filename = mission.getSortableImageFilename(resource.getAttributes().getSourceURL()); resourceFilenames.add(filename); resourcesByFile.put(filename, resource); } Collections.sort(resourceFilenames); List<Resource> sortedResources = Lists.newArrayList(); for (String resourceFilename : resourceFilenames) { sortedResources.add(resourcesByFile.get(resourceFilename)); } note.setResources(sortedResources); return note; } public static void reloadNotes(Context context) { reloadNotes(context, false); } public static void reloadNotes(Context context, boolean resetConnection) { EVERNOTE.hasNotesRemaining = true; if (resetConnection) { EVERNOTE.noteStore = null; EVERNOTE.userStore = null; EVERNOTE.userInfo = null; EVERNOTE.uriPrefix = null; } EVERNOTE.loadMoreNotes(context, true); } public static void setSearchWords(String searchWords, Context context) { EvernoteMars.searchWords = searchWords; EvernoteMars.reloadNotes(context); } private String formatSearch(String text) { String[] words = text.split("\\s+"); StringBuilder formattedText = new StringBuilder(); for (String w : words) { String word = w; int value = 0; try { value = Integer.parseInt(word); } catch (NumberFormatException e) { Log.w("search words", "Expected an integer value instead of "+word); } if (value > 0 && !word.endsWith("*")) { word = String.format("\"Sol %05d\"", value); } if (formattedText.length() > 0) { formattedText.append(" "); } formattedText.append(String.format("intitle:%s", word)); } Log.d("search words", "formatted text: " + formattedText); return formattedText.toString(); } public static class WifiStateReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); boolean isConnected = activeNetwork != null && activeNetwork.isConnectedOrConnecting(); if (isConnected) { Log.d("wifi state connected", "Wifi reconnected."); LocalBroadcastManager.getInstance(context).unregisterReceiver(this); EVERNOTE.hasNotesRemaining = true; EVERNOTE.loadMoreNotes(context, false); } } } }