<file_sep>package com.droidko.voltfeed.activities;

import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.ActionBarActivity;

public abstract class VoltfeedActivity extends ActionBarActivity {

    //Vars
    protected Context mContext;
    private FragmentManager mFragmentManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(layout());

        mContext = this;

        init();
        setUi();
        initFragments();
        populate();
        setListeners();
    }

    protected abstract void initFragments();

    protected abstract int layout();

    protected abstract void setUi();

    protected abstract void setListeners();

    protected abstract void populate();

    protected abstract void init();
    
    public FragmentManager getVoltfeedFragmentManager() {
        if (mFragmentManager == null) mFragmentManager = super.getSupportFragmentManager();
        return mFragmentManager;
    }

    public Context getContext() {
        return mContext;
    }

    protected void replaceFragment(int resId, Fragment f) {
        getVoltfeedFragmentManager()
                .beginTransaction()
                .replace(resId, f)
                .commit();
    }

    protected void replaceFragment(int resId, Fragment f, String tag) {
        getVoltfeedFragmentManager()
                .beginTransaction()
                .replace(resId, f, tag)
                .commit();
    }

    public void addFragment(int resId, Fragment f) {
        getVoltfeedFragmentManager()
                .beginTransaction()
                .add(resId, f)
                .commit();
    }

    public void addFragment(int resId, Fragment f, String tag) {
        getVoltfeedFragmentManager()
                .beginTransaction()
                .add(resId, f, tag)
                .commit();
    }

    public void removeFragment(Fragment f) {
        getVoltfeedFragmentManager()
                .beginTransaction()
                .remove(f)
                .commit();
    }

}
<file_sep>package com.droidko.voltfeed.ui;

import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.widget.LinearLayout;

import com.droidko.voltfeed.ui.widget.SlidingTabs.SlidingTabLayout;

public class QuickReturnAnimation extends RecyclerView.OnScrollListener {

    private LinearLayout mToolbarWrapper;
    private Toolbar mToolbar;
    private SlidingTabLayout mSlidingTabLayout;

    public QuickReturnAnimation(LinearLayout toolbarWrapper,
                                Toolbar toolbar,
                                SlidingTabLayout slidingTabLayout) {
        this.mToolbarWrapper = toolbarWrapper;
        this.mToolbar = toolbar;
        this.mSlidingTabLayout = slidingTabLayout;
    }

    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);

        if (newState == RecyclerView.SCROLL_STATE_IDLE) {
            if (Math.abs(mToolbarWrapper.getTranslationY()) > mToolbar.getHeight()) {
                hideToolbar();
            } else {
                showToolbar();
            }
        }
    }

    private void showToolbar() {
        mToolbarWrapper
                .animate()
                .translationY(0)
                .start();
    }
    private void hideToolbar() {
        mToolbarWrapper
                .animate()
                .translationY(-mSlidingTabLayout.getBottom())
                .start();
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);

        scrollColoredViewParallax(dy);
        if (dy > 0) {
            hideToolbarBy(dy);
        } else {
            showToolbarBy(dy);
        }
    }

    private void scrollColoredViewParallax(int dy) {
        //Use this to create a Parallax effect with an image
        //parallaxImage.setTranslationY(parallaxImage.getTranslationY() — dy / 3);
    }
    private void hideToolbarBy(int dy) {
        if (cannotHideMoreToolbar(dy)) {
            mToolbarWrapper.setTranslationY(-mSlidingTabLayout.getBottom());
        } else {
            mToolbarWrapper.setTranslationY(mToolbarWrapper.getTranslationY() - dy);
        }
    }

    private boolean cannotHideMoreToolbar(int dy) {
        return Math.abs(mToolbarWrapper.getTranslationY() - dy) > mSlidingTabLayout.getBottom();
    }
    private void showToolbarBy(int dy) {
        if (cannotShowMoreToolbar(dy)) {
            mToolbarWrapper.setTranslationY(0);
        } else {
            mToolbarWrapper.setTranslationY(mToolbarWrapper.getTranslationY() - dy);
        }
    }
    private boolean cannotShowMoreToolbar(int dy) {
        return (mToolbarWrapper.getTranslationY() - dy) > 0 ;
    }

}
<file_sep>package com.droidko.voltfeed.utils;

import android.content.Context;
import android.net.Uri;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

import com.droidko.voltfeed.Config;
import com.droidko.voltfeed.R;
import com.droidko.voltfeed.Schema;
import com.droidko.voltfeed.events.EventDispatcher;
import com.droidko.voltfeed.model.Post;
import com.droidko.voltfeed.ui.timeline.TimelineIdeaPostViewHolder;
import com.droidko.voltfeed.ui.timeline.TimelineImagePostViewHolder;

import org.ocpsoft.pretty.time.PrettyTime;

public class TimelineHelper {

    public volatile static int sLastPosition = -1;

    public static RecyclerView.ViewHolder
    getTimelineViewHolder(int type,
                          Context context) {
        View itemLayoutView;
        switch (type) {

            //Case: Idea post
            case Schema.POST_COL_TYPE_IDEA:
                itemLayoutView = LayoutInflater.from(context)
                        .inflate(R.layout.fragment_timeline_post_idea, null);
                return new TimelineIdeaPostViewHolder(itemLayoutView);

            //Case: Image post
            case Schema.POST_COL_TYPE_IMAGE:
                itemLayoutView = LayoutInflater.from(context)
                        .inflate(R.layout.fragment_timeline_post_image, null);
                return new TimelineImagePostViewHolder(itemLayoutView);

            //Case default: Not a valid post type
            default:
                return null;
        }

    }

    public boolean populateTimelineViewHolder(final Post post,
                                              RecyclerView.ViewHolder viewHolder,
                                              int position) {
        switch (post.getType()) {

            //Case: Idea post
            case Schema.POST_COL_TYPE_IDEA:
                final TimelineIdeaPostViewHolder ideaPostViewHolder =
                        ((TimelineIdeaPostViewHolder) viewHolder);
                ideaPostViewHolder.mContent.setText(post.getText());
                UiHelper.setFontRoboto(ideaPostViewHolder.mContent);
                ideaPostViewHolder.mDate.setText(
                        new PrettyTime().format(post.getCreatedAt())
                );
                UiHelper.setFontVarela(ideaPostViewHolder.mDate);
                populateVoltViews(post,
                        ideaPostViewHolder.mVoltButton,
                        ideaPostViewHolder.mVoltsCounter);

                ideaPostViewHolder.mVoltButton.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        ApiHelper.voltPost(post, !view.isSelected());
                        populateVoltViews(post,
                                ideaPostViewHolder.mVoltButton,
                                ideaPostViewHolder.mVoltsCounter);
                        AnimationHelper.popButton(view);
                    }
                });

                ideaPostViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        //Do something on row click
                    }
                });

                animateViewIfNew(ideaPostViewHolder.mCard, position);

                return true;

            //Case: Image post
            case Schema.POST_COL_TYPE_IMAGE:
                final TimelineImagePostViewHolder imagePostViewHolder =
                        ((TimelineImagePostViewHolder) viewHolder);

                if (!TextUtils.isEmpty(post.getText())) {
                    imagePostViewHolder.mTitle.setText(post.getText());
                    UiHelper.setFontRoboto(imagePostViewHolder.mTitle);
                } else {
                    imagePostViewHolder.mTitle.setVisibility(View.GONE);
                }

                imagePostViewHolder.mDate.setText(
                        new PrettyTime().format(post.getCreatedAt())
                );

                UiHelper.setFontVarela(imagePostViewHolder.mDate);
                if (!TextUtils.isEmpty(post.getPicture())) {
                    final Uri fullResUri =
                            Uri.parse(ImagesHelper.getZoomableScreenImage(post.getPicture()));
                    final Uri standarResUri =
                            Uri.parse(ImagesHelper.getTimelineImage(post.getPicture()));

                    ImagesHelper.loadImageInTimeline(imagePostViewHolder.mPicture, post);

                    imagePostViewHolder.mPicture.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            EventDispatcher.dispatchTimelineImageClick(fullResUri.toString(),
                                    standarResUri.toString());
                        }
                    });
                }
                populateVoltViews(post,
                        imagePostViewHolder.mVoltButton,
                        imagePostViewHolder.mVoltsCounter);

                imagePostViewHolder.mVoltButton.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        ApiHelper.voltPost(post, !view.isSelected());
                        populateVoltViews(post,
                                imagePostViewHolder.mVoltButton,
                                imagePostViewHolder.mVoltsCounter);
                        AnimationHelper.popButton(view);
                    }
                });

                imagePostViewHolder.itemView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        //Do something on row click
                    }
                });

                animateViewIfNew(imagePostViewHolder.mCard, position);

                return true;

            //Case default: Not a valid post type
            default:
                return false;
        }
    }

    private void animateViewIfNew(View view, int newPosition) {
        if (newPosition > sLastPosition) {
            sLastPosition = newPosition;
            AnimationHelper.slideFromBelowWithFade(view.getRootView(),
                    Config.UI_TIMELINE_ANIMATION_DURATION);
        }
    }

    private static void populateVoltViews(Post post,
                                          View button,
                                          TextView counter) {
        if (ApiHelper.isPostVolted(post)) {
            ((TextView)button).setText(R.string.volted);
            button.setSelected(true);

        } else {
            ((TextView)button).setText(R.string.volt);
            button.setSelected(false);
        }
        counter.setText(String.valueOf(post.getVolts()));
    }

}
<file_sep>package com.droidko.voltfeed.events.innerEvents;

import java.util.HashSet;

public class OnVoltsPostsUpdate {

    private HashSet<String> voltedPostsSet;

    public OnVoltsPostsUpdate(HashSet<String> voltedPostsSet) {
        this.voltedPostsSet = voltedPostsSet;
    }

    public HashSet<String> getVoltedPostsSet() {
        return voltedPostsSet;
    }
}
<file_sep>package com.droidko.voltfeed.utils;

import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Environment;
import android.view.View;

import com.cloudinary.Transformation;
import com.droidko.voltfeed.Config;
import com.droidko.voltfeed.VoltfeedApp;
import com.droidko.voltfeed.model.Post;
import com.droidko.voltfeed.model.User;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.interfaces.DraweeController;
import com.facebook.drawee.view.SimpleDraweeView;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public class ImagesHelper {

    //SETTINGS
    private static final int QUALITY_HIGH = 90;
    private static final int QUALITY_MID = 80;
    private static final int QUALITY_LOW = 65;
    private static final int SIZE_THUMBNAIL = 100;
    private static final String CROP_LIMIT = "limit";
    private static final String CROP_THUMBNAIL = "thumb";
    private static final String PREFERED_FORMAT = ".webp";
    private static final int BITMAP_MAX_SIZE = 4048;
    private static final String PICTURES_TAKEN_PREFIX = "Voltfeed";

    //** Base dimensions
    public static int getBaseHeight() {
        return Math.min(Config.UI_SCREEN_BASE_HEIGHT, BITMAP_MAX_SIZE);
    }

    public static int getBaseWidth() {
        return Math.min(Config.UI_SCREEN_BASE_WIDTH, BITMAP_MAX_SIZE);
    }

    //** Start of CLOUDINARY **
    public static String getThumbnailImage(String cloudinaryImageId) {
        return VoltfeedApp.getCloudinary()
                .url()
                .transformation(new Transformation()
                        .width(SIZE_THUMBNAIL)
                        .height(SIZE_THUMBNAIL)
                        .crop(CROP_THUMBNAIL)
                        .quality(QUALITY_LOW))
                .generate(cloudinaryImageId + PREFERED_FORMAT);
    }


    public static String getTimelineImage(String cloudinaryImageId) {
        return VoltfeedApp.getCloudinary()
                .url()
                .transformation(new Transformation()
                        .width(Double.valueOf(getBaseWidth() * 0.9).intValue())
                        .height(Double.valueOf(getBaseHeight() * 0.6).intValue())
                        .crop(CROP_LIMIT)
                        .quality(QUALITY_MID))
                .generate(cloudinaryImageId + PREFERED_FORMAT);
    }

    public static String getFullScreenImage(String cloduinaryImageId) {
        return VoltfeedApp.getCloudinary()
                .url()
                .transformation(new Transformation()
                        .width(getBaseWidth())
                        .height(getBaseHeight())
                        .crop(CROP_LIMIT)
                        .quality(QUALITY_HIGH))
                .generate(cloduinaryImageId + PREFERED_FORMAT);
    }

    //WARNING: This is a very heavy request that can consume considerable bandwith. Caution advised.
    public static String getZoomableScreenImage(String cloduinaryImageId) {
        return VoltfeedApp.getCloudinary()
                .url()
                .transformation(new Transformation()
                        .width(getBaseWidth() * 2)
                        .height(getBaseHeight() * 2)
                        .crop(CROP_LIMIT)
                        .quality(QUALITY_HIGH))
                .generate(cloduinaryImageId + PREFERED_FORMAT);
    }
    //** End of CLOUDINARY **

    //** Start of FRESCO **
    public static void loadThumbAvatarImage(SimpleDraweeView simpleDraweeView, User user) {
        simpleDraweeView.setVisibility(View.VISIBLE);

        String cloudinaryAvatarUrl = getThumbnailImage(user.getAvatar());
        Uri avatarUri = Uri.parse(cloudinaryAvatarUrl);

        //Main request for the image to load
        ImageRequest avatarImageRequest = ImageRequestBuilder.newBuilderWithSource(avatarUri)
                .setLocalThumbnailPreviewsEnabled(true)
                .setProgressiveRenderingEnabled(true)
                .build();

        //Image loader controller
        DraweeController controller = Fresco.newDraweeControllerBuilder()
                .setImageRequest(avatarImageRequest)
                .setTapToRetryEnabled(false)
                .setOldController(simpleDraweeView.getController())
                .build();

        simpleDraweeView.setController(controller);
    }
    public static void loadImageInTimeline(SimpleDraweeView draweeView, Post post) {
        draweeView.setVisibility(View.VISIBLE);

        String cloudinaryPictureUrl = getTimelineImage(post.getPicture());
        Uri pictureUri = Uri.parse(cloudinaryPictureUrl);

        //Main request for the image to load
        ImageRequest highResRequest = ImageRequestBuilder.newBuilderWithSource(pictureUri)
                .setLocalThumbnailPreviewsEnabled(true)
                .setProgressiveRenderingEnabled(true)
                .build();

        //Image loader controller
        DraweeController controller = Fresco.newDraweeControllerBuilder()
                .setImageRequest(highResRequest)
                .setTapToRetryEnabled(true)
                .setOldController(draweeView.getController())
                .build();

        draweeView.setController(controller);
    }
    //** End of FRESCO **

    //** Start of UPLOAD IMAGE **
    public static class UploadImageTask extends AsyncTask<Uri, Void, Boolean> {

        public interface UploadFinishedCallback {
            void onUploadFinished(List<String> imagesIds);
        }

        UploadFinishedCallback mUploadFinishedCallback;
        List<String> mImagesIds = new LinkedList<String>();

        public UploadImageTask(UploadFinishedCallback uploadFinishedCallback) {
            this.mUploadFinishedCallback = uploadFinishedCallback;
        }

        @Override
        protected void onPostExecute(Boolean uploadResult) {
            super.onPostExecute(uploadResult);
            if (uploadResult) mUploadFinishedCallback.onUploadFinished(mImagesIds);
            else mUploadFinishedCallback.onUploadFinished(null);
        }

        @Override
        protected Boolean doInBackground(Uri... uris) {
            try {
                for (int i = 0; i < uris.length; i++) {
                    InputStream in = VoltfeedApp
                            .getContextInstance()
                            .getContentResolver()
                            .openInputStream(uris[i]);


                    //This method call uploads the image and returns a map with some upload
                    //params, including the id generated for this image
                    Map uploadParams = VoltfeedApp.getCloudinary().uploader().upload(in, null);
                    mImagesIds.add((String) uploadParams.get("public_id"));
                    int a = 0;
                }
                return true;
            } catch (IOException e) {
                e.printStackTrace();
                return false;
            }
        }
    }
    //** End of UPLOAD IMAGE **

    //** Start of STORE IMAGE **
    public static File createImageFile() throws IOException {
        // Create an image file name
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String imageFileName = PICTURES_TAKEN_PREFIX + "_" + timeStamp + "_";
        File storageDir = Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_DCIM);
        File image = File.createTempFile(
                imageFileName,  /* prefix */
                ".jpg",         /* suffix */
                storageDir      /* directory */
        );

        return image;
    }

    //This method adds a picture to the user's images gallery
    public static void galleryAddPic(Uri pictureUri) {
        Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
        mediaScanIntent.setData(pictureUri);
        VoltfeedApp.getContextInstance().sendBroadcast(mediaScanIntent);
    }
    //** End of STORE IMAGE **

}
<file_sep>package com.droidko.voltfeed.utils;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.droidko.voltfeed.Config;
import com.droidko.voltfeed.Schema;
import com.droidko.voltfeed.VoltfeedApp;
import com.droidko.voltfeed.model.Post;
import com.droidko.voltfeed.model.Volt;
import com.droidko.voltfeed.events.EventDispatcher;
import com.parse.FindCallback;
import com.parse.ParseException;
import com.parse.ParseObject;
import com.parse.ParseQuery;
import com.parse.ParseUser;

import java.util.Date;
import java.util.HashSet;
import java.util.List;

public class ApiHelper {

    //** Start of PUBLISH **
    private static Post publishGeneric(ParseObject parseObject) {
        ParseUser.getCurrentUser().increment(Schema.USER_COL_POSTS_CREATED_COUNT, +1);
        ParseUser.getCurrentUser().saveEventually();
        ParseUser.getCurrentUser().fetchInBackground();
        Post post = new Post(parseObject);
        EventDispatcher.dispatchPublish(post);
        return post;
    }

    public static Post publishIdea(String ideaText) {
        ParseObject parsePost = new ParseObject(Schema.POST_TABLE_NAME);
        parsePost.put(Schema.POST_COL_TYPE, Schema.POST_COL_TYPE_IDEA);
        parsePost.put(Schema.POST_COL_TEXT, ideaText);
        parsePost.put(Schema.POST_COL_USER_ID, ParseUser.getCurrentUser().getUsername());
        parsePost.saveEventually();

        return publishGeneric(parsePost);
    }

    public static Post publishImage(String imageText,String imageId) {
        ParseObject parsePost = new ParseObject(Schema.POST_TABLE_NAME);
        parsePost.put(Schema.POST_COL_TYPE, Schema.POST_COL_TYPE_IMAGE);
        parsePost.put(Schema.POST_COL_TEXT, imageText);
        parsePost.put(Schema.POST_COL_PICTURE, imageId);
        parsePost.put(Schema.POST_COL_USER_ID, ParseUser.getCurrentUser().getUsername());
        parsePost.saveEventually();

        return publishGeneric(parsePost);
    }
    //** End of PUBLISH **

    //** Start of TIMELINE **
    public static void getOlderTimelinePosts(@Nullable Date fromPostDate, FindCallback findCallback) {
        ParseQuery<ParseObject> parseQuery = ParseQuery.getQuery(Schema.POST_TABLE_NAME);
        parseQuery.orderByDescending(Schema.COL_CREATED_AT);
        if (fromPostDate != null) parseQuery.whereLessThan(Schema.COL_CREATED_AT, fromPostDate);
        parseQuery.setLimit(Config.PERFORMANCE_API_PAGE_SIZE);
        //parseQuery.setMaxCacheAge(Config.PERFORMANCE_CACHE_MAX_AGE);
        //parseQuery.setCachePolicy(ParseQuery.CachePolicy.CACHE_ELSE_NETWORK);
        parseQuery.findInBackground(findCallback);
    }

    public static void getNewerTimelinePosts(@NonNull Date latestPostDate, FindCallback findCallback) {
        ParseQuery<ParseObject> parseQuery = ParseQuery.getQuery(Schema.POST_TABLE_NAME);
        parseQuery.orderByAscending(Schema.COL_CREATED_AT);
        parseQuery.whereGreaterThan(Schema.COL_CREATED_AT, latestPostDate);
        parseQuery.setLimit(Config.PERFORMANCE_API_PAGE_SIZE);
        //parseQuery.setMaxCacheAge(Config.PERFORMANCE_CACHE_MAX_AGE);
        //parseQuery.setCachePolicy(ParseQuery.CachePolicy.CACHE_ELSE_NETWORK);
        parseQuery.findInBackground(findCallback);
    }
    //** End of TIMELINE **
    public static void getOlderConnectionsUsers(@Nullable Date fromPostDate, FindCallback findCallback) {
        ParseQuery<ParseUser> parseQuery = ParseUser.getQuery();
        parseQuery.orderByDescending(Schema.COL_CREATED_AT);
        if (fromPostDate != null) parseQuery.whereLessThan(Schema.COL_CREATED_AT, fromPostDate);
        parseQuery.setLimit(Config.PERFORMANCE_API_PAGE_SIZE);
        //parseQuery.setMaxCacheAge(Config.PERFORMANCE_CACHE_MAX_AGE);
        //parseQuery.setCachePolicy(ParseQuery.CachePolicy.CACHE_ELSE_NETWORK);
        parseQuery.findInBackground(findCallback);
    }

    public static void getNewerConnectionsUsers(@NonNull Date latestPostDate, FindCallback findCallback) {
        ParseQuery<ParseUser> parseQuery = ParseUser.getQuery();
        parseQuery.orderByAscending(Schema.COL_CREATED_AT);
        parseQuery.whereGreaterThan(Schema.COL_CREATED_AT, latestPostDate);
        parseQuery.setLimit(Config.PERFORMANCE_API_PAGE_SIZE);
        //parseQuery.setMaxCacheAge(Config.PERFORMANCE_CACHE_MAX_AGE);
        //parseQuery.setCachePolicy(ParseQuery.CachePolicy.CACHE_ELSE_NETWORK);
        parseQuery.findInBackground(findCallback);
    }
    //** Start of CONNECTIONS **

    //** End of CONNECTIONS *

    //** Start of VOLTS **
    private static HashSet<String> sVoltedPostsSet = new HashSet<String>();

    public static Volt voltPost(Post post, boolean isVolted) {
        ParseObject parsePost = ParseObject.createWithoutData(Schema.POST_TABLE_NAME, post.getId());
        Volt volt = new Volt(post.getId(), isVolted);
        if (volt.getState()) {
            post.setVolts(post.getVolts() + 1);
            getVoltedPosts().add(post.getId());
            parsePost.increment(Schema.POST_COL_VOLTS, +1);
            ParseUser.getCurrentUser().getRelation(Schema.USER_COL_VOLTS_POSTS).add(parsePost);
            ParseUser.getCurrentUser().increment(Schema.USER_COL_VOLTS_POSTS_COUNT, +1);
        } else {
            post.setVolts(post.getVolts() - 1);
            getVoltedPosts().remove(post.getId());
            parsePost.increment(Schema.POST_COL_VOLTS, -1);
            ParseUser.getCurrentUser().getRelation(Schema.USER_COL_VOLTS_POSTS).remove(parsePost);
            ParseUser.getCurrentUser().increment(Schema.USER_COL_VOLTS_POSTS_COUNT, -1);
        }
        parsePost.saveEventually();
        ParseUser.getCurrentUser().saveEventually();
        ParseUser.getCurrentUser().fetchInBackground();
        EventDispatcher.dispatchVolt(volt);
        EventDispatcher.dispatchVoltsPostsUpdate(getVoltedPosts());
        return volt;
    }

    public static void initVoltedPosts() {
        fetchVoltedPosts((new FindCallback<ParseObject>() {
            public void done(List<ParseObject> results, ParseException e) {
                if (e != null) {
                    UiHelper.showParseError(VoltfeedApp.getContextInstance(), e);
                } else {
                    populateSet(results);
                    EventDispatcher.dispatchVoltsPostsUpdate(getVoltedPosts());
                }
            }

            private void populateSet(List<ParseObject> results) {
                sVoltedPostsSet.clear();
                for (ParseObject parseObject : results) {
                    sVoltedPostsSet.add(parseObject.getObjectId());
                }
            }
        }));
    }

    public static HashSet<String> getVoltedPosts() {
        return sVoltedPostsSet;
    }

    public static boolean isPostVolted(Post post) {
        return getVoltedPosts().contains(post.getId());
    }

    //Any callback passed to this method will be called twice, first for the catched
    //results and later with the results from the network!
    protected static void fetchVoltedPosts(FindCallback<ParseObject> findCallback) {
        if (ParseUser.getCurrentUser() == null) return;
        ParseUser.getCurrentUser().fetchInBackground();
        ParseQuery<ParseObject> parseQuery =
                ParseUser
                        .getCurrentUser()
                        .getRelation(Schema.USER_COL_VOLTS_POSTS)
                        .getQuery();

        parseQuery.setCachePolicy(ParseQuery.CachePolicy.CACHE_THEN_NETWORK);
        parseQuery.findInBackground(findCallback);
    }
    //** End of VOLTS **
}
<file_sep>package com.droidko.voltfeed.fragments;

import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.droidko.voltfeed.R;
import com.droidko.voltfeed.Schema;
import com.droidko.voltfeed.events.innerEvents.OnPublishEvent;
import com.droidko.voltfeed.events.innerEvents.OnVoltEvent;
import com.droidko.voltfeed.utils.ImagesHelper;
import com.droidko.voltfeed.utils.UiHelper;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.interfaces.DraweeController;
import com.facebook.drawee.view.SimpleDraweeView;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import com.parse.ParseUser;

import de.greenrobot.event.EventBus;

public class ProfileFragment extends Fragment {

    private TextView mTextViewUsername;
    private TextView mTextViewFollowers;
    private TextView mTextViewFollowersTitle;
    private TextView mTextViewVolts;
    private TextView mTextViewVoltsTitle;
    private TextView mTextViewPosts;
    private TextView mTextViewPostsTitle;
    private SimpleDraweeView mDraweeViewAvatar;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        return inflater.inflate(R.layout.fragment_profile, container, false);
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EventBus.getDefault().register(this);
    }

    @Override
    public void onDestroy() {
        EventBus.getDefault().unregister(this);
        super.onStop();
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        mTextViewUsername =
                (TextView) view.findViewById(R.id.fragment_profile_text_view_username);
        mTextViewFollowers =
                (TextView) view.findViewById(R.id.fragment_profile_text_view_followers);
        mTextViewFollowersTitle =
                (TextView) view.findViewById(R.id.fragment_profile_text_view_followers_title);
        mTextViewVolts =
                (TextView) view.findViewById(R.id.fragment_profile_text_view_volts);
        mTextViewVoltsTitle =
                (TextView) view.findViewById(R.id.fragment_profile_text_view_volts_title);
        mTextViewPosts =
                (TextView) view.findViewById(R.id.fragment_profile_text_view_posts);
        mTextViewPostsTitle =
                (TextView) view.findViewById(R.id.fragment_profile_text_view_posts_title);
        mDraweeViewAvatar =
                (SimpleDraweeView) view.findViewById(R.id.fragment_profile_image_view_avatar);

        UiHelper.setFontVarela(mTextViewFollowersTitle);
        UiHelper.setFontVarela(mTextViewVoltsTitle);
        UiHelper.setFontVarela(mTextViewPostsTitle);

        populateUi();
        loadAvatarImage();
    }

    private void populateUi() {
        mTextViewUsername.setText(ParseUser.getCurrentUser().getString(Schema.USER_COL_USERNAME));
        UiHelper.setFontVarela(mTextViewUsername);
        mTextViewFollowers.setText(
                String.valueOf(
                        ParseUser.getCurrentUser().getInt(Schema.USER_COL_FOLLOWERS_USERS_COUNT)));
        mTextViewVolts.setText(
                String.valueOf(
                        ParseUser.getCurrentUser().getInt(Schema.USER_COL_VOLTS_POSTS_COUNT)));
        mTextViewPosts.setText(
                String.valueOf(
                        ParseUser.getCurrentUser().getInt(Schema.USER_COL_POSTS_CREATED_COUNT)));
    }

    private void loadAvatarImage() {
        mDraweeViewAvatar.setVisibility(View.VISIBLE);

        String cloudinaryAvatarUrl = ImagesHelper
                .getFullScreenImage(ParseUser.getCurrentUser().getString(Schema.USER_COL_PICTURE));
        Uri avatarUri = Uri.parse(cloudinaryAvatarUrl);

        //Main request for the image to load
        ImageRequest avatarImageRequest = ImageRequestBuilder.newBuilderWithSource(avatarUri)
                .setLocalThumbnailPreviewsEnabled(true)
                .setProgressiveRenderingEnabled(true)
                .build();

        //Image loader controller
        DraweeController controller = Fresco.newDraweeControllerBuilder()
                .setImageRequest(avatarImageRequest)
                .setTapToRetryEnabled(true)
                .setOldController(mDraweeViewAvatar.getController())
                .build();

        mDraweeViewAvatar.setController(controller);
    }

    //** EVENT BUS **
    public void onEvent(OnPublishEvent event){
        populateUi();
    }

    public void onEvent(OnVoltEvent event){
        populateUi();
    }
    //** End of EVENT BUS **
}
<file_sep>package com.droidko.voltfeed.ui;

import android.app.Dialog;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;

import com.droidko.voltfeed.R;

public class ConnectingDialog extends DialogFragment {

    public static final String TAG = "spinner_fragment_tag";

    @Override
    public Dialog onCreateDialog(final Bundle savedInstanceState) {

        ProgressDialog dialog = new ProgressDialog(getActivity(), R.style.DialogStyle);
        dialog.setMessage(getActivity().getString(R.string.progressdialog_connecting));
        dialog.setCancelable(false);
        dialog.setCanceledOnTouchOutside(false);

        return dialog;
    }
}
<file_sep>package com.droidko.voltfeed.ui.widget.RecyclerView.modules;

import com.droidko.voltfeed.ui.widget.RecyclerView.RecyclerAdapter;
import com.droidko.voltfeed.ui.widget.RecyclerView.RecyclerItem;

import java.util.ArrayList;

public class ModUtils<E extends RecyclerItem> {

    public interface ModUtilsInterface<E> {
        public int insertOrderedRow(ArrayList<Integer> itemsList, int value);

        public int removeOrderedRow(ArrayList<Integer> itemsList, int positionToRemove);
    }

    //Vars
    RecyclerAdapter<E> mRecyclerViewAdapter;

    //Constructor
    public ModUtils(RecyclerAdapter<E> aRecyclerViewAdapter) {
        this.mRecyclerViewAdapter = aRecyclerViewAdapter;
    }

    //** Start of UTILS MODULE methods **
    public static int insertOrderedRow(ArrayList<Integer> itemsList, int valueToInsert) {
        int i = 0;
        int size = itemsList.size();
        while (i < size && itemsList.get(i) < valueToInsert) {
            i++;
        }
        if (i < size) {
            itemsList.add(itemsList.get(size - 1) + 1);
            for (int j = size - 1; j > i; j--) {
                itemsList.set(j, itemsList.get(j - 1) + 1); //+1 because there is a new row
            }
            itemsList.set(i, valueToInsert);
        } else itemsList.add(valueToInsert);
        return i;
    }

    public static int removeOrderedRow(ArrayList<Integer> itemsList, int positionToRemove) {
        int size = itemsList.size();
        if (positionToRemove < size && positionToRemove >= 0) {
            for (int j = size - 1; j > positionToRemove; j--) {
                itemsList.set(j, itemsList.get(j) - 1); //-1 because there is one less row
            }
            itemsList.remove(positionToRemove);
        } else return -1;
        return positionToRemove;
    }
    //** End of UTILS MODULE methods **
}
<file_sep>package com.droidko.voltfeed.model;

import com.droidko.voltfeed.Schema;
import com.parse.ParseObject;

import java.util.Date;

public class Post {

    //Attributes
    private String mId;
    private int mType;
    private String mPicture;
    private String mText;
    private String mUserId;
    private int mVolts;
    private Date mCreatedAt;
    private Date mUpdatedAt;

    public Post(ParseObject parseObject) {
        setId(parseObject.getObjectId());
        setType(parseObject.getInt(Schema.POST_COL_TYPE));
        setPicture(parseObject.getString(Schema.POST_COL_PICTURE));
        setText(parseObject.getString(Schema.POST_COL_TEXT));
        setUserId(parseObject.getString(Schema.POST_COL_USER_ID));
        setVolts(parseObject.getInt(Schema.POST_COL_VOLTS));
        setCreatedAt(parseObject.getCreatedAt());
        setUpdatedAt(parseObject.getUpdatedAt());
    }

    public String getId() {
        return mId;
    }

    public void setId(String id) {
        this.mId = id;
    }

    public int getType() {
        return mType;
    }

    public void setType(int type) {
        this.mType = type;
    }

    public String getPicture() {
        return mPicture;
    }

    public void setPicture(String picture) {
        this.mPicture = picture;
    }

    public String getText() {
        return mText;
    }

    public void setText(String text) {
        this.mText = text;
    }

    public String getUserId() {
        return mUserId;
    }

    public void setUserId(String userId) {
        this.mUserId = userId;
    }

    public int getVolts() {
        return mVolts;
    }

    public void setVolts(int volts) {
        mVolts = volts;
    }

    public Date getCreatedAt() {
        return mCreatedAt;
    }

    public void setCreatedAt(Date createdAt) {
        this.mCreatedAt = createdAt;
    }

    public Date getUpdatedAt() {
        return mUpdatedAt;
    }

    public void setUpdatedAt(Date updatedAt) {
        this.mUpdatedAt = updatedAt;
    }

}
<file_sep>package com.droidko.voltfeed.utils;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Typeface;
import android.os.Build;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.droidko.voltfeed.Config;
import com.droidko.voltfeed.R;
import com.droidko.voltfeed.VoltfeedApp;
import com.droidko.voltfeed.activities.VoltfeedActivity;
import com.droidko.voltfeed.events.EventDispatcher;
import com.parse.ParseException;

import java.util.Random;

public class UiHelper {

    private static final Typeface sFontVarela = Typeface.createFromAsset(
            VoltfeedApp.getContextInstance().getAssets(), "VarelaRound-Regular.otf");

    private static final Typeface sFontRoboto = Typeface.createFromAsset(
            VoltfeedApp.getContextInstance().getAssets(), "Roboto-Regular.ttf");

    public static Toolbar setToolbar(ActionBarActivity activity,
                                     int view_toolbar,
                                     int view_title,
                                     String title,
                                     int view_logo,
                                     int logo) {
        Toolbar local_toolbar = (Toolbar) activity.findViewById(view_toolbar);
        ImageView local_logo = (ImageView) activity.findViewById(view_logo);
        TextView local_title = (TextView) activity.findViewById(view_title);

        activity.setSupportActionBar(local_toolbar);
        activity.getSupportActionBar().setElevation(Config.UI_TOOLBAR_ELEVATION);
        //We place the tittle in view_title. Otherwise it would end slide_to_top on the left side of the logo
        activity.getSupportActionBar().setTitle(null);
        local_logo.setImageResource(logo);
        local_title.setText(title);
        setFontVarela(local_title);

        return local_toolbar;
    }

    public static boolean isValidEmail(String email) {
        return !TextUtils.isEmpty(email) && android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches();
    }

    public static void showToast(String message) {
        Toast.makeText(VoltfeedApp.getContextInstance(), message, Toast.LENGTH_SHORT).show();
    }

    public static void showToast(int resourceId) {
        Context context = VoltfeedApp.getContextInstance();
        Toast.makeText(context, context.getText(resourceId), Toast.LENGTH_SHORT).show();
    }

    public static void startActivityClearStack(Context localContext,
                                               Class<? extends Activity> targetActivity) {
        localContext.startActivity(
                new Intent(localContext, targetActivity).
                        setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)
        );
    }

    public static void showParseError(Context context, ParseException e) {
        switch (e.getCode()) {
            //Error: No internet connection
            case ParseException.CONNECTION_FAILED:
                showToast(R.string.error_no_internet);
                EventDispatcher.dispatchNoConnection();
                break;

            //Error: Email and/or password incorrect
            case ParseException.OBJECT_NOT_FOUND:
                showToast(R.string.login_wrong_credentials);
                break;

            //Error: Invalid username (already taken)
            case ParseException.USERNAME_TAKEN:
                showToast(R.string.signup_invalid_username);
                break;

            //Error: Session expired
            case ParseException.INVALID_SESSION_TOKEN:
                showToast(R.string.error_session_expired);
                break;

            //Error: Query results not cached
            case ParseException.CACHE_MISS:
                //Do nothing
                break;

            //Error ??: Default unknown error
            default:
                showToast(R.string.error_unknown);
                Log.e(Config.LOG_ERROR, e.getCode() + ": " + e.getMessage());
        }
    }

    public static void setFontVarela(TextView textView) {
        textView.setTypeface(sFontVarela);
    }

    public static void setFontRoboto(TextView textView) {
        textView.setTypeface(sFontRoboto);
    }

    public static boolean canRunLollipopFx() {
        return (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
                && Config.UI_LOLLIPOP_FX_ENABLED;
    }

    @SuppressLint("NewApi")
    public static void renderElevation(int elevation, View view) {
        if (!canRunLollipopFx()) return;
        view.setElevation(elevation);
    }

    public static void renderElevation(int elevation, View... views) {
        for (View view : views) {
            renderElevation(elevation, view);
        }
    }

    public static void addFragment(Activity activity,
                                   int containerId,
                                   Fragment fragment,
                                   String tag) {
        ((VoltfeedActivity)activity).getSupportFragmentManager()
                .beginTransaction()
                .setCustomAnimations(
                        R.anim.abc_fade_in,
                        R.anim.abc_fade_out,
                        R.anim.abc_fade_in,
                        R.anim.abc_fade_out)
                .add(containerId, fragment)
                .addToBackStack(tag)
                .commit();
    }

    public static void removeFragment(Activity activity, Fragment fragment) {
        ((VoltfeedActivity)activity).getSupportFragmentManager()
                .beginTransaction()
                .setCustomAnimations(R.anim.abc_fade_in, R.anim.abc_fade_out)
                .remove(fragment)
                .commit();
        ((VoltfeedActivity)activity).getSupportFragmentManager().popBackStack();
    }

    public static void replaceFragment(Activity activity, int containerId, Fragment fragment) {
        ((VoltfeedActivity)activity).getSupportFragmentManager()
                .beginTransaction()
                .replace(containerId, fragment)
                .commit();
    }

    public static String getMessageOfTheDay() {
        Random rand = new Random();
        int randomNum = rand.nextInt((Config.UI_MOTD_COUNT - 1) + 1) + 1;
        int resourceId = VoltfeedApp.getContextInstance().getResources().getIdentifier(
                String.valueOf("motd_" + randomNum),
                "string",
                VoltfeedApp.getContextInstance().getPackageName());
        return VoltfeedApp.getContextInstance().getResources().getString(resourceId);
    }

}<file_sep>package com.droidko.voltfeed.ui.timeline;

import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;

import com.droidko.voltfeed.R;
import com.facebook.drawee.view.SimpleDraweeView;

public class TimelineImagePostViewHolder extends RecyclerView.ViewHolder {

    public TextView mTitle;
    public View mVoltButton;
    public TextView mVoltsCounter;
    public TextView mDate;
    public SimpleDraweeView mPicture;
    public View mCard;

    public TimelineImagePostViewHolder(View itemLayoutView) {
        super(itemLayoutView);
        this.mTitle = (TextView) itemLayoutView.findViewById(R.id.timeline_post_image_title);
        this.mVoltButton = itemLayoutView.findViewById(R.id.timeline_post_image_volt_button);
        this.mVoltsCounter = (TextView) itemLayoutView.findViewById(R.id.timeline_post_image_volt_count);
        this.mDate = (TextView) itemLayoutView.findViewById(R.id.timeline_post_image_date);
        this.mPicture = (SimpleDraweeView) itemLayoutView.findViewById(R.id.timeline_post_image_picture);
        this.mCard = itemLayoutView.findViewById(R.id.timeline_post_image_card);
    }

}
<file_sep>package com.droidko.voltfeed.activities;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import com.droidko.voltfeed.Config;
import com.droidko.voltfeed.R;
import com.droidko.voltfeed.Schema;
import com.droidko.voltfeed.ui.ConnectingDialog;
import com.droidko.voltfeed.utils.UiHelper;
import com.parse.ParseException;
import com.parse.ParseUser;
import com.parse.SignUpCallback;


public class SignUpActivity extends ActionBarActivity {

    private Context mContext;

    private EditText mUsername;
    private EditText mMail;
    private EditText mPassword;
    private EditText mConfirmPassword;
    private Button mJoin;
    private TextView mToS;
    private Toolbar mToolbar;

    private boolean mActivityIsVisible;

    private FragmentManager mFragmentManager;
    private ConnectingDialog mConnectingDialogInstance;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sign_up);

        mContext = this;

        setUi();
        setListeners();
        initFragments();
    }

    @Override
    protected void onStart() {
        super.onStart();
        mActivityIsVisible = true;
    }

    @Override
    protected void onStop() {
        super.onStop();
        mActivityIsVisible = false;
    }

    private void setUi() {
        mUsername = (EditText) findViewById(R.id.signup_username);
        mMail = (EditText) findViewById(R.id.signup_email);
        mPassword = (EditText) findViewById(R.id.login_password);
        mConfirmPassword = (EditText) findViewById(R.id.signup_password_confirm);
        mJoin = (Button) findViewById(R.id.signup_btn_join);
        mToS = (TextView) findViewById(R.id.tv_tos);
        mToolbar = UiHelper.setToolbar(
                this,
                R.id.main_toolbar,
                R.id.toolbar_title,
                getString(R.string.title_activity_sign_up),
                R.id.toolbar_logo,
                R.drawable.ic_topbar);
    }

    private void setListeners() {
        mJoin.setOnClickListener(mJoinClickListener);
        mToS.setOnClickListener(mTosClickListener);
    }

    private void initFragments() {
        mFragmentManager = getSupportFragmentManager();
        mConnectingDialogInstance = new ConnectingDialog();
    }

    private void blockUi() {
        if (mActivityIsVisible) {
            mJoin.setEnabled(false);
            mJoin.setTextColor(getResources().getColor(R.color.gray_1));
            mUsername.setEnabled(false);
            mMail.setEnabled(false);
            mPassword.setEnabled(false);
            mConfirmPassword.setEnabled(false);
            mConnectingDialogInstance.show(mFragmentManager, ConnectingDialog.TAG);
        }
    }

    private void unlockUi() {
        if (mActivityIsVisible) {
            mJoin.setEnabled(true);
            mJoin.setTextColor(getResources().getColor(R.color.white_0));
            mUsername.setEnabled(true);
            mMail.setEnabled(true);
            mPassword.setEnabled(true);
            mConfirmPassword.setEnabled(true);
            mConnectingDialogInstance.dismiss();
        }
    }

    private void doSignUp(String username, String email, String password) {
        ParseUser user = new ParseUser();
        user.setUsername(username);
        user.setPassword(<PASSWORD>);
        user.put(Schema.USER_COL_EMAIL, email);
        user.signUpInBackground(mSignUpCallback);

        blockUi();
    }

    // ** ANONYMOUS CLASSES **

    View.OnClickListener mJoinClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            String username = mUsername.getText().toString().trim();
            String mail = mMail.getText().toString().trim();
            String password = mPassword.getText().toString();
            String confirmPassword = mConfirmPassword.getText().toString();

            //Rule: Every field is required
            if ( TextUtils.isEmpty(username)
                    || TextUtils.isEmpty(mail)
                    || TextUtils.isEmpty(password)
                    || TextUtils.isEmpty(confirmPassword) ) {
                UiHelper.showToast(R.string.login_require_all);
                return;
            }

            //Rule: must be a valid email adress
            if (!UiHelper.isValidEmail(mail)) {
                mMail.setError(getString(R.string.login_not_valid_email));
                return;
            }

            //Rule: passwords can't be too short
            if (password.length() < Config.USER_PASSWORD_MIN_LENGHT) {
                mPassword.setError(getResources()
                        .getString(R.string.login_password_too_short,
                                Config.USER_PASSWORD_MIN_LENGHT));
                return;
            }

            //Rule: The field "password" and "confirm password" must match
            if (!password.equals(confirmPassword)) {
                mConfirmPassword.setError(getString(R.string.signup_passwords_dont_match));
                return;
            }

            doSignUp(username, mail, password);
        }
    };

    View.OnClickListener mTosClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Config.ToS_URL)));
        }
    };

    SignUpCallback mSignUpCallback = new  SignUpCallback() {
        @Override
        public void done(ParseException e) {
            unlockUi();
            if (e == null) {
                UiHelper.showToast(R.string.signup_user_created);
                UiHelper.startActivityClearStack(mContext, MainActivity.class);
            } else {
                UiHelper.showParseError(mContext, e);
            }
        }
    };

    // ** End of ANONYMOUS CLASSES **

}
