com.melkir.ourstreets.fragment.GalleryFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.melkir.ourstreets.fragment.GalleryFragment.java

Source

/*
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * 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.melkir.ourstreets.fragment;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.animation.FastOutLinearInInterpolator;
import android.support.v7.widget.RecyclerView;
import android.transition.Explode;
import android.transition.Fade;
import android.transition.TransitionSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.widget.FrameLayout;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.model.CameraPosition;
import com.melkir.ourstreets.R;
import com.melkir.ourstreets.data.DataView;
import com.melkir.ourstreets.data.GalleryPresenter;
import com.melkir.ourstreets.model.Gallery;
import com.melkir.ourstreets.transition.Elevation;
import com.melkir.ourstreets.view.GalleryAdapter;
import com.melkir.ourstreets.view.GalleryDivider;
import com.melkir.ourstreets.view.GalleryViewHolder;
import com.melkir.ourstreets.view.RecyclerItemClickListener;
import com.melkir.ourstreets.view.ViewUtils;

import java.util.List;

/**
 * A {@link Fragment} that displays an overview of all available StreetView galleries.
 */
public class GalleryFragment extends Fragment implements DataView<Gallery> {

    public static final String TAG = "GalleryFragment";

    private static final FastOutLinearInInterpolator INTERPOLATOR = new FastOutLinearInInterpolator();

    private List<Gallery> mGalleries;
    private RecyclerView.Adapter mAdapter;
    private RecyclerView mRecyclerView;
    private View mEmptyView;
    private View mGalleryContent;

    private boolean mAnimateViewSwap;

    public GalleryFragment() {

        final Fade fade = new Fade();
        fade.addTarget(R.id.appbar);

        Explode explode = new Explode();
        explode.excludeTarget(R.id.appbar, true);

        Elevation elevation = new Elevation();
        elevation.addTarget(R.id.gallery_card);
        elevation.setStartDelay(250); // arbitrarily chosen delay

        TransitionSet exit = new TransitionSet();
        exit.addTransition(fade);
        exit.addTransition(explode);
        exit.addTransition(elevation);

        setExitTransition(exit);
    }

    public static GalleryFragment newInstance() {
        return new GalleryFragment();
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        ViewUtils.setStatusBarColor(getActivity(), R.color.background);
        return inflater.inflate(R.layout.fragment_gallery, container, false);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        ViewCompat.requestApplyInsets(view);
        mEmptyView = view.findViewById(R.id.progress);
        mGalleryContent = view.findViewById(R.id.gallery_content);
        initRecyclerView(view);
        mAnimateViewSwap = savedInstanceState == null && mGalleries == null;
        if (mGalleries == null) {
            new GalleryPresenter(this).getData();
        } else {
            showData(mGalleries);
        }
    }

    @Override
    public void onDestroyView() {
        mRecyclerView = null;
        mEmptyView = null;
        mGalleryContent = null;
        super.onDestroyView();
    }

    @Override
    public void showData(List<Gallery> galleries) {
        mGalleries = galleries;
        initRecyclerViewAdapter();
        showContent(mAnimateViewSwap);
    }

    @Override
    public void showError() {
        mGalleryContent.setVisibility(View.GONE);
        mEmptyView.setVisibility(View.GONE);
        //noinspection ConstantConditions
        View failedContainer = getView().findViewById(R.id.failedStub);
        if (failedContainer == null) {
            getView().findViewById(R.id.failed).setVisibility(View.VISIBLE);
            // Loading already failed before. Everything else is already inflated and set up.
            return;
        }
        setupAndInflate((ViewStub) failedContainer);
    }

    private void initRecyclerView(View contentView) {
        mRecyclerView = (RecyclerView) contentView.findViewById(R.id.galleries);
        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        mRecyclerView.setHasFixedSize(true);
        mRecyclerView.addOnItemTouchListener(new RecyclerItemClickListener(getActivity()) {
            @Override
            public void onItemClick(RecyclerView.ViewHolder holder, int position) {
                showDetailFragment((GalleryViewHolder) holder, mGalleries.get(position));
            }
        });
        final int itemMargin = getResources().getDimensionPixelSize(R.dimen.grid_single);
        mRecyclerView.addItemDecoration(new GalleryDivider(itemMargin));
    }

    private void initRecyclerViewAdapter() {
        if (mRecyclerView == null || mGalleries == null) {
            Log.d(TAG, "initRecyclerViewAdapter: Missing data, not initializing.");
            return;
        }
        if (mAdapter == null) {
            mAdapter = new GalleryAdapter(mGalleries, getContext());
        }
        mRecyclerView.setAdapter(mAdapter);
    }

    private void showContent(boolean animate) {
        if (!isAdded()) {
            Log.d(TAG, "showData: Not showing data");
            return;
        }
        if (animate) {
            safelyAnimateProgressToContent();
        } else {
            swapEmptyWithContentView(false);
        }
    }

    private void showDetailFragment(@NonNull final GalleryViewHolder holder, @NonNull final Gallery gallery) {
        // Turn of transition grouping for clicked item view to break card structure.
        ((ViewGroup) holder.itemView).setTransitionGroup(false);
        holder.mapView.getMapAsync(new OnMapReadyCallback() {
            @Override
            public void onMapReady(GoogleMap googleMap) {
                CameraPosition cameraPosition = googleMap.getCameraPosition();
                performDetailTransition(holder, gallery, cameraPosition);
            }
        });
    }

    private void performDetailTransition(@NonNull GalleryViewHolder itemHolder, @NonNull Gallery gallery,
            CameraPosition cameraPosition) {
        DetailFragment fragment = getDetailFragment(gallery, cameraPosition);
        FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
        @SuppressLint("CommitTransaction")
        FragmentTransaction transaction = fragmentManager.beginTransaction().replace(android.R.id.content, fragment,
                DetailFragment.TAG);
        createAndAddTransitionParticipants(itemHolder, transaction);
        transaction.addToBackStack(DetailFragment.TAG).commit();
    }

    @NonNull
    private DetailFragment getDetailFragment(Gallery gallery, CameraPosition cameraPosition) {
        FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
        DetailFragment detailFragment = (DetailFragment) fragmentManager.findFragmentByTag(DetailFragment.TAG);
        if (detailFragment == null) {
            detailFragment = DetailFragment.newInstance(getActivity(), gallery, cameraPosition);
        } else {
            detailFragment.setNewDetailInformation(gallery, cameraPosition);
        }
        return detailFragment;
    }

    private void createAndAddTransitionParticipants(@NonNull GalleryViewHolder itemHolder,
            @NonNull FragmentTransaction transaction) {
        transaction.addSharedElement(itemHolder.descriptionContainer, getString(R.string.transition_description));
        transaction.addSharedElement(itemHolder.mapView, getString(R.string.transition_map));
    }

    /**
     * Add animation safety by making sure that the fragment currently is attached.
     */
    private void safelyAnimateProgressToContent() {
        if (mGalleryContent.isAttachedToWindow()) {
            animateProgressToContent();
        } else {
            mGalleryContent.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
                @Override
                public void onViewAttachedToWindow(View v) {
                    v.removeOnAttachStateChangeListener(this);
                    animateProgressToContent();
                }

                @Override
                public void onViewDetachedFromWindow(View v) {
                    v.removeOnAttachStateChangeListener(this);
                }
            });
        }
    }

    /**
     * Perform the animation from loading progress to the actual fragment's content.
     */
    private void animateProgressToContent() {
        AnimatorSet animatorSet = new AnimatorSet();
        FrameLayout targetView = ((FrameLayout) getView());
        //noinspection ConstantConditions
        animatorSet.play(createCircularReveal(targetView)).with(createColorChange(targetView));
        animatorSet.start();
    }

    @NonNull
    private Animator createCircularReveal(@NonNull View targetView) {
        Animator circularReveal = ViewUtils.createCircularReveal(targetView, R.id.progress, INTERPOLATOR);
        circularReveal.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                swapEmptyWithContentView(true);
            }
        });
        return circularReveal;
    }

    @NonNull
    private ObjectAnimator createColorChange(@NonNull FrameLayout targetView) {
        return ViewUtils.createColorChange(targetView, R.color.blue_grey_600, android.R.color.transparent,
                INTERPOLATOR);
    }

    /**
     * Replace the empty view with the gallery view.
     *
     * @param animate <code>true</code> if the swap should be animated, else <code>false</code>.
     */
    private void swapEmptyWithContentView(boolean animate) {
        if (animate) {
            mEmptyView.animate().alpha(0f).setInterpolator(INTERPOLATOR).withEndAction(new Runnable() {
                @Override
                public void run() {
                    mEmptyView.setVisibility(View.GONE);
                }
            });
            mGalleryContent.setAlpha(0f);
            mGalleryContent.setVisibility(View.VISIBLE);
            mGalleryContent.animate().alpha(1f).setInterpolator(INTERPOLATOR).start();
        } else {
            mEmptyView.setVisibility(View.GONE);
            mGalleryContent.setVisibility(View.VISIBLE);
        }
    }

    private void setupAndInflate(ViewStub failedStub) {
        failedStub.setOnInflateListener(new ViewStub.OnInflateListener() {
            @Override
            public void onInflate(ViewStub stub, final View inflated) {
                inflated.findViewById(R.id.try_again).setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        new GalleryPresenter(GalleryFragment.this).getData();
                        mEmptyView.setVisibility(View.VISIBLE);
                        inflated.setVisibility(View.GONE);
                    }
                });
            }
        });
        failedStub.inflate();
    }
}