com.saulmm.cui.OrderDialogFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.saulmm.cui.OrderDialogFragment.java

Source

/*
 * Copyright (C) 2017
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.saulmm.cui;

import android.databinding.BindingAdapter;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.constraint.ConstraintLayout;
import android.support.design.widget.BottomSheetDialogFragment;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewCompat;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.RelativeSizeSpan;
import android.transition.Scene;
import android.transition.Transition;
import android.transition.TransitionInflater;
import android.transition.TransitionManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.OvershootInterpolator;
import android.widget.ImageView;
import android.widget.TextView;

import com.saulmm.cui.databinding.FragmentOrderFormBinding;
import com.saulmm.cui.databinding.LayoutFormOrderStep1Binding;
import com.saulmm.cui.databinding.LayoutFormOrderStep2Binding;
import com.saulmm.cui.databinding.LayoutOrderConfirmationBinding;
import com.saulmm.cui.model.Product;

import java.util.ArrayList;
import java.util.List;

import de.hdodenhof.circleimageview.CircleImageView;

public class OrderDialogFragment extends BottomSheetDialogFragment {
    public static final String ID_SIZE_SUFFIX = "txt_size";
    public static final String ID_COLOR_SUFFIX = "img_color";
    public static final String ID_DATE_SUFFIX = "txt_date";
    public static final String ID_TIME_SUFFIX = "txt_time";
    private static final String ARG_PRODUCT = "arg_product";

    private List<View> clonedViews = new ArrayList<>();

    private FragmentOrderFormBinding binding;
    private Transition selectedViewTransition;

    private OrderSelection orderSelection;

    public static class OrderSelection {
        public int size = 0;
        public int color = 0;
        public String date = "";
        public String time = "";
    }

    public interface Step1Listener {
        void onSizeSelected(View v);

        void onColorSelected(View v);
    }

    public interface Step2Listener {
        void onDateSelected(View v);

        void onTimeSelected(View v);
    }

    public static OrderDialogFragment newInstance(Product product) {
        final Bundle args = new Bundle();
        args.putSerializable(ARG_PRODUCT, product);

        final OrderDialogFragment orderFragment = new OrderDialogFragment();
        orderFragment.setArguments(args);

        return orderFragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);
        binding = FragmentOrderFormBinding.inflate(inflater, container, false);
        return binding.getRoot();
    }

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

        orderSelection = new OrderSelection();

        selectedViewTransition = TransitionInflater.from(getContext())
                .inflateTransition(R.transition.transition_selected_view);

        binding.setProduct(getProduct());
        binding.imgProduct.setImageDrawable(createProductImageDrawable(getProduct()));

        binding.btnGo.setBackground(new ColorDrawable(ContextCompat.getColor(getContext(), getProduct().color)));

        initOrderStepOneView(binding.layoutStep1);
    }

    private void showDeliveryForm() {
        binding.switcher.setDisplayedChild(1);
        initOrderStepTwoView(binding.layoutStep2);
    }

    private void transitionSelectedView(View v) {
        // Create the cloned view from the selected view at the same position
        final View selectionView = createSelectionView(v);

        // Add the cloned view to the parent constraint layout
        if (clonedViews.size() < 2) {
            binding.mainContainer.addView(selectionView);
            clonedViews.add(selectionView);
        }

        // Perform the transition by changing constraint's layout params
        startCloneAnimation(selectionView, getTargetView(v));
    }

    private void startCloneAnimation(View clonedView, View targetView) {
        clonedView.post(() -> {
            TransitionManager.beginDelayedTransition((ViewGroup) binding.getRoot(), selectedViewTransition);

            // Fires the transition
            clonedView.setLayoutParams(SelectedParamsFactory.endParams(clonedView, targetView));
        });
    }

    private View createSelectionView(View v) {
        final String resourceName = getResources().getResourceEntryName(v.getId());

        return (resourceName.startsWith(ID_COLOR_SUFFIX)) ? createSelectedColorView((ImageView) v)
                : createSelectedTextView(v);
    }

    private View getTargetView(View v) {
        final String resourceName = getResources().getResourceEntryName(v.getId());

        if (resourceName.startsWith(ID_SIZE_SUFFIX) || resourceName.startsWith(ID_DATE_SUFFIX)) {
            v.setId(R.id.first_position);
            return binding.txtLabelSize;

        } else if (resourceName.startsWith(ID_COLOR_SUFFIX) || resourceName.startsWith(ID_TIME_SUFFIX)) {
            v.setId(R.id.second_position);
            return binding.txtLabelColour;
        }

        throw new IllegalStateException();
    }

    @BindingAdapter("app:spanOffset")
    public static void setItemSpan(View v, int spanOffset) {
        final String itemText = ((TextView) v).getText().toString();
        final SpannableString sString = new SpannableString(itemText);

        sString.setSpan(new RelativeSizeSpan(1.65f), itemText.length() - spanOffset, itemText.length(),
                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

        ((TextView) v).setText(sString);
    }

    private void initOrderStepOneView(LayoutFormOrderStep1Binding layoutStep1) {
        binding.btnGo.setOnClickListener(v -> {
            binding.txtAction.setText(R.string.action_book);

            for (View clonedView : clonedViews)
                binding.mainContainer.removeView(clonedView);

            clonedViews.clear();

            showDeliveryForm();
        });

        layoutStep1.setListener(new Step1Listener() {
            @Override
            public void onSizeSelected(View v) {
                if (orderSelection.size == 0) {
                    orderSelection.size = Integer.parseInt(((TextView) v).getText().toString());
                    v.setSelected(true);
                    transitionSelectedView(v);
                }
            }

            @Override
            public void onColorSelected(View v) {
                if (orderSelection.color == 0) {
                    orderSelection.color = ((ColorDrawable) ((ImageView) v).getDrawable()).getColor();
                    v.setSelected(true);
                    transitionSelectedView(v);
                }
            }
        });
    }

    private void initOrderStepTwoView(LayoutFormOrderStep2Binding step2Binding) {
        binding.btnGo.setOnClickListener(v -> changeToConfirmScene());

        step2Binding.setListener(new Step2Listener() {
            @Override
            public void onDateSelected(View v) {
                if (orderSelection.date.isEmpty()) {
                    orderSelection.date = getCleanedText(v);
                    v.setSelected(true);
                    transitionSelectedView(v);
                }
            }

            @Override
            public void onTimeSelected(View v) {
                if (orderSelection.time.isEmpty()) {
                    orderSelection.time = getCleanedText(v);
                    v.setSelected(true);
                    transitionSelectedView(v);
                }
            }
        });
    }

    private String getCleanedText(View v) {
        return ((TextView) v).getText().toString().replace("\n", " ");
    }

    private View createSelectedTextView(View v) {
        final TextView fakeSelectedTextView = new TextView(getContext(), null, R.attr.selectedTextStyle);

        final String resourceName = getResources().getResourceEntryName(v.getId());

        if (resourceName.startsWith(ID_DATE_SUFFIX))
            fakeSelectedTextView.setText(orderSelection.date);

        else if (resourceName.startsWith(ID_TIME_SUFFIX))
            fakeSelectedTextView.setText(orderSelection.time);

        else if (resourceName.startsWith(ID_SIZE_SUFFIX))
            fakeSelectedTextView.setText(String.valueOf(orderSelection.size));

        fakeSelectedTextView.setLayoutParams(SelectedParamsFactory.startTextParams(v));
        return fakeSelectedTextView;
    }

    private View createSelectedColorView(ImageView selectedView) {
        final ImageView fakeImageView = new CircleImageView(getContext(), null, R.attr.colorStyle);

        fakeImageView.setImageDrawable(selectedView.getDrawable());
        fakeImageView.setLayoutParams(SelectedParamsFactory.startColorParams(selectedView));
        return fakeImageView;
    }

    private void changeToConfirmScene() {
        final LayoutOrderConfirmationBinding confBinding = prepareConfirmationBinding();

        final Scene scene = new Scene(binding.content, ((ViewGroup) confBinding.getRoot()));

        scene.setEnterAction(onEnterConfirmScene(confBinding));

        final Transition transition = TransitionInflater.from(getContext())
                .inflateTransition(R.transition.transition_confirmation_view);

        TransitionManager.go(scene, transition);
    }

    private LayoutOrderConfirmationBinding prepareConfirmationBinding() {
        LayoutOrderConfirmationBinding confBinding = LayoutOrderConfirmationBinding
                .inflate(LayoutInflater.from(getContext()), binding.mainContainer, false);

        confBinding.getRoot()
                .setBackground(new ColorDrawable(ContextCompat.getColor(getContext(), getProduct().color)));

        confBinding.setProduct(getProduct());
        confBinding.setSelection(orderSelection);

        confBinding.imgProduct.setImageDrawable(ContextCompat.getDrawable(getContext(), getProduct().image));

        confBinding.txtColor.setText(
                getString(R.string.txt_label_conf_color, String.format("#%06X", (0xFFFFFF & getProduct().color))));

        return confBinding;
    }

    private Product getProduct() {
        return (Product) getArguments().getSerializable(ARG_PRODUCT);
    }

    private Drawable createProductImageDrawable(Product product) {
        final ShapeDrawable background = new ShapeDrawable();
        background.setShape(new OvalShape());
        background.getPaint().setColor(ContextCompat.getColor(getContext(), product.color));

        final BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(),
                BitmapFactory.decodeResource(getResources(), product.image));

        final LayerDrawable layerDrawable = new LayerDrawable(new Drawable[] { background, bitmapDrawable });

        final int padding = (int) getResources().getDimension(R.dimen.spacing_huge);
        layerDrawable.setLayerInset(1, padding, padding, padding, padding);

        return layerDrawable;
    }

    @NonNull
    private Runnable onEnterConfirmScene(LayoutOrderConfirmationBinding confirmationBinding) {
        return () -> {
            ViewCompat.animate(confirmationBinding.txtSubtitle).scaleX(1).scaleY(1)
                    .setInterpolator(new OvershootInterpolator()).setStartDelay(200).start();
        };
    }

    private static class SelectedParamsFactory {
        private static ConstraintLayout.LayoutParams startColorParams(View selectedView) {
            final int colorSize = selectedView.getContext().getResources()
                    .getDimensionPixelOffset(R.dimen.product_color_size);

            final ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(colorSize,
                    colorSize);

            setStartState(selectedView, layoutParams);
            return layoutParams;
        }

        private static ConstraintLayout.LayoutParams startTextParams(View selectedView) {
            final ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(
                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);

            setStartState(selectedView, layoutParams);
            return layoutParams;
        }

        private static void setStartState(View selectedView, ConstraintLayout.LayoutParams layoutParams) {
            layoutParams.topToTop = ((ViewGroup) selectedView.getParent().getParent()).getId();
            layoutParams.leftToLeft = ((ViewGroup) selectedView.getParent().getParent()).getId();
            layoutParams.setMargins((int) selectedView.getX(), (int) selectedView.getY(), 0, 0);
        }

        private static ConstraintLayout.LayoutParams endParams(View v, View targetView) {
            final ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) v.getLayoutParams();

            final int marginLeft = v.getContext().getResources().getDimensionPixelOffset(R.dimen.spacing_medium);

            layoutParams.setMargins(marginLeft, 0, 0, 0);
            layoutParams.topToTop = targetView.getId();
            layoutParams.startToEnd = targetView.getId();
            layoutParams.bottomToBottom = targetView.getId();

            return layoutParams;
        }
    }
}