com.jaspervanriet.huntingthatproduct.Activities.MainActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.jaspervanriet.huntingthatproduct.Activities.MainActivity.java

Source

/*
 * Copyright (C) 2015 Jasper van Riet
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.jaspervanriet.huntingthatproduct.Activities;

import android.app.DatePickerDialog;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.DatePicker;
import android.widget.LinearLayout;
import android.widget.Toast;

import com.crashlytics.android.Crashlytics;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.jaspervanriet.huntingthatproduct.Activities.Settings.SettingsActivity;
import com.jaspervanriet.huntingthatproduct.Adapters.ProductListAdapter;
import com.jaspervanriet.huntingthatproduct.Classes.Product;
import com.jaspervanriet.huntingthatproduct.R;
import com.jaspervanriet.huntingthatproduct.Utils.Constants;
import com.jaspervanriet.huntingthatproduct.Utils.Utils;
import com.jaspervanriet.huntingthatproduct.Views.DatePickerFragment;
import com.jaspervanriet.huntingthatproduct.Views.FeedContextMenu;
import com.jaspervanriet.huntingthatproduct.Views.FeedContextMenuManager;
import com.koushikdutta.async.future.FutureCallback;
import com.koushikdutta.ion.Ion;
import com.nanotasks.BackgroundWork;
import com.nanotasks.Completion;
import com.nanotasks.Tasks;
import com.pnikosis.materialishprogress.ProgressWheel;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.PicassoTools;

import java.text.DateFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.TimeoutException;

import butterknife.ButterKnife;
import butterknife.InjectView;
import io.fabric.sdk.android.Fabric;
import io.realm.Realm;
import io.realm.RealmResults;

public class MainActivity extends BaseActivity implements ProductListAdapter.OnProductClickListener,
        DatePickerDialog.OnDateSetListener, FeedContextMenu.OnFeedContextMenuItemClickListener {

    private final static int ANIM_TOOLBAR_INTRO_DURATION = 350;
    private final static String URL_PLAY_STORE = "market://details?id=com.jaspervanriet" + ".huntingthatproduct";

    private ArrayList<Product> mProducts = new ArrayList<>();
    private ArrayList<Integer> mReadProductsIds = new ArrayList<>();
    private ProductListAdapter mListAdapter;
    private Handler mHandler = new Handler();
    private Boolean mIsRefreshing = false;
    private Boolean startIntroAnimation = true;
    private String mDateString;
    private JsonObject mJsonResult;
    private Realm mRealm;

    // Date for last time user used the app
    private String mSavedDate;

    // true if user has picked a day to view products for
    private boolean mDateSet = false;

    // true if activity no longer exists
    private boolean mIsDestroyed = false;

    @InjectView(R.id.toolbar)
    Toolbar mToolBar;
    @InjectView(android.R.id.list)
    RecyclerView mRecyclerView;
    @InjectView(R.id.list_progress_wheel)
    ProgressWheel mProgressWheel;
    @InjectView(R.id.products_empty_view)
    LinearLayout mEmptyView;

    private void setProductAsRead(final Product product) {
        mRealm.beginTransaction();
        int i;
        RealmResults<Product> result = mRealm.where(Product.class).equalTo("id", product.getId()).findAll();

        for (i = 0; i < result.size(); i++) {
            Product resultProduct = result.get(i);
            resultProduct.setRead(true);
        }
        mRealm.commitTransaction();
        mListAdapter.notifyDataSetChanged();
    }

    private void removeOldCache() {
        mSavedDate = getSharedPreferences("PREFERENCE", MODE_PRIVATE).getString("saved_date", mDateString);
        if (!mSavedDate.equals(mDateString)) {
            Realm realm = Realm.getInstance(this);
            realm.executeTransaction(new Realm.Transaction() {
                @Override
                public void execute(Realm realm) {
                    RealmResults<Product> result = realm.where(Product.class).equalTo("date", mSavedDate).findAll();
                    result.clear();
                }
            });
            getSharedPreferences("PREFERENCE", MODE_PRIVATE).edit().putString("saved_date", mDateString).apply();
        }
    }

    private void showDatePickerDialog() {
        DialogFragment dialogFragment = new DatePickerFragment();
        dialogFragment.show(getSupportFragmentManager(), "dataPicker");
    }

    private void activityExitAnimation(View v, Product product, Intent i) {
        int[] startingLocation = new int[2];
        v.getLocationOnScreen(startingLocation);
        i.putExtra(CommentsActivity.ARG_DRAWING_START_LOCATION, startingLocation[1]);
        i.putExtra("productId", product.getId());
        i.putExtra("collection", false);
        startActivity(i);
        overridePendingTransition(0, 0);
    }

    private void setToolbarIntroAnimation() {
        int toolBarSize = Utils.dpToPx(56);
        mToolBar.setTranslationY(-toolBarSize);
        mToolBar.animate().translationY(0).setDuration(ANIM_TOOLBAR_INTRO_DURATION).setStartDelay(300);
    }

    private void completeRefresh() {
        mEmptyView.setVisibility(View.GONE);
        if (mProducts.size() != 0) {
            mProducts.clear();
            mListAdapter.notifyDataSetChanged();
        }
        mProgressWheel.setVisibility(View.VISIBLE);
        mProgressWheel.spin();
        getContent();
    }

    private void setupRecyclerView() {
        mRecyclerView.setHasFixedSize(true);
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());
        mRecyclerView.setLayoutManager(getLayoutManager());
        mRecyclerView.setAdapter(mListAdapter);
        mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                FeedContextMenuManager.getInstance().onScrolled(recyclerView, dx, dy);
            }
        });
    }

    private void getContent() {
        mIsRefreshing = true;
        mHandler.post(refreshingContent);
        mProducts.clear();

        if (!(Utils.hasInternetAccess(this))) {
            if (mSavedDate.equals(mDateString)) {
                getLocalCache();
                showUpdatedList();
            } else {
                showNoConnectionError();
                mIsRefreshing = false;
                checkEmpty();
            }
        } else {
            if (Constants.TOKEN_EXPIRES < System.currentTimeMillis()) {
                getAuthToken();
            } else {
                getProducts();
            }
        }
    }

    private void getAuthToken() {
        JsonObject json = new JsonObject();
        json.addProperty("client_id", Constants.CLIENT_ID);
        json.addProperty("client_secret", Constants.CLIENT_SECRET);
        json.addProperty("grant_type", Constants.GRANT_TYPE);

        Ion.with(this).load(Constants.API_TOKEN_URL).setJsonObjectBody(json).asJsonObject()
                .setCallback(new FutureCallback<JsonObject>() {
                    @Override
                    public void onCompleted(Exception e, JsonObject result) {
                        if (result != null && result.has("access_token")) {
                            Constants.CLIENT_TOKEN = result.get("access_token").getAsString();
                            Constants.TOKEN_EXPIRES = System.currentTimeMillis()
                                    + (long) result.get("expires_in").getAsInt();
                            getContent();
                        }
                    }
                });
    }

    private void getProducts() {
        String url;
        if (mDateSet) {
            url = Constants.API_URL + "posts?day=" + mDateString;
        } else {
            url = Constants.API_URL + "posts";
        }
        Ion.with(this).load(url).setHeader("Authorization", "Bearer " + Constants.CLIENT_TOKEN).asJsonObject()
                .setCallback(new FutureCallback<JsonObject>() {
                    @Override
                    public void onCompleted(Exception e, JsonObject result) {
                        if (e != null && e instanceof TimeoutException) {
                            getLocalCache();
                            return;
                        }
                        if (result != null && result.has("posts")) {
                            mJsonResult = result;
                            Tasks.executeInBackground(getApplicationContext(), new BackgroundWork<Void>() {
                                @Override
                                public Void doInBackground() throws Exception {
                                    processPosts();
                                    return null;
                                }
                            }, new Completion<Void>() {
                                @Override
                                public void onSuccess(Context context, Void result) {
                                    if (!mIsDestroyed) {
                                        queryRealmForProducts();
                                        showUpdatedList();
                                    }
                                }

                                @Override
                                public void onError(Context context, Exception e) {
                                    Crashlytics.logException(e);
                                }
                            });
                        }
                    }
                });
    }

    private void showUpdatedList() {
        mIsRefreshing = false;
        mListAdapter.notifyDataSetChanged();
    }

    private void getLocalCache() {
        showNoConnectionError();
        queryRealmForProducts();
    }

    private void showNoConnectionError() {
        Toast.makeText(this, getResources().getString(R.string.error_connection), Toast.LENGTH_SHORT).show();
    }

    private void processPosts() {
        int i;
        JsonArray products = mJsonResult.getAsJsonArray("posts");
        Realm realm = Realm.getInstance(this);
        for (i = 0; i < products.size(); i++) {
            JsonObject obj = products.get(i).getAsJsonObject();
            Product product = new Product(obj);
            if (mReadProductsIds.contains(product.getId())) {
                product.setRead(true);
            }
            product.setRank(i);
            cacheProduct(product, realm);
        }
        realm.close();
    }

    private void queryRealmForProducts() {
        RealmResults<Product> resultProducts = mRealm.where(Product.class).equalTo("date", mDateString).findAll();
        resultProducts.sort("rank");
        for (Product product : resultProducts) {
            mProducts.add(product);
        }
    }

    // Saves Product to Realm or updates the Realm entry if db contains Product already.
    private void cacheProduct(final Product product, Realm realm) {
        realm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {
                Product realmProduct = realm.copyToRealmOrUpdate(product);
            }
        });
    }

    private boolean sendCrashData() {
        return SettingsActivity.getCrashDataPref(this);
    }

    private void setActionBarTitle(String title) {
        ActionBar actionBar = getSupportActionBar();
        actionBar.setTitle(title);
    }

    private LinearLayoutManager getLayoutManager() {
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        return layoutManager;
    }

    private void getTodaysDate() {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        simpleDateFormat.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
        mDateString = simpleDateFormat.format(new Date()) + "";
    }

    public String getMonth(int month) {
        return new DateFormatSymbols().getMonths()[month];
    }

    private String getDateFormattedString(Calendar calendar) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        return simpleDateFormat.format(calendar.getTime());
    }

    private void checkEmpty() {
        if (mListAdapter.getItemCount() == 0) {
            mEmptyView.setVisibility(View.VISIBLE);
        } else {
            mEmptyView.setVisibility(View.GONE);
        }
    }

    private void goToPlayStorePage() {
        Intent intent = new Intent(Intent.ACTION_VIEW).setData(Uri.parse(URL_PLAY_STORE));
        startActivity(intent);
    }

    private final Runnable refreshingContent = new Runnable() {
        public void run() {
            try {
                if (mIsRefreshing) {
                    mHandler.postDelayed(this, 1000);
                } else {
                    mProgressWheel.stopSpinning();
                    mProgressWheel.setVisibility(View.GONE);
                    checkEmpty();
                }
            } catch (Exception error) {
                error.printStackTrace();
            }
        }
    };

    /*
     * Datepicker callback
     */

    @Override
    public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
        mDateSet = true;
        Calendar chosenCalendar = Calendar.getInstance();
        chosenCalendar.set(Calendar.YEAR, year);
        chosenCalendar.set(Calendar.MONTH, monthOfYear);
        chosenCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
        mDateString = getDateFormattedString(chosenCalendar);
        setActionBarTitle(getMonth(monthOfYear) + " " + dayOfMonth);
        completeRefresh();
    }

    /*
     * Adapter onClickListeners
     */

    @Override
    public void onShareClick(int feedItem) {
        Product product = mProducts.get(feedItem);
        Intent share = new Intent(android.content.Intent.ACTION_SEND);
        share.setType("text/plain");
        share.putExtra(Intent.EXTRA_SUBJECT, product.getTitle());
        share.putExtra(Intent.EXTRA_TEXT, product.getProductUrl());
        startActivity(Intent.createChooser(share, "Share product"));
        FeedContextMenuManager.getInstance().hideContextMenu();
    }

    @Override
    public void onCancelClick(int feedItem) {
        FeedContextMenuManager.getInstance().hideContextMenu();
    }

    @Override
    public void onImageClick(View v, int position) {
        Product product = mProducts.get(position);
        setProductAsRead(product);
        Intent openUrl = new Intent(this, WebActivity.class);
        activityExitAnimation(v, product, openUrl);
    }

    @Override
    public void onCommentsClick(View v, Product product) {
        setProductAsRead(product);
        Intent i = new Intent(this, CommentsActivity.class);
        activityExitAnimation(v, product, i);
    }

    @Override
    public void onContextClick(View v, int position) {
        FeedContextMenuManager.getInstance().toggleContextMenuFromView(v, position, this, true);
    }

    /*
     * UI boilerplate
     */

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.main_menu, menu);
        if (startIntroAnimation) {
            setToolbarIntroAnimation();
            startIntroAnimation = false;
        }
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int itemId = item.getItemId();
        if (itemId == R.id.menu_main_refresh) {
            completeRefresh();
            return true;
        }
        if (itemId == R.id.menu_calendar) {
            showDatePickerDialog();
            return true;
        }
        if (itemId == R.id.menu_rate) {
            goToPlayStorePage();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    protected int getSelfNavDrawerItem() {
        return NAVDRAWER_ITEM_TODAYS_PRODUCTS;
    }

    /*
     * App lifecycle
     */

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

        // If you do not use Fabric, remove this. Be sure to remove the references in build.gradle
        // as well.
        if (sendCrashData()) {
            Fabric.with(this, new Crashlytics());
        }

        setContentView(R.layout.activity_main);
        super.onCreateDrawer();
        ButterKnife.inject(this);

        boolean toolbarAnimation = getIntent().getBooleanExtra("toolbar_animation", true);
        startIntroAnimation = (savedInstanceState == null) && toolbarAnimation;

        mRealm = Realm.getInstance(this);
        Product.getReadProductsIds(mRealm, mReadProductsIds);

        setToolBar();
        getTodaysDate();
        removeOldCache();

        mProgressWheel.setBarColor(getResources().getColor(R.color.primary_accent));
        mListAdapter = new ProductListAdapter(this, mProducts);
        mListAdapter.setOnProductClickListener(this);
        setupRecyclerView();
        completeRefresh();
    }

    @Override
    public void onRestart() {
        super.onRestart();
        completeRefresh();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mIsDestroyed = true;
        PicassoTools.clearCache(Picasso.with(this));
        mRealm.close();
    }
}