ly.kite.journey.selection.ProductOverviewFragment.java Source code

Java tutorial

Introduction

Here is the source code for ly.kite.journey.selection.ProductOverviewFragment.java

Source

/*****************************************************
 *
 * ProductOverviewFragment.java
 *
 *
 * Modified MIT License
 *
 * Copyright (c) 2010-2015 Kite Tech Ltd. https://www.kite.ly
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The software MAY ONLY be used with the Kite Tech Ltd platform and MAY NOT be modified
 * to be used with any competitor platforms. This means the software MAY NOT be modified 
 * to place orders with any competitors to Kite Tech Ltd, all orders MUST go through the
 * Kite Tech Ltd platform servers. 
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 *****************************************************/

///// Package Declaration /////

package ly.kite.journey.selection;

///// Import(s) /////

///// Class Declaration /////

import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.RotateAnimation;
import android.view.animation.ScaleAnimation;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.List;
import java.util.Locale;

import ly.kite.R;
import ly.kite.address.Country;
import ly.kite.analytics.Analytics;
import ly.kite.journey.AKiteActivity;
import ly.kite.journey.AKiteFragment;
import ly.kite.catalogue.MultipleCurrencyAmount;
import ly.kite.catalogue.MultipleDestinationShippingCosts;
import ly.kite.catalogue.Product;
import ly.kite.catalogue.SingleUnitSize;
import ly.kite.catalogue.UnitOfLength;
import ly.kite.catalogue.SingleCurrencyAmount;
import ly.kite.catalogue.SingleDestinationShippingCost;
import ly.kite.widget.BellInterpolator;
import ly.kite.widget.PagingDots;
import ly.kite.widget.SlidingOverlayFrame;

/*****************************************************
 *
 * This fragment displays a product overview.
 *
 *****************************************************/
public class ProductOverviewFragment extends AKiteFragment implements View.OnClickListener {
    ////////// Static Constant(s) //////////

    @SuppressWarnings("unused")
    public static final String TAG = "ProductOverviewFragment";

    public static final String BUNDLE_KEY_PRODUCT = "product";

    private static final long PAGING_DOT_ANIMATION_DURATION_MILLIS = 300L;
    private static final float PAGING_DOT_ANIMATION_OPAQUE = 1.0f;
    private static final float PAGING_DOT_ANIMATION_TRANSLUCENT = 0.5f;
    private static final float PAGING_DOT_ANIMATION_NORMAL_SCALE = 1.0f;

    private static final long SLIDE_ANIMATION_DURATION_MILLIS = 500L;
    private static final long OPEN_CLOSE_ICON_ANIMATION_DELAY_MILLIS = 250L;
    private static final long OPEN_CLOSE_ICON_ANIMATION_DURATION_MILLIS = SLIDE_ANIMATION_DURATION_MILLIS
            - OPEN_CLOSE_ICON_ANIMATION_DELAY_MILLIS;

    private static final float OPEN_CLOSE_ICON_ROTATION_UP = -180f;
    private static final float OPEN_CLOSE_ICON_ROTATION_DOWN = 0f;

    private static final String BUNDLE_KEY_SLIDING_DRAWER_IS_EXPANDED = "slidingDrawerIsExpanded";

    ////////// Static Variable(s) //////////

    ////////// Member Variable(s) //////////

    private Product mProduct;

    private View mOverlaidComponents;
    private ViewPager mProductImageViewPager;
    private PagingDots mPagingDots;
    private Button mOverlaidStartButton;
    private SlidingOverlayFrame mSlidingOverlayFrame;
    private View mDrawerControlLayout;
    private ImageView mOpenCloseDrawerIconImageView;
    private Button mProceedOverlayButton;

    private PagerAdapter mProductImageAdaptor;

    ////////// Static Initialiser(s) //////////

    ////////// Static Method(s) //////////

    /*****************************************************
     *
     * Creates a new instance of this fragment.
     *
     *****************************************************/
    public static ProductOverviewFragment newInstance(Product product) {
        ProductOverviewFragment fragment = new ProductOverviewFragment();

        Bundle arguments = new Bundle();

        arguments.putParcelable(BUNDLE_KEY_PRODUCT, product);

        fragment.setArguments(arguments);

        return (fragment);
    }

    ////////// Constructor(s) //////////

    ////////// Activity Method(s) //////////

    /*****************************************************
     *
     * Called when the fragment is created.
     *
     *****************************************************/
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Get the product

        Bundle arguments = getArguments();

        if (arguments == null) {
            Log.e(TAG, "No arguments found");

            mKiteActivity.displayModalDialog(R.string.alert_dialog_title_no_arguments,
                    R.string.alert_dialog_message_no_arguments, AKiteActivity.NO_BUTTON, null, R.string.Cancel,
                    mKiteActivity.new FinishRunnable());

            return;
        }

        mProduct = (Product) arguments.getParcelable(BUNDLE_KEY_PRODUCT);

        if (mProduct == null) {
            Log.e(TAG, "No product found");

            mKiteActivity.displayModalDialog(R.string.alert_dialog_title_product_not_found,
                    R.string.alert_dialog_message_product_not_found, AKiteActivity.NO_BUTTON, null, R.string.Cancel,
                    mKiteActivity.new FinishRunnable());

            return;
        }
    }

    /*****************************************************
     *
     * Returns the content view for this fragment
     *
     *****************************************************/
    @Override
    public View onCreateView(LayoutInflater layoutInflator, ViewGroup container, Bundle savedInstanceState) {
        boolean slidingDrawerIsExpanded = false;

        // Get any saved instance state

        if (savedInstanceState != null) {
            slidingDrawerIsExpanded = savedInstanceState.getBoolean(BUNDLE_KEY_SLIDING_DRAWER_IS_EXPANDED, false);
        } else {
            Analytics.getInstance(mKiteActivity).trackProductOverviewScreenViewed(mProduct);
        }

        // Set up the screen. Note that the SDK allows for different layouts to be used in place of the standard
        // one, so some of these views are optional and may not actually exist in the current layout.

        View view = layoutInflator.inflate(R.layout.screen_product_overview, container, false);

        mProductImageViewPager = (ViewPager) view.findViewById(R.id.view_pager);
        mOverlaidComponents = view.findViewById(R.id.overlaid_components);
        mPagingDots = (PagingDots) view.findViewById(R.id.paging_dots);
        mOverlaidStartButton = (Button) view.findViewById(R.id.overlaid_start_button);
        mSlidingOverlayFrame = (SlidingOverlayFrame) view.findViewById(R.id.sliding_overlay_frame);
        mDrawerControlLayout = view.findViewById(R.id.drawer_control_layout);
        mOpenCloseDrawerIconImageView = (ImageView) view.findViewById(R.id.open_close_drawer_icon_image_view);
        mProceedOverlayButton = (Button) view.findViewById(R.id.proceed_overlay_button);
        TextView priceTextView = (TextView) view.findViewById(R.id.price_text_view);
        TextView summaryDescriptionTextView = (TextView) view.findViewById(R.id.summary_description_text_view);
        TextView summaryShippingTextView = (TextView) view.findViewById(R.id.summary_shipping_text_view);
        View descriptionLayout = view.findViewById(R.id.description_layout);
        TextView descriptionTextView = (TextView) view.findViewById(R.id.description_text_view);
        View sizeLayout = view.findViewById(R.id.size_layout);
        TextView sizeTextView = (TextView) view.findViewById(R.id.size_text_view);
        View quantityLayout = view.findViewById(R.id.quantity_layout);
        TextView quantityTextView = (TextView) view.findViewById(R.id.quantity_text_view);
        TextView shippingTextView = (TextView) view.findViewById(R.id.shipping_text_view);

        // Paging dots

        Animation pagingDotOutAlphaAnimation = new AlphaAnimation(PAGING_DOT_ANIMATION_OPAQUE,
                PAGING_DOT_ANIMATION_TRANSLUCENT);
        pagingDotOutAlphaAnimation.setFillAfter(true);
        pagingDotOutAlphaAnimation.setDuration(PAGING_DOT_ANIMATION_DURATION_MILLIS);

        Animation pagingDotOutScaleAnimation = new ScaleAnimation(0f, PAGING_DOT_ANIMATION_NORMAL_SCALE, 0f,
                PAGING_DOT_ANIMATION_NORMAL_SCALE, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
                0.5f);

        pagingDotOutScaleAnimation.setFillAfter(true);
        pagingDotOutScaleAnimation.setDuration(PAGING_DOT_ANIMATION_DURATION_MILLIS);
        pagingDotOutScaleAnimation.setInterpolator(new BellInterpolator(1.0f, 0.8f, true));

        AnimationSet pagingDotOutAnimation = new AnimationSet(false);
        pagingDotOutAnimation.addAnimation(pagingDotOutAlphaAnimation);
        pagingDotOutAnimation.addAnimation(pagingDotOutScaleAnimation);
        pagingDotOutAnimation.setFillAfter(true);

        Animation pagingDotInAlphaAnimation = new AlphaAnimation(PAGING_DOT_ANIMATION_TRANSLUCENT,
                PAGING_DOT_ANIMATION_OPAQUE);
        pagingDotInAlphaAnimation.setFillAfter(true);
        pagingDotInAlphaAnimation.setDuration(PAGING_DOT_ANIMATION_DURATION_MILLIS);

        Animation pagingDotInScaleAnimation = new ScaleAnimation(0f, PAGING_DOT_ANIMATION_NORMAL_SCALE, 0f,
                PAGING_DOT_ANIMATION_NORMAL_SCALE, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
                0.5f);

        pagingDotInScaleAnimation.setFillAfter(true);
        pagingDotInScaleAnimation.setDuration(PAGING_DOT_ANIMATION_DURATION_MILLIS);
        pagingDotInScaleAnimation.setInterpolator(new BellInterpolator(1.0f, 1.2f));

        AnimationSet pagingDotInAnimation = new AnimationSet(false);
        pagingDotInAnimation.addAnimation(pagingDotInAlphaAnimation);
        pagingDotInAnimation.addAnimation(pagingDotInScaleAnimation);
        pagingDotInAnimation.setFillAfter(true);

        mPagingDots.setProperties(mProduct.getImageURLList().size(), R.drawable.paging_dot_unselected,
                R.drawable.paging_dot_selected);
        mPagingDots.setOutAnimation(pagingDotOutAlphaAnimation);
        mPagingDots.setInAnimation(pagingDotInAnimation);

        mProductImageViewPager.setOnPageChangeListener(mPagingDots);

        mOverlaidComponents.setAlpha(slidingDrawerIsExpanded ? 0f : 1f); // If the drawer starts open, these components need to be invisible

        if (mSlidingOverlayFrame != null) {
            mSlidingOverlayFrame.snapToExpandedState(slidingDrawerIsExpanded);
            mSlidingOverlayFrame.setSlideAnimationDuration(SLIDE_ANIMATION_DURATION_MILLIS);
            mOpenCloseDrawerIconImageView.setRotation(
                    slidingDrawerIsExpanded ? OPEN_CLOSE_ICON_ROTATION_DOWN : OPEN_CLOSE_ICON_ROTATION_UP);
        }

        SingleUnitSize size = mProduct.getSizeWithFallback(UnitOfLength.CENTIMETERS);
        boolean formatAsInt = size.getWidth() == (int) size.getWidth()
                && size.getHeight() == (int) size.getHeight();
        String sizeFormatString = getString(
                formatAsInt ? R.string.product_size_format_string_int : R.string.product_size_format_string_float);
        String sizeString = String.format(sizeFormatString, size.getWidth(), size.getHeight(),
                size.getUnit().shortString(mKiteActivity));

        int quantityPerSheet = mProduct.getQuantityPerSheet();
        MultipleDestinationShippingCosts shippingCosts = mProduct.getShippingCosts();

        Locale locale = Locale.getDefault();
        Country country = Country.getInstance(locale);

        SingleCurrencyAmount singleCurrencyCost;

        // Price

        if (isVisible(priceTextView)) {
            singleCurrencyCost = mProduct.getCostWithFallback(locale);

            if (singleCurrencyCost != null)
                priceTextView.setText(singleCurrencyCost.getDisplayAmountForLocale(locale));
        }

        // Summary description. This is a short description - not to be confused with the (full) description.

        if (isVisible(summaryDescriptionTextView)) {
            String summaryDescription = String.valueOf(quantityPerSheet) + " " + mProduct.getName()
                    + (Product.isSensibleSize(size) ? " (" + sizeString + ")" : "");

            summaryDescriptionTextView.setText(summaryDescription);
        }

        // (Full) description

        String description = mProduct.getDescription();

        boolean haveDescription = (description != null && (!description.trim().equals("")));

        if (haveDescription && descriptionLayout != null && descriptionTextView != null) {
            descriptionLayout.setVisibility(View.VISIBLE);
            descriptionTextView.setVisibility(View.VISIBLE);

            descriptionTextView.setText(description);
        } else {
            if (descriptionLayout != null)
                descriptionLayout.setVisibility(View.GONE);
            if (descriptionTextView != null)
                descriptionTextView.setVisibility(View.GONE);
        }

        // Size

        if (isVisible(sizeTextView)) {
            if (Product.isSensibleSize(size)) {
                sizeTextView.setText(String.format(sizeFormatString, size.getWidth(), size.getHeight(),
                        size.getUnit().shortString(mKiteActivity)));
            } else {
                sizeLayout.setVisibility(View.GONE);
            }
        }

        // Quantity

        if (isVisible(quantityTextView)) {
            if (quantityPerSheet > 1) {
                quantityLayout.setVisibility(View.VISIBLE);

                quantityTextView.setText(getString(R.string.product_quantity_format_string, quantityPerSheet));
            } else {
                quantityLayout.setVisibility(View.GONE);
            }
        }

        // Shipping description

        if (isVisible(summaryShippingTextView)) {
            // Currently we just check that shipping is free everywhere. If it isn't - we don't display
            // anything.

            boolean freeShippingEverywhere = true;

            MultipleDestinationShippingCosts multipleDestinationShippingCosts = shippingCosts;

            for (SingleDestinationShippingCost singleDestinationShippingCosts : multipleDestinationShippingCosts
                    .asList()) {
                MultipleCurrencyAmount multipleCurrencyShippingCost = singleDestinationShippingCosts.getCost();

                for (SingleCurrencyAmount singleCurrencyShippingCost : multipleCurrencyShippingCost
                        .asCollection()) {
                    if (singleCurrencyShippingCost.isNonZero()) {
                        freeShippingEverywhere = false;
                    }
                }
            }

            if (freeShippingEverywhere) {
                summaryShippingTextView.setText(R.string.product_free_worldwide_shipping);
            } else {
                summaryShippingTextView.setText(getString(R.string.product_shipping_summary_format_string,
                        shippingCosts.getDisplayCost(locale)));
            }
        }

        // Shipping (postage)

        if (isVisible(shippingTextView)) {
            List<SingleDestinationShippingCost> sortedShippingCostList = mProduct.getSortedShippingCosts(country);

            StringBuilder shippingCostsStringBuilder = new StringBuilder();

            String newlineString = "";

            for (SingleDestinationShippingCost singleDestinationShippingCost : sortedShippingCostList) {
                // We want to prepend a new line for every shipping destination except the first

                shippingCostsStringBuilder.append(newlineString);

                newlineString = "\n";

                // Get the cost in the default currency for the locale, and format the amount.

                singleCurrencyCost = singleDestinationShippingCost.getCost().getDefaultAmountWithFallback();

                if (singleCurrencyCost != null) {
                    String formatString = getString(R.string.product_shipping_format_string);

                    String costString = (singleCurrencyCost.isNonZero()
                            ? singleCurrencyCost.getDisplayAmountForLocale(locale)
                            : getString(R.string.product_free_shipping));

                    shippingCostsStringBuilder.append(String.format(formatString,
                            singleDestinationShippingCost.getDestinationDescription(mKiteActivity), costString));
                }

                shippingTextView.setText(shippingCostsStringBuilder.toString());
            }
        }

        if (mProceedOverlayButton != null) {
            mProceedOverlayButton.setText(R.string.product_overview_start_button_text);

            mProceedOverlayButton.setOnClickListener(this);
        }

        mProductImageViewPager.setOnClickListener(this);

        if (mDrawerControlLayout != null)
            mDrawerControlLayout.setOnClickListener(this);

        mOverlaidStartButton.setOnClickListener(this);

        return (view);
    }

    /*****************************************************
     *
     * Called when the back key is pressed. The fragment
     * can either intercept it, or ignore it - in which case
     * the default behaviour is performed.
     *
     *****************************************************/
    @Override
    public boolean onBackPressIntercepted() {
        // If the slider is open - close it
        if (mSlidingOverlayFrame != null && mSlidingOverlayFrame.sliderIsExpanded()) {
            toggleSliderState();

            return (true);
        }

        return (false);
    }

    /*****************************************************
     *
     * Called to save the state of the instance when (e.g.)
     * changing orientation.
     *
     *****************************************************/
    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        // Save the state of the sliding drawer
        if (mSlidingOverlayFrame != null) {
            outState.putBoolean(BUNDLE_KEY_SLIDING_DRAWER_IS_EXPANDED, mSlidingOverlayFrame.sliderIsExpanded());
        }
    }

    /*****************************************************
     *
     * Called when the fragment is top-most.
     *
     *****************************************************/
    @Override
    public void onTop() {
        super.onTop();

        if (mProduct != null)
            mKiteActivity.setTitle(mProduct.getName());

        // Set up the product image gallery
        mProductImageAdaptor = new ProductImagePagerAdaptor(mKiteActivity, mProduct.getImageURLList(), this);
        mProductImageViewPager.setAdapter(mProductImageAdaptor);
    }

    /*****************************************************
     *
     * Called when the fragment is not on top.
     *
     *****************************************************/
    @Override
    public void onNotTop() {
        super.onNotTop();

        // Clear out the stored images to reduce memory usage
        // when not on this screen.

        if (mProductImageViewPager != null)
            mProductImageViewPager.setAdapter(null);

        mProductImageAdaptor = null;
    }

    ////////// View.OnClickListener Method(s) //////////

    /*****************************************************
     *
     * Called when a view is clicked.
     *
     *****************************************************/
    @Override
    public void onClick(View view) {
        // Anything that's not the drawer control is assumed to be
        // one of the start buttons.

        if (view == mDrawerControlLayout) {
            toggleSliderState();
        } else {
            onCreateProduct();
        }
    }

    ////////// Method(s) //////////

    /*****************************************************
     *
     * Returns true if a view is visible, false otherwise.
     *
     *****************************************************/
    private boolean isVisible(View view) {
        return (view != null && view.getVisibility() == View.VISIBLE);
    }

    /*****************************************************
     *
     * Called when one of the start creating buttons is clicked.
     *
     *****************************************************/
    public void onCreateProduct() {
        // Call back to the activity
        if (mKiteActivity instanceof ICallback) {
            ((ICallback) mKiteActivity).poOnCreateProduct(mProduct);
        }
    }

    /*****************************************************
     *
     * Called when the details control is clicked.
     *
     *****************************************************/
    private void toggleSliderState() {
        // We want to animation the following:
        //   - Overlaid start button fade in / out
        //   - Sliding drawer up / down
        //   - Open / close drawer icon rotation

        boolean sliderWillBeOpening = !mSlidingOverlayFrame.sliderIsExpanded();

        float overlaidComponentsInitialAlpha;
        float overlaidComponentsFinalAlpha;

        float openCloseIconInitialRotation;
        float openCloseIconFinalRotation;

        if (sliderWillBeOpening) {
            overlaidComponentsInitialAlpha = 1f;
            overlaidComponentsFinalAlpha = 0f;

            openCloseIconInitialRotation = OPEN_CLOSE_ICON_ROTATION_UP;
            openCloseIconFinalRotation = OPEN_CLOSE_ICON_ROTATION_DOWN;
        } else {
            overlaidComponentsInitialAlpha = 0f;
            overlaidComponentsFinalAlpha = 1f;

            openCloseIconInitialRotation = OPEN_CLOSE_ICON_ROTATION_DOWN;
            openCloseIconFinalRotation = OPEN_CLOSE_ICON_ROTATION_UP;
        }

        // Create the overlaid components animation
        Animation overlaidComponentsAnimation = new AlphaAnimation(overlaidComponentsInitialAlpha,
                overlaidComponentsFinalAlpha);
        overlaidComponentsAnimation.setDuration(SLIDE_ANIMATION_DURATION_MILLIS);
        overlaidComponentsAnimation.setFillAfter(true);

        // Create the open/close icon animation.
        // The rotation is delayed, but will finish at the same time as the slide animation.
        Animation openCloseIconAnimation = new RotateAnimation(openCloseIconInitialRotation,
                openCloseIconFinalRotation, mOpenCloseDrawerIconImageView.getWidth() * 0.5f,
                mOpenCloseDrawerIconImageView.getHeight() * 0.5f);
        openCloseIconAnimation.setStartOffset(OPEN_CLOSE_ICON_ANIMATION_DELAY_MILLIS);
        openCloseIconAnimation.setDuration(OPEN_CLOSE_ICON_ANIMATION_DURATION_MILLIS);
        openCloseIconAnimation.setFillAfter(true);

        if (mOverlaidComponents != null) {
            mOverlaidComponents.setAlpha(1f); // Clear any alpha already applied
            mOverlaidComponents.startAnimation(overlaidComponentsAnimation);
        }

        if (mOpenCloseDrawerIconImageView != null) {
            mOpenCloseDrawerIconImageView.setRotation(0f); // Clear any rotation already applied
            mOpenCloseDrawerIconImageView.startAnimation(openCloseIconAnimation);
        }

        mSlidingOverlayFrame.animateToExpandedState(sliderWillBeOpening);
    }

    ////////// Inner Class(es) //////////

    /*****************************************************
     *
     * A callback interface.
     *
     *****************************************************/
    public interface ICallback {
        public void poOnCreateProduct(Product product);
    }

}