ca.mymenuapp.ui.activities.RestaurantActivity.java Source code

Java tutorial

Introduction

Here is the source code for ca.mymenuapp.ui.activities.RestaurantActivity.java

Source

/*
 * Copyright (C) 2014 MyMenu, Inc.
 *
 * 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 ca.mymenuapp.ui.activities;

import android.app.Fragment;
import android.app.FragmentManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.RectF;
import android.net.Uri;
import android.os.Bundle;
import android.support.v13.app.FragmentPagerAdapter;
import android.support.v4.app.NavUtils;
import android.support.v4.app.TaskStackBuilder;
import android.support.v4.view.ViewPager;
import android.text.Spannable;
import android.text.SpannableString;
import android.util.TypedValue;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.ShareActionProvider;
import butterknife.InjectView;
import ca.mymenuapp.R;
import ca.mymenuapp.data.MyMenuDatabase;
import ca.mymenuapp.data.api.model.RestaurantMenu;
import ca.mymenuapp.data.api.model.User;
import ca.mymenuapp.data.prefs.ObjectPreference;
import ca.mymenuapp.data.rx.EndlessObserver;
import ca.mymenuapp.ui.fragments.MenuItemsGridFragment;
import ca.mymenuapp.ui.fragments.RestaurantInfoDialogFragment;
import ca.mymenuapp.ui.fragments.ReviewsFragment;
import ca.mymenuapp.ui.misc.AlphaForegroundColorSpan;
import ca.mymenuapp.ui.widgets.KenBurnsView;
import com.astuetz.PagerSlidingTabStrip;
import com.f2prateek.dart.InjectExtra;
import com.squareup.picasso.Picasso;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;

import static ca.mymenuapp.data.DataModule.USER_PREFERENCE;

/**
 * An activity to display a single restaurant.
 *
 * Limitations : menu item images must not be transparent (or contain any transparent pixels, or we
 * can see the list scrolling behind it.
 *
 * todo : sync scrolling
 * todo : handle landscape
 */
public class RestaurantActivity extends BaseActivity implements AbsListView.OnScrollListener {
    public static final String ARGS_RESTAURANT_ID = "restaurant_id";

    @InjectExtra(ARGS_RESTAURANT_ID)
    long restaurantId;
    // The action bar icon view
    @InjectView(android.R.id.home)
    ImageView actionBarIconView;
    // top level header that contains restaurantHeaderBackground, restaurantHeaderLogo and tabStrip
    @InjectView(R.id.restaurant_header)
    View restaurantHeader;
    // background that animates with random menu item pictures
    @InjectView(R.id.restaurant_header_image)
    KenBurnsView restaurantHeaderBackground;
    // logo for this restaurant
    @InjectView(R.id.restaurant_header_logo)
    ImageView restaurantHeaderLogo;
    // tabs for pager
    @InjectView(R.id.restaurant_category_tabs)
    PagerSlidingTabStrip tabStrip;
    // viewpager for swiping between different categories
    @InjectView(R.id.restaurant_menu_pager)
    ViewPager pager;

    @Inject
    MyMenuDatabase myMenuDatabase;
    @Inject
    Picasso picasso;
    @Inject
    @Named(USER_PREFERENCE)
    ObjectPreference<User> userPreference;

    private RestaurantMenu menu;

    // color of the action bar title
    private int actionBarTitleColor;
    // height of the action bar title
    private int actionBarHeight;
    // height of the restaurantHeader read from dimensions, not the view
    private int headerHeight;
    // minimum translation for the sticky effect
    private int minHeaderTranslation;
    // interpolator for smooth animations
    private AccelerateDecelerateInterpolator smoothInterpolator;
    private RectF rectF1 = new RectF();
    private RectF rectF2 = new RectF();
    private AlphaForegroundColorSpan alphaForegroundColorSpan;
    private SpannableString spannableString;
    private TypedValue typedValue = new TypedValue();
    private ShareActionProvider shareActionProvider;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        inflateView(R.layout.activity_restaurant);

        tabStrip.setTextColorResource(android.R.color.white);

        init();
        setupFancyScroll();

        myMenuDatabase.getRestaurantAndMenu(userPreference.get(), restaurantId,
                new EndlessObserver<RestaurantMenu>() {
                    @Override
                    public void onNext(RestaurantMenu restaurantMenu) {
                        menu = restaurantMenu;
                        setShareIntent(menu);
                        spannableString = new SpannableString(menu.getRestaurant().businessName);
                        picasso.load(menu.getRestaurant().businessPicture).fit().centerCrop()
                                .into(restaurantHeaderLogo);
                        restaurantHeaderBackground.loadImages(picasso, menu.getRandomMenuItem().picture,
                                menu.getRandomMenuItem().picture);
                        pager.setAdapter(new MenuCategoryAdapter(getFragmentManager(), menu));
                        tabStrip.setViewPager(pager);
                    }
                });
    }

    /**
     * Initialize some global variables
     */
    private void init() {
        smoothInterpolator = new AccelerateDecelerateInterpolator();
        headerHeight = getResources().getDimensionPixelSize(R.dimen.restaurant_header_height);
        // we have tabs, so we need twice the space of the actionbar
        minHeaderTranslation = -headerHeight + (2 * getActionBarHeight());
        actionBarTitleColor = getResources().getColor(android.R.color.white);
        alphaForegroundColorSpan = new AlphaForegroundColorSpan(actionBarTitleColor);
    }

    /**
     * Setup the action bar for effects.
     */
    private void setupFancyScroll() {
        getActionBar().setDisplayHomeAsUpEnabled(true);
        getActionBar().setIcon(R.drawable.ic_transparent);
        restaurantHeader.bringToFront(); // explicit, list scrolls behind the header
        restaurantHeaderLogo.bringToFront();
    }

    /**
     * Get the height of the action bar.
     */
    public int getActionBarHeight() {
        if (actionBarHeight != 0) {
            return actionBarHeight;
        }
        getTheme().resolveAttribute(android.R.attr.actionBarSize, typedValue, true);
        actionBarHeight = TypedValue.complexToDimensionPixelSize(typedValue.data,
                getResources().getDisplayMetrics());
        return actionBarHeight;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case android.R.id.home:
            Intent upIntent = NavUtils.getParentActivityIntent(this);
            if (NavUtils.shouldUpRecreateTask(this, upIntent)) {
                TaskStackBuilder.create(this).addNextIntentWithParentStack(upIntent).startActivities();
            } else {
                NavUtils.navigateUpTo(this, upIntent);
            }
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        // ignore
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        int scrollY = getScrollY(view);
        restaurantHeader.setTranslationY(Math.max(-scrollY, minHeaderTranslation));
        float ratio = clamp(restaurantHeader.getTranslationY() / minHeaderTranslation, 0.0f, 1.0f);
        interpolate(restaurantHeaderLogo, actionBarIconView, smoothInterpolator.getInterpolation(ratio));
        float alpha = clamp(5.0F * ratio - 4.0F, 0.0F, 1.0F);
        setTitleAlpha(alpha);
    }

    public int getScrollY(AbsListView listView) {
        View c = listView.getChildAt(0);
        if (c == null) {
            return 0;
        }

        int firstVisiblePosition = listView.getFirstVisiblePosition();
        int top = c.getTop();

        int headerHeight = 0;
        if (firstVisiblePosition >= 1) {
            headerHeight = listView.getChildAt(0).getHeight();
        }

        return -top + firstVisiblePosition * c.getHeight() + headerHeight;
    }

    /**
     * Set the alpha value for the action bar title.
     */
    private void setTitleAlpha(float alpha) {
        // spannable string may not be initialized yet, skip until next pass
        if (spannableString != null) {
            alphaForegroundColorSpan.setAlpha(alpha);
            spannableString.setSpan(alphaForegroundColorSpan, 0, spannableString.length(),
                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            getActionBar().setTitle(spannableString);
        }
    }

    /**
     * Standard math clamp method https://en.wikipedia.org/wiki/Clamping_(graphics)
     *
     * @param value value to clamp
     * @param max maximum to return
     * @param min minimum to return
     * @return min if value < min, max if value > max, else value
     */
    public static float clamp(float value, float max, float min) {
        return Math.max(Math.min(value, min), max);
    }

    /**
     * Interpolate between two views.
     * This will animate view1 to somewhere between view1 and view2 dependening on the interpolation
     * value.
     * Used to translate the logo to the action bar icon.
     *
     * @param interpolation 'progress' of the interpolation
     */
    private void interpolate(View view1, View view2, float interpolation) {
        getOnScreenRect(rectF1, view1);
        getOnScreenRect(rectF2, view2);

        float scaleX = 1.0F + interpolation * (rectF2.width() / rectF1.width() - 1.0F);
        float scaleY = 1.0F + interpolation * (rectF2.height() / rectF1.height() - 1.0F);
        float translationX = 0.5F * (interpolation * (rectF2.left + rectF2.right - rectF1.left - rectF1.right));
        float translationY = 0.5F * (interpolation * (rectF2.top + rectF2.bottom - rectF1.top - rectF1.bottom));

        view1.setTranslationX(translationX);
        view1.setTranslationY(translationY - restaurantHeader.getTranslationY());
        view1.setScaleX(scaleX);
        view1.setScaleY(scaleY);
    }

    /**
     * Get the position of the view into the given RectF.
     */
    private RectF getOnScreenRect(RectF rect, View view) {
        rect.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
        return rect;
    }

    /**
     * A pager that displays a menu by categories.
     * Each page contains a list of menu items for that particular category.
     */
    class MenuCategoryAdapter extends FragmentPagerAdapter {
        private final RestaurantMenu menu;

        MenuCategoryAdapter(FragmentManager fm, RestaurantMenu menu) {
            super(fm);
            this.menu = menu;
        }

        @Override
        public int getCount() {
            return menu.getCategoryCount() + 1;
        }

        @Override
        public Fragment getItem(int position) {
            if (position < menu.getCategoryCount()) {
                return MenuItemsGridFragment.newInstance(menu.getMenuItemsByCategory(position),
                        menu.getRestaurant(), menu.getReviews());
            } else {
                return ReviewsFragment.newInstance(menu.getReviews(), true);
            }
        }

        @Override
        public CharSequence getPageTitle(int position) {
            if (position < menu.getCategoryCount()) {
                return menu.getCategoryTitle(position);
            } else {
                return getString(R.string.reviews);
            }
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_restaurant, menu);
        android.view.MenuItem item = menu.findItem(R.id.restaurant_share);
        shareActionProvider = (ShareActionProvider) item.getActionProvider();
        return true;
    }

    private void setShareIntent(RestaurantMenu menu) {
        Intent shareIntent = new Intent();
        shareIntent.setAction(Intent.ACTION_SEND);
        // todo check if this item has a picture
        shareIntent.putExtra(Intent.EXTRA_TEXT, getString(R.string.share_restaurant,
                menu.getRestaurant().businessName, menu.getRestaurant().businessPicture));
        shareIntent.setType("text/plain");
        shareActionProvider.setShareIntent(shareIntent);
    }

    @Override
    public boolean onMenuItemSelected(int featureId, MenuItem item) {
        switch (item.getItemId()) {
        case R.id.restaurant_call:
            Intent intent = new Intent(Intent.ACTION_CALL);
            intent.setData(Uri.parse("tel:" + menu.getRestaurant().businessNumber));
            if (isAvailable(this, intent)) {
                startActivity(intent);
            }
            return true;
        case R.id.restaurant_info:
            final RestaurantInfoDialogFragment fragment = RestaurantInfoDialogFragment
                    .newInstance(menu.getRestaurant());
            fragment.show(getFragmentManager(), "info");
            return true;
        default:
            return super.onMenuItemSelected(featureId, item);
        }
    }

    /**
     * Check if any apps are installed on the app to receive this intent.
     */
    public static boolean isAvailable(Context ctx, Intent intent) {
        final PackageManager mgr = ctx.getPackageManager();
        List<ResolveInfo> list = mgr.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        return list.size() > 0;
    }
}