com.vishwa.pinit.MainActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.vishwa.pinit.MainActivity.java

Source

/*
 * 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;
    }

}