/*
* Copyright 2013 Vishwa Patel
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License in the 'assets' directory of this
* application or at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.vishwa.pinit;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import android.app.AlertDialog;
import android.app.SearchManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Point;
import android.media.ThumbnailUtils;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.FragmentActivity;
import android.support.v4.util.LruCache;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MenuItem.OnActionExpandListener;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.CursorAdapter;
import android.widget.FrameLayout;
import android.widget.SearchView;
import android.widget.SearchView.OnQueryTextListener;
import android.widget.SearchView.OnSuggestionListener;
import android.widget.Toast;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapsInitializer;
import com.google.android.gms.maps.GoogleMap.CancelableCallback;
import com.google.android.gms.maps.GoogleMap.OnCameraChangeListener;
import com.google.android.gms.maps.GoogleMap.OnInfoWindowClickListener;
import com.google.android.gms.maps.GoogleMap.OnMapClickListener;
import com.google.android.gms.maps.GoogleMap.OnMapLongClickListener;
import com.google.android.gms.maps.GoogleMap.OnMarkerClickListener;
import com.google.android.gms.maps.GoogleMap.OnMarkerDragListener;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.parse.DeleteCallback;
import com.parse.GetCallback;
import com.parse.GetDataCallback;
import com.parse.ParseException;
import com.parse.ParseFacebookUtils;
import com.parse.ParseFile;
import com.parse.ParseGeoPoint;
import com.parse.ParseObject;
import com.parse.ParseQuery;
import com.parse.ParseUser;
import com.parse.SaveCallback;
public class MainActivity extends FragmentActivity implements OnMapLongClickListener, OnMarkerDragListener,
OnMarkerClickListener, OnInfoWindowClickListener, OnMapClickListener{
public static final int REQUEST_CODE_CREATE_NOTE = 102;
public static final int REQUEST_CODE_DISPLAY_NOTE = 103;
public static final int REQUEST_CODE_EDIT_NOTE = 104;
public static final int REQUEST_CODE_CHANGE_SETTINGS = 105;
public static final boolean DEBUG = false;
private Button mAllNotesButton;
private Button mYourNotesButton;
private SearchView mSearchView;
private FrameLayout mFrameLayout;
private GoogleMap mMap;
private Menu mMenu;
private MenuItem mSearchMenuItem;
private Marker mFocusedMarker = null;
private Handler mHandler;
private Bitmap mUserPhotoThumbnail;
private LruCache<String, Bitmap> mMemoryCache;
private String mCurrentUsername;
private ArrayList<Marker> mMarkerList = new ArrayList<Marker>(20);
private HashMap<String, Note> mNoteStore = new HashMap<String, Note>();
private HashMap<String, String> mReverseNoteStore = new HashMap<String, String>();
private boolean mHasInternet = true;
private MapViewMode mMapViewMode = MapViewMode.ALL_NOTES;
private MapEditMode mMapEditMode = MapEditMode.DEFAULT_MODE;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
mFrameLayout = new FrameLayout(this);
if(getResources().getBoolean(R.bool.portrait_only)){
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
setContentView(mFrameLayout);
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.activity_main, mFrameLayout);
inflater.inflate(R.layout.splash_screen, mFrameLayout);
mHandler = new Handler();
final Runnable runnable = new Runnable() {
@Override
public void run() {
Animation fadeOutAnimation = AnimationUtils.loadAnimation(getApplicationContext(),
android.R.anim.fade_out);
fadeOutAnimation.setAnimationListener(new AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
mFrameLayout.removeViewAt(1);
if(PinItUtils.isUsersFirstLogin(mCurrentUsername, getApplicationContext())) {
if(!DEBUG) {
handleUsersFirstTime();
}
}
}
});
mFrameLayout.getChildAt(1).startAnimation(fadeOutAnimation);
}
};
mHandler.postDelayed(runnable, 2300);
mCurrentUsername = ParseUser.getCurrentUser().getUsername();
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
final int cacheSize = maxMemory / 10;
RetainFragment mRetainFragment =
RetainFragment.findOrCreateRetainFragment(getSupportFragmentManager());
mMemoryCache = mRetainFragment.mRetainedCache;
if(mMemoryCache == null) {
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getByteCount() / 1024;
}
};
mRetainFragment.mRetainedCache = mMemoryCache;
}
mAllNotesButton = (Button) findViewById(R.id.main_all_notes_button);
mYourNotesButton = (Button) findViewById(R.id.main_your_notes_button);
setProgressBarIndeterminateVisibility(false);
mAllNotesButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
LatLngBounds mapBounds = mMap.getProjection().getVisibleRegion().latLngBounds;
LatLng southwest = mapBounds.southwest;
LatLng northeast = mapBounds.northeast;
mMapViewMode = MapViewMode.ALL_NOTES;
for(Marker marker: mMarkerList) {
marker.remove();
}
mMarkerList.clear();
mNoteStore.clear();
mReverseNoteStore.clear();
LatLngTuple tuple = new LatLngTuple(southwest, northeast);
LoadNotesTask currentUserNotesTask = new LoadNotesTask(tuple, false);
currentUserNotesTask.execute();
setProgressBarIndeterminateVisibility(true);
}
});
mYourNotesButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
LatLngBounds mapBounds = mMap.getProjection().getVisibleRegion().latLngBounds;
LatLng southwest = mapBounds.southwest;
LatLng northeast = mapBounds.northeast;
for(Marker marker: mMarkerList) {
marker.remove();
}
mMarkerList.clear();
mNoteStore.clear();
mReverseNoteStore.clear();
mMapViewMode = MapViewMode.YOUR_NOTES;
LatLngTuple tuple = new LatLngTuple(southwest, northeast);
LoadNotesTask currentUserNotesTask = new LoadNotesTask(tuple, true);
currentUserNotesTask.execute();
setProgressBarIndeterminateVisibility(true);
}
});
try {
MapsInitializer.initialize(this);
if(!PinItUtils.isOnline(getApplicationContext())) {
PinItUtils.createAlert("Internet connection not found.",
"Connect to the Internet and press the refresh button at the top", this);
mHasInternet = false;
}
else {
mHasInternet = true;
loadMapWhenOnline();
}
}
catch (GooglePlayServicesNotAvailableException e) {
PinItUtils.createAlert("We're sorry", "It seems that your phone doesn't have Google Play" +
"services installed, or is missing the maps application. Please download both of" +
"these and then try running this application", MainActivity.this);
}
}
private void loadMapWhenOnline() {
setUpMapIfNeeded();
if(mMemoryCache.get("defaultPhoto") == null) {
Bitmap defaultPhoto = ThumbnailUtils.extractThumbnail(
BitmapFactory.decodeResource(getResources(), R.drawable.default_image),
100, 100);
mMemoryCache.put("defaultPhoto", defaultPhoto);
}
LoadCurrentUserPhotoTask loadUserPhotoTask = new LoadCurrentUserPhotoTask();
loadUserPhotoTask.execute();
mMap.setOnCameraChangeListener(new OnCameraChangeListener() {
@Override
public void onCameraChange(CameraPosition position) {
if(!PinItUtils.isOnline(getApplicationContext())) {
PinItUtils.createAlert("Internet connection not found.",
"Connect to the Internet and press the refresh button at the top",
MainActivity.this);
mHasInternet = false;
hideNonRefreshMenuItems();
mMap.getUiSettings().setAllGesturesEnabled(false);
mMap.getUiSettings().setZoomControlsEnabled(false);
mMap.getUiSettings().setZoomGesturesEnabled(false);
}
else if(mMapEditMode == MapEditMode.DEFAULT_MODE) {
mHasInternet = true;
LatLngBounds mapBounds = mMap.getProjection().getVisibleRegion().latLngBounds;
LatLng southwest = mapBounds.southwest;
LatLng northeast = mapBounds.northeast;
Iterator<Marker> iterator = mMarkerList.iterator();
while(iterator.hasNext()) {
Marker marker = iterator.next();
if(!mapBounds.contains(marker.getPosition())) {
if(mFocusedMarker != null) {
if(marker.getId().equals(mFocusedMarker.getId())) {
mFocusedMarker.hideInfoWindow();
hideNoteEditButtons();
mFocusedMarker = null;
}
}
Note note = mNoteStore.get(marker.getId());
mNoteStore.remove(marker.getId());
mReverseNoteStore.remove(note.getNoteId());
marker.remove();
iterator.remove();
}
}
if(mMapViewMode == MapViewMode.YOUR_NOTES) {
LatLngTuple tuple = new LatLngTuple(southwest, northeast);
LoadNotesTask currentUserNotesTask = new LoadNotesTask(tuple, true);
currentUserNotesTask.execute();
}
else {
LatLngTuple tuple = new LatLngTuple(southwest, northeast);
LoadNotesTask currentUserNotesTask = new LoadNotesTask(tuple, false);
currentUserNotesTask.execute();
}
}
}
});
}
private void handleUsersFirstTime() {
final String userTutorialInstruction1 = "You can tilt the map by using a two-finger up or down swipe. Try it!";
final String userTutorialInstruction2 = "Click on a map pin to open a note preview";
LatLng timesSquare = new LatLng(40.758582795476215, -73.98525886237621);
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(timesSquare, 17.0f), 1500, new CancelableCallback() {
@Override
public void onFinish() {
Toast.makeText(getApplicationContext(), userTutorialInstruction1, Toast.LENGTH_LONG).show();
Toast.makeText(getApplicationContext(), userTutorialInstruction1, Toast.LENGTH_LONG).show();
Toast.makeText(getApplicationContext(), userTutorialInstruction2, Toast.LENGTH_LONG).show();
Toast.makeText(getApplicationContext(), userTutorialInstruction2, Toast.LENGTH_LONG).show();
PinItUtils.finishUsersFirstLogin(mCurrentUsername, getApplicationContext());
}
@Override
public void onCancel() {
}
});
}
private void setUpMapIfNeeded() {
if (mMap == null) {
mMap = ((SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.main_map))
.getMap();
if (mMap != null) {
//The name is random
Random itsIstanbulNotConstantinople = new Random();
double longitude = itsIstanbulNotConstantinople.nextInt(359) - 179;
mMap.moveCamera(CameraUpdateFactory.newLatLng(new LatLng(0,longitude)));
setUpMap();
}
}
}
private void setUpMap() {
mMap.setMyLocationEnabled(true);
mMap.setOnMapLongClickListener(this);
mMap.setOnMapClickListener(this);
mMap.setOnMarkerClickListener(this);
mMap.setOnInfoWindowClickListener(this);
mMap.getUiSettings().setCompassEnabled(false);
mMap.getUiSettings().setRotateGesturesEnabled(false);
mMap.setInfoWindowAdapter(new CustomInfoWindowAdapter(
getApplicationContext(),
mNoteStore,
mMemoryCache,
((PinItApplication) getApplication()).getImageLoader()));
}
class LoadCurrentUserPhotoTask extends AsyncTask<Void, Boolean, Void> {
@Override
protected Void doInBackground(Void... params) {
if(ParseUser.getCurrentUser().getBoolean("isDefaultPhoto")) {
Bitmap userPhoto = mMemoryCache.get("defaultPhoto");
if(userPhoto != null) {
mUserPhotoThumbnail = ThumbnailUtils.extractThumbnail(userPhoto, 100, 100);
mMemoryCache.put(mCurrentUsername, mUserPhotoThumbnail);
}
else {
mUserPhotoThumbnail = ThumbnailUtils.extractThumbnail(
BitmapFactory.decodeResource(getResources(), R.drawable.default_image),
100, 100);
mMemoryCache.put(mCurrentUsername, mUserPhotoThumbnail);
}
}
else {
try {
FileInputStream inputStream = openFileInput(mCurrentUsername + ".png");
mUserPhotoThumbnail = BitmapFactory.decodeStream(inputStream);
mMemoryCache.put(mCurrentUsername, mUserPhotoThumbnail);
}
catch(FileNotFoundException e) {
loadAndCacheProfilePicture();
}
}
return null;
}
private void loadAndCacheProfilePicture() {
ParseFile userPhotoFile =
ParseUser.getCurrentUser().getParseFile("profilePhotoThumbnail");
userPhotoFile.getDataInBackground(new GetDataCallback() {
@Override
public void done(byte[] data, ParseException e) {
if(e == null) {
mUserPhotoThumbnail = BitmapFactory.decodeByteArray(data, 0, data.length);
mMemoryCache.put(mCurrentUsername, mUserPhotoThumbnail);
try {
FileOutputStream outputStream =
openFileOutput(mCurrentUsername, Context.MODE_PRIVATE);
mUserPhotoThumbnail.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
outputStream.close();
publishProgress(true);
}
catch(FileNotFoundException e1) {
publishProgress(false);
}
catch(IOException e1) {
publishProgress(false);
}
}
else {
publishProgress(false);
}
}
});
}
@Override
protected void onProgressUpdate(Boolean... params) {
super.onProgressUpdate(params);
if(!params[0]) {
PinItUtils.createAlert("This is embarrassing",
"We couldn't load your notes this time, please try again", MainActivity.this);
}
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
if(mMapViewMode == MapViewMode.YOUR_NOTES) {
LatLng southwest = mMap.getProjection().getVisibleRegion().latLngBounds.southwest;
LatLng northeast = mMap.getProjection().getVisibleRegion().latLngBounds.northeast;
LatLngTuple tuple = new LatLngTuple(southwest, northeast);
LoadNotesTask currentUserNotesTask = new LoadNotesTask(tuple, true);
currentUserNotesTask.execute();
}
}
}
class LoadNotesTask extends AsyncTask<Void, Void, List<ParseObject>> {
private LatLngTuple tuple;
private String errorMessage;
private final String PARSE_BOX_LATITUDE_ERROR = "Box latitude height below precision limit.";
private boolean loadCurrentUserNotes;
public LoadNotesTask(LatLngTuple tuple, boolean loadCurrentUserNotes) {
this.tuple = tuple;
this.loadCurrentUserNotes = loadCurrentUserNotes;
}
@Override
protected List<ParseObject> doInBackground(Void... params) {
try {
ParseQuery query = new ParseQuery("Note");
if(loadCurrentUserNotes) {
query.whereEqualTo("creator", mCurrentUsername);
}
else {
query.whereEqualTo("visibility", "public");
}
LatLng southwest = tuple.getSouthwest();
LatLng northeast = tuple.getNortheast();
boolean isProximateToIDLine = isProximateToIDLine(southwest, northeast);
if(northeast.longitude < southwest.longitude && !isProximateToIDLine) {
query.whereWithinGeoBox("geopoint",
new ParseGeoPoint(southwest.latitude, southwest.longitude),
new ParseGeoPoint(northeast.latitude, 179.9));
query.setLimit(5);
ParseQuery postIDLineQuery = new ParseQuery("Note");
if(loadCurrentUserNotes) {
postIDLineQuery.whereEqualTo("creator", mCurrentUsername);
}
else {
postIDLineQuery.whereNotEqualTo("creator", mCurrentUsername);
}
postIDLineQuery.whereWithinGeoBox("geopoint",
new ParseGeoPoint(southwest.latitude, -179.9),
new ParseGeoPoint(northeast.latitude, northeast.longitude));
postIDLineQuery.setLimit(5);
List<ParseObject> preIDLineNotes = query.find();
List<ParseObject> postIDLineNotes = postIDLineQuery.find();
preIDLineNotes.addAll(postIDLineNotes);
return preIDLineNotes;
}
else if(!isProximateToIDLine){
query.whereWithinGeoBox("geopoint",
new ParseGeoPoint(southwest.latitude, southwest.longitude),
new ParseGeoPoint(northeast.latitude, northeast.longitude));
query.setLimit(10);
return query.find();
}
return new ArrayList<ParseObject>();
} catch (ParseException e) {
errorMessage = e.getMessage();
return null;
}
}
private boolean isProximateToIDLine(LatLng southwest, LatLng northeast) {
if((180 - southwest.longitude) < 0.1 || (northeast.longitude + 180) < 0.1) {
return true;
}
return false;
}
@Override
protected void onPostExecute(List<ParseObject> noteList) {
super.onPostExecute(noteList);
if(noteList == null && !errorMessage.equals(PARSE_BOX_LATITUDE_ERROR)) {
PinItUtils.createAlert("This is embarrassing",
"We couldn't load your notes please try again", MainActivity.this);
}
else {
if(noteList != null) {
for(ParseObject note: noteList) {
final double latitude = note.getParseGeoPoint("geopoint").getLatitude();
final double longitude = note.getParseGeoPoint("geopoint").getLongitude();
String noteTitle = note.getString("title");
String noteBody = note.getString("body");
String noteCreator = note.getString("creator");
boolean hasPhoto = note.getBoolean("hasPhoto");
String date = note.getCreatedAt().toString();
String[] arr = date.split("\\s");
String noteCreatedAt = arr[1] + " " + arr[2] + ", " + arr[5];
String noteCreatedAtFull = date;
String noteImageThumbnailUrl;
if(hasPhoto) {
noteImageThumbnailUrl = note.getParseFile("notePhotoThumbnail").getUrl();
}
else {
noteImageThumbnailUrl = new String();
}
ParseGeoPoint noteParseGeoPoint = note.getParseGeoPoint("geopoint");
LatLng noteGeoPoint = new LatLng(noteParseGeoPoint.getLatitude(),
noteParseGeoPoint.getLongitude());
Note userNote = new Note(note.getObjectId(), noteCreator, noteTitle, noteBody,
noteGeoPoint, noteImageThumbnailUrl, noteCreatedAt, noteCreatedAtFull);
createMarkerAtLocation(latitude, longitude, userNote);
}
}
setProgressBarIndeterminateVisibility(false);
}
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode) {
case REQUEST_CODE_CREATE_NOTE:
if(resultCode != RESULT_OK) {
mMapEditMode = MapEditMode.DEFAULT_MODE;
mMapViewMode = MapViewMode.YOUR_NOTES;
mMenu.findItem(R.id.action_create_note).setVisible(true);
mMenu.findItem(R.id.action_cancel).setVisible(false);
LatLngBounds mapBounds = mMap.getProjection().getVisibleRegion().latLngBounds;
LatLng southwest = mapBounds.southwest;
LatLng northeast = mapBounds.northeast;
LatLngTuple tuple = new LatLngTuple(southwest, northeast);
LoadNotesTask currentUserNotesTask = new LoadNotesTask(tuple, true);
currentUserNotesTask.execute();
return;
}
if(PinItUtils.isUsersFirstNoteCreate(mCurrentUsername, getApplicationContext())) {
String userTutorialInstruction = "You can always edit your note by clicking on the pin " +
"and then clicking the 'Edit' button in the menu above";
Toast.makeText(getApplicationContext(), userTutorialInstruction, Toast.LENGTH_LONG).show();
PinItUtils.finishUsersFirstNoteCreate(mCurrentUsername, getApplicationContext());
}
mMapEditMode = MapEditMode.DEFAULT_MODE;
mMapViewMode = MapViewMode.YOUR_NOTES;
mMenu.findItem(R.id.action_create_note).setVisible(true);
mMenu.findItem(R.id.action_cancel).setVisible(false);
double latitude = Double.parseDouble(data.getStringExtra("geopoint").split(",")[0]);
double longitude = Double.parseDouble(data.getStringExtra("geopoint").split(",")[1]);
String noteTitle = data.getStringExtra("noteTitle");
String noteBody = data.getStringExtra("noteBody");
String noteImageThumbnailUrl = data.getStringExtra("noteImageThumbnailUrl");
String noteCreatedAt = data.getStringExtra("noteCreatedAt");
String noteCreatedAtFull = data.getStringExtra("noteCreatedAtFull");
String noteId = data.getStringExtra("noteId");
LatLng noteGeoPoint = new LatLng(latitude, longitude);
Note note = new Note(noteId, mCurrentUsername, noteTitle, noteBody, noteGeoPoint,
noteImageThumbnailUrl, noteCreatedAt, noteCreatedAtFull);
createMarkerAtLocation(latitude, longitude, note);
break;
case REQUEST_CODE_EDIT_NOTE:
if(resultCode == RESULT_OK) {
mFocusedMarker.hideInfoWindow();
Note editedNote = (Note) data.getExtras().getParcelable("note");
mMemoryCache.remove(editedNote.getNoteId());
mNoteStore.put(mFocusedMarker.getId(), editedNote);
mFocusedMarker.showInfoWindow();
}
break;
case REQUEST_CODE_CHANGE_SETTINGS:
if(resultCode == RESULT_OK) {
if(data.getBooleanExtra("wasPhotoChanged", false)) {
mMemoryCache.remove(mCurrentUsername);
FileInputStream inputStream = null;
try {
inputStream = openFileInput(mCurrentUsername + ".png");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
mUserPhotoThumbnail = BitmapFactory.decodeStream(inputStream);
mMemoryCache.put(mCurrentUsername, mUserPhotoThumbnail);
}
}
}
}
private void createMarkerAtLocation(final double latitude, final double longitude, final Note note) {
if(mReverseNoteStore.get(note.getNoteId()) == null) {
Bitmap balloonBackground = BitmapFactory.decodeResource(
getResources(), R.drawable.balloon_background);
Bitmap userPhoto = null;
if(note.getNoteCreator().equals(mCurrentUsername)) {
balloonBackground = Bitmap.createScaledBitmap(balloonBackground, 87, 94, false);
userPhoto = Bitmap.createScaledBitmap(mUserPhotoThumbnail, 75, 71, false);
Canvas canvas = new Canvas(balloonBackground);
canvas.drawBitmap(balloonBackground, 0, 0, null);
canvas.drawBitmap(userPhoto, 6, 6, null);
addMarkerToMap(note, balloonBackground, latitude, longitude);
}
else {
if(mMemoryCache.get(note.getNoteCreator()) != null) {
balloonBackground = Bitmap.createScaledBitmap(balloonBackground, 87, 94, false);
userPhoto = Bitmap.createScaledBitmap(
mMemoryCache.get(note.getNoteCreator()), 75, 71, false);
Canvas canvas = new Canvas(balloonBackground);
canvas.drawBitmap(balloonBackground, 0, 0, null);
canvas.drawBitmap(userPhoto, 6, 6, null);
addMarkerToMap(note, balloonBackground, latitude, longitude);
}
else {
ParseQuery query = ParseUser.getQuery();
query.whereEqualTo("username", note.getNoteCreator());
query.getFirstInBackground(new GetCallback() {
@Override
public void done(ParseObject object, ParseException e) {
if(e == null) {
ParseObject noteCreator = object;
if(noteCreator.getBoolean("isDefaultPhoto")) {
Bitmap balloonBackground = BitmapFactory.decodeResource(
getResources(), R.drawable.balloon_background);
Bitmap userPhoto = null;
balloonBackground = Bitmap.createScaledBitmap(
balloonBackground, 87, 94, false);
userPhoto = Bitmap.createScaledBitmap(
mMemoryCache.get("defaultPhoto"), 75, 71, false);
note.setNoteCreatorHasDefaultPhoto(true);
Canvas canvas = new Canvas(balloonBackground);
canvas.drawBitmap(balloonBackground, 0, 0, null);
canvas.drawBitmap(userPhoto, 6, 6, null);
addMarkerToMap(note, balloonBackground, latitude, longitude);
}
else {
ParseFile userPhotoFile =
noteCreator.getParseFile("profilePhotoThumbnail");
userPhotoFile.getDataInBackground(new GetDataCallback() {
@Override
public void done(byte[] data, ParseException e) {
if(e == null) {
Bitmap balloonBackground = BitmapFactory.decodeResource(
getResources(), R.drawable.balloon_background);
Bitmap userPhoto = BitmapFactory.decodeByteArray(
data, 0, data.length);
mMemoryCache.put(note.getNoteCreator(), userPhoto);
balloonBackground = Bitmap.createScaledBitmap(
balloonBackground, 87, 94, false);
userPhoto = Bitmap.createScaledBitmap(userPhoto, 75, 71, false);
Canvas canvas = new Canvas(balloonBackground);
canvas.drawBitmap(balloonBackground, 0, 0, null);
canvas.drawBitmap(userPhoto, 6, 6, null);
addMarkerToMap(note, balloonBackground, latitude, longitude);
}
else {
Toast.makeText(getApplicationContext(), "Photo load failed", Toast.LENGTH_LONG).show();
PinItUtils.createAlert("This is embarrassing",
"Please log out and login again", MainActivity.this);
}
}
});
}
}
else {
Toast.makeText(getApplicationContext(), note.getNoteCreator(), Toast.LENGTH_LONG).show();
PinItUtils.createAlert("This is embarrassing",
"Please log out and login again", MainActivity.this);
}
}
});
}
}
}
}
public void addMarkerToMap(Note note, Bitmap balloonBackground, double latitude, double longitude) {
LatLng geopoint = new LatLng(latitude, longitude);
Marker newMarker = mMap.addMarker(new MarkerOptions()
.position(geopoint)
.draggable(false)
.icon(BitmapDescriptorFactory.fromBitmap(balloonBackground)));
if(mNoteStore.get(newMarker.getId()) == null) {
mNoteStore.put(newMarker.getId(), note);
mReverseNoteStore.put(note.getNoteId(), newMarker.getId());
if(mMarkerList.size() == 20) {
Marker marker = mMarkerList.get(0);
if(mFocusedMarker != null) {
if(marker.getId() == mFocusedMarker.getId()) {
marker = mMarkerList.remove(1);
Note removeNote = mNoteStore.remove(marker.getId());
mReverseNoteStore.remove(removeNote.getNoteId());
marker.remove();
}
else {
marker = mMarkerList.remove(0);
Note removeNote = mNoteStore.remove(marker.getId());
mReverseNoteStore.remove(removeNote.getNoteId());
marker.remove();
}
}
else {
marker = mMarkerList.remove(0);
Note removeNote = mNoteStore.get(marker.getId());
mNoteStore.remove(marker.getId());
mReverseNoteStore.remove(removeNote.getNoteId());
marker.remove();
}
}
mMarkerList.add(newMarker);
}
}
@Override
public void onMapLongClick(LatLng point) {
if(mMapEditMode == MapEditMode.CREATE_NOTE) {
Intent intent = new Intent(this.getApplicationContext(), CreateNoteActivity.class);
intent.putExtra("geopoint", point.latitude + "," + point.longitude);
startActivityForResult(intent, REQUEST_CODE_CREATE_NOTE);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main_action_bar, menu);
mMenu = menu;
mSearchMenuItem = menu.findItem(R.id.action_search);
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
mSearchView = (SearchView) mSearchMenuItem.getActionView();
mSearchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
mSearchView.setQueryHint("Search for locations...");
mSearchView.setOnSuggestionListener(new OnSuggestionListener() {
@Override
public boolean onSuggestionSelect(int position) {
return false;
}
@Override
public boolean onSuggestionClick(int position) {
CursorAdapter adapter = mSearchView.getSuggestionsAdapter();
Cursor cursor = adapter.getCursor();
if(cursor != null) {
if(cursor.moveToPosition(position)) {
InputMethodManager imm = (InputMethodManager)getSystemService(
Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mSearchView.getWindowToken(), 0);
mSearchMenuItem.collapseActionView();
String geolocation = cursor.getString(
cursor.getColumnIndex(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID));
if(geolocation != null && !geolocation.isEmpty()) {
double latitude = Double.parseDouble(geolocation.split(",")[0]);
double longitude = Double.parseDouble(geolocation.split(",")[1]);
LatLng geopoint = new LatLng(latitude, longitude);
mMap.animateCamera(CameraUpdateFactory.newCameraPosition(
new CameraPosition(geopoint, 17, 0, 0)));
}
}
}
return true;
}
});
mSearchView.setOnQueryTextListener(new OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
CursorAdapter adapter = mSearchView.getSuggestionsAdapter();
Cursor cursor = adapter.getCursor();
if(cursor != null) {
if(cursor.moveToFirst()) {
InputMethodManager imm = (InputMethodManager)getSystemService(
Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mSearchView.getWindowToken(), 0);
mSearchMenuItem.collapseActionView();
String geolocation = cursor.getString(
cursor.getColumnIndex(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID));
if(geolocation != null && !geolocation.isEmpty()) {
double latitude = Double.parseDouble(geolocation.split(",")[0]);
double longitude = Double.parseDouble(geolocation.split(",")[1]);
LatLng geopoint = new LatLng(latitude, longitude);
mMap.animateCamera(CameraUpdateFactory.newCameraPosition(
new CameraPosition(geopoint, 17, 0, 0)));
}
}
}
return true;
}
@Override
public boolean onQueryTextChange(String newText) {
return false;
}
});
if(!mHasInternet) {
hideNonRefreshMenuItems();
}
else {
showNonRefreshMenuItems();
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_search:
final MenuItem createNoteItem = mMenu.getItem(1);
createNoteItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
item.setOnActionExpandListener(new OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
return true;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
createNoteItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
return true;
}
});
break;
case R.id.action_create_note:
mMapEditMode = MapEditMode.CREATE_NOTE;
hideNoteEditButtons();
mMenu.findItem(R.id.action_create_note).setVisible(false);
mMenu.findItem(R.id.action_cancel).setVisible(true).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
Toast.makeText(getApplicationContext(),
"Press and hold the location where you'd like to create a note",
Toast.LENGTH_SHORT).show();
break;
case R.id.action_logout:
if(ParseFacebookUtils.getSession() != null) {
ParseFacebookUtils.getSession().closeAndClearTokenInformation();
}
ParseUser.logOut();
finish();
break;
case R.id.action_refresh:
if(!PinItUtils.isOnline(getApplicationContext())) {
PinItUtils.createAlert("Internet connection not found.",
"Connect to the Internet and press the refresh button at the top", this);
mHasInternet = false;
hideNonRefreshMenuItems();
}
else {
mHasInternet = true;
showNonRefreshMenuItems();
loadMapWhenOnline();
mMap.getUiSettings().setAllGesturesEnabled(true);
mMap.getUiSettings().setZoomControlsEnabled(true);
mMap.getUiSettings().setZoomGesturesEnabled(true);
}
break;
case R.id.action_edit_location:
if(mFocusedMarker != null) {
Toast.makeText(getApplicationContext(),
"Press and hold the current note and then drag it to where " +
"you'd like to position it and click save", Toast.LENGTH_LONG).show();
mMenu.findItem(R.id.action_save).setVisible(true);
mMenu.findItem(R.id.action_cancel).setVisible(true);
mMenu.findItem(R.id.action_create_note).setVisible(false);
mMenu.findItem(R.id.action_search).setVisible(false);
mMenu.findItem(R.id.action_edit).setVisible(false);
for(Marker marker: mMarkerList) {
marker.setDraggable(false);
}
mFocusedMarker.setDraggable(true);
}
else {
Toast.makeText(getApplicationContext(), "Please click on the note you'd like " +
"to move and then try again", Toast.LENGTH_SHORT).show();
}
break;
case R.id.action_save:
if(mHasInternet) {
setProgressBarIndeterminateVisibility(true);
ParseQuery query = new ParseQuery("Note");
query.getInBackground(mNoteStore.get(mFocusedMarker.getId()).getNoteId(), new GetCallback() {
public void done(ParseObject object, ParseException e) {
if (e == null) {
ParseGeoPoint geopoint = new ParseGeoPoint(
mFocusedMarker.getPosition().latitude,
mFocusedMarker.getPosition().longitude);
object.put("geopoint", geopoint);
object.saveInBackground(new SaveCallback() {
@Override
public void done(ParseException e) {
if(e == null) {
setProgressBarIndeterminateVisibility(false);
mFocusedMarker.setDraggable(false);
hideNoteEditButtons();
mMenu.findItem(R.id.action_edit).setVisible(true);
Toast.makeText(getApplicationContext(), "Note location updated!",
Toast.LENGTH_LONG).show();
}
else {
PinItUtils.createAlert("This note could not be updated.",
"We apologize, this note update failed, please try again",
MainActivity.this);
}
}
});
} else {
PinItUtils.createAlert("This note could not be updated.",
"We apologize, this note update failed, please try again",
MainActivity.this);
}
}
});
}
break;
case R.id.action_delete_note:
if(mFocusedMarker != null) {
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setTitle("Are you sure?");
dialog.setMessage("Please confirm that you'd like to delete this note.");
dialog.setPositiveButton("Delete", new DialogInterface.OnClickListener(){
public void onClick(DialogInterface arg0, int arg1) {
final Note note = mNoteStore.get(mFocusedMarker.getId());
ParseQuery query = new ParseQuery("Note");
query.getInBackground(note.getNoteId(), new GetCallback() {
@Override
public void done(ParseObject object, ParseException e) {
if(e == null) {
object.deleteInBackground(new DeleteCallback() {
@Override
public void done(ParseException e) {
if(e == null) {
Toast.makeText(getApplicationContext(),
"Note deleted!", Toast.LENGTH_SHORT).show();
if(mFocusedMarker != null) {
mNoteStore.remove(mFocusedMarker.getId());
mReverseNoteStore.remove(note.getNoteId());
Iterator<Marker> iterator = mMarkerList.iterator();
while(iterator.hasNext()) {
Marker marker = iterator.next();
if(marker.getId().equals(mFocusedMarker.getId())) {
mFocusedMarker.remove();
mFocusedMarker = null;
iterator.remove();
break;
}
}
hideNoteEditButtons();
}
}
}
});
}
else {
PinItUtils.createAlert("Something's gone wrong",
"Please try deleting the note again", MainActivity.this);
}
}
});
}
});
dialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
return;
}
});
dialog.show();
}
break;
case R.id.action_edit_note:
if(mFocusedMarker != null) {
Note note = mNoteStore.get(mFocusedMarker.getId());
Intent intent = new Intent(this, EditNoteActivity.class);
intent.putExtra("note", note);
startActivityForResult(intent, REQUEST_CODE_EDIT_NOTE);
}
else {
Toast.makeText(getApplicationContext(), "Please click on the note you'd like " +
"to edit and then try again", Toast.LENGTH_SHORT).show();
}
break;
case R.id.action_cancel:
if(mFocusedMarker != null) {
double latitude = mNoteStore.get(mFocusedMarker.getId()).getNoteLatitude();
double longitude = mNoteStore.get(mFocusedMarker.getId()).getNoteLongitude();
LatLng geopoint = new LatLng(latitude, longitude);
mFocusedMarker.setPosition(geopoint);
mFocusedMarker.setDraggable(false);
mFocusedMarker.hideInfoWindow();
}
if(mMapEditMode == MapEditMode.CREATE_NOTE) {
mMapEditMode = MapEditMode.DEFAULT_MODE;
}
hideNoteEditButtons();
break;
case R.id.action_settings:
Intent settingsIntent = new Intent(this, SettingsActivity.class);
startActivityForResult(settingsIntent, REQUEST_CODE_CHANGE_SETTINGS);
break;
case R.id.action_about:
Intent intent = new Intent(this, AboutActivity.class);
startActivity(intent);
break;
default:
return super.onOptionsItemSelected(item);
}
return true;
}
private void hideNoteEditButtons() {
mMenu.findItem(R.id.action_edit).setVisible(false);
mMenu.findItem(R.id.action_save).setVisible(false);
mMenu.findItem(R.id.action_cancel).setVisible(false);
mMenu.findItem(R.id.action_search).setVisible(true);
mMenu.findItem(R.id.action_create_note).setVisible(true);
}
private void hideNonRefreshMenuItems() {
for(int i = 0; i < mMenu.size(); i++) {
if(mMenu.getItem(i).getItemId() != R.id.action_refresh) {
mMenu.getItem(i).setVisible(false);
}
else {
mMenu.getItem(i).setVisible(true);
}
}
}
private void showNonRefreshMenuItems() {
for(int i = 0; i < mMenu.size(); i++) {
if(mMenu.getItem(i).getItemId() != R.id.action_refresh &&
mMenu.getItem(i).getItemId() != R.id.action_save &&
mMenu.getItem(i).getItemId() != R.id.action_edit &&
mMenu.getItem(i).getItemId() != R.id.action_cancel) {
mMenu.getItem(i).setVisible(true);
}
else {
mMenu.getItem(i).setVisible(false);
}
}
}
@Override
public void onMarkerDrag(Marker marker) {
}
@Override
public void onMarkerDragEnd(Marker marker) {
}
@Override
public void onMarkerDragStart(Marker marker) {
mFocusedMarker = marker;
}
@Override
public boolean onMarkerClick(final Marker marker) {
if(mHasInternet) {
mFocusedMarker = marker;
if(PinItUtils.isUsersFirstMarkerClick(mCurrentUsername, getApplicationContext())) {
String userTutorialInstruction = "Now click on the window above the pin to view " +
"the full note!";
Toast.makeText(getApplicationContext(), userTutorialInstruction, Toast.LENGTH_LONG).show();
PinItUtils.finishUsersFirstMarkerClick(mCurrentUsername, getApplicationContext());
}
if(mNoteStore.get(marker.getId()).getNoteCreator().equals(mCurrentUsername)) {
if(!mMenu.findItem(R.id.action_save).isVisible()) {
mMenu.findItem(R.id.action_edit).setVisible(true);
}
}
else {
mMenu.findItem(R.id.action_edit).setVisible(false);
}
marker.showInfoWindow();
LatLng markerGeoPoint = marker.getPosition();
Point markerPoint = mMap.getProjection().toScreenLocation(markerGeoPoint);
Point poinToMoveCameraTo = new Point(markerPoint.x, markerPoint.y - 160);
LatLng newMarkerGeoPoint = mMap.getProjection().fromScreenLocation(poinToMoveCameraTo);
mMap.animateCamera(CameraUpdateFactory.newLatLng(newMarkerGeoPoint), 500, null);
}
return true;
}
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
@Override
public void onBackPressed() {
if(mMenu != null) {
mMenu.findItem(R.id.action_cancel).setVisible(false);
if(mMapEditMode == MapEditMode.CREATE_NOTE) {
hideNoteEditButtons();
mMenu.findItem(R.id.action_create_note).setVisible(true);
mMenu.findItem(R.id.action_search).setVisible(true);
mMapEditMode = MapEditMode.DEFAULT_MODE;
}
else if(mFocusedMarker != null) {
if(mFocusedMarker.isInfoWindowShown()) {
hideNoteEditButtons();
mFocusedMarker.hideInfoWindow();
mFocusedMarker = null;
}
}
else {
super.onBackPressed();
}
}
else {
super.onBackPressed();
}
}
@Override
public void onInfoWindowClick(Marker marker) {
hideNoteEditButtons();
Note note = mNoteStore.get(marker.getId());
if(note.getNoteCreator().equals(mCurrentUsername)) {
mMenu.findItem(R.id.action_edit).setVisible(true);
}
ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream();
if(note.getNoteCreatorHasDefaultPhoto()) {
mMemoryCache.get("defaultPhoto").compress(CompressFormat.PNG, 100, byteArrayStream);
}
else {
mMemoryCache.get(note.getNoteCreator()).compress(CompressFormat.PNG, 100, byteArrayStream);
}
Intent intent = new Intent(this, DisplayNoteActivity.class);
intent.putExtra("note", note);
intent.putExtra("userPhoto", byteArrayStream.toByteArray());
startActivityForResult(intent, REQUEST_CODE_DISPLAY_NOTE);
}
@Override
public void onMapClick(LatLng point) {
if(mFocusedMarker != null) {
if(mNoteStore.get(mFocusedMarker.getId()).getNoteCreator().equals(mCurrentUsername)) {
hideNoteEditButtons();
}
mFocusedMarker.hideInfoWindow();
mFocusedMarker = null;
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
@Override
public boolean onSearchRequested() {
if(mSearchMenuItem.isActionViewExpanded())
{
mSearchMenuItem.collapseActionView();
return false;
}
final MenuItem createNoteItem = mMenu.getItem(1);
createNoteItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
mSearchMenuItem.expandActionView();
mSearchMenuItem.setOnActionExpandListener(new OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
return true;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
createNoteItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
return true;
}
});
return false;
}
}