/*
* Copyright 2015 sourcestream GmbH
*
* 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 de.sourcestream.movieDB.controller;
import android.app.Fragment;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.AbsListView;
import de.sourcestream.movieDB.MainActivity;
import de.sourcestream.movieDB.R;
import de.sourcestream.movieDB.adapter.MovieSlideAdapter;
import de.sourcestream.movieDB.helper.ObservableScrollViewCallbacks;
import de.sourcestream.movieDB.helper.ScrollState;
import de.sourcestream.movieDB.helper.Scrollable;
import de.sourcestream.movieDB.view.SlidingTabLayout;
/**
* A basic sample which shows how to use {@link de.sourcestream.movieDB.view.SlidingTabLayout}
* to display a custom {@link android.support.v4.view.ViewPager} title strip which gives continuous feedback to the user
* when scrolling.
* This class configures the ViewPager in the Movies and has some custom methods for the sliding toolbar
*/
public class MovieSlideTab extends Fragment implements ObservableScrollViewCallbacks {
/**
* A custom {@link android.support.v4.view.ViewPager} title strip which looks much like Tabs present in Android v4.0 and
* above, but is designed to give continuous feedback to the user when scrolling.
*/
private SlidingTabLayout mSlidingTabLayout;
/**
* A {@link android.support.v4.view.ViewPager} which will be used in conjunction with the {@link SlidingTabLayout} above.
*/
private ViewPager mViewPager;
private MovieSlideAdapter adapter;
private onPageChangeSelected onPageChangeSelected;
private MainActivity activity;
private Bundle savedInstanceState;
private int currPos;
private int oldScrollY;
private float dy;
private float upDy;
private float downDy;
private float downDyTrans;
private boolean upDyKey;
private boolean downDyKey;
private float scale;
private boolean phone;
private int hideThreshold;
private int minThreshold;
/**
* Inflates the {@link android.view.View} which will be displayed by this {@link Fragment}, from the app's
* resources.
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.moviepager, container, false);
// Get the ViewPager and set it's PagerAdapter so that it can display items
adapter = new MovieSlideAdapter(getFragmentManager(), getResources());
mViewPager = (ViewPager) view.findViewById(R.id.moviePager);
mViewPager.setOffscreenPageLimit(1);
mViewPager.setAdapter(adapter);
// Give the SlidingTabLayout the ViewPager, this must be done AFTER the ViewPager has had
// it's PagerAdapter set.
mSlidingTabLayout = (SlidingTabLayout) view.findViewById(R.id.sliding_tabs);
mSlidingTabLayout.setViewPager(mViewPager);
onPageChangeSelected = new onPageChangeSelected();
activity = ((MainActivity) getActivity());
mSlidingTabLayout.setOnPageChangeListener(onPageChangeSelected);
mSlidingTabLayout.setSelectedIndicatorColors(ContextCompat.getColor(activity, R.color.tabSelected));
phone = getResources().getBoolean(R.bool.portrait_only);
scale = getResources().getDisplayMetrics().density;
if (phone) {
hideThreshold = (int) (-105 * scale);
minThreshold = (int) (-49 * scale);
} else {
hideThreshold = (int) (-100 * scale);
minThreshold = (int) (-42 * scale);
}
return view;
}
/**
* This is called after the {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} has finished.
* Here we can pick out the {@link View}s we need to configure from the content view.
* <p/>
* We set the {@link ViewPager}'s adapter to be an instance of {@link MovieSlideAdapter}. The
* {@link SlidingTabLayout} is then given the {@link ViewPager} so that it can populate itself.
*
* @param view View created in {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}
*/
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
this.savedInstanceState = savedInstanceState;
if (activity.getReAttachMovieFragments()) {
adapter.reAttachFragments(mViewPager);
activity.setReAttachMovieFragments(false);
}
currPos = activity.getCurrentMovViewPagerPos();
mViewPager.setCurrentItem(currPos);
onPageChangeSelected.onPageSelected(currPos);
activity.setMovieSlideTab(this);
showInstantToolbar();
}
/**
* Class which listens when the user has changed the tap in Cast details
*/
public class onPageChangeSelected implements ViewPager.OnPageChangeListener {
MovieList fragment;
@Override
public void onPageScrollStateChanged(int state) {
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
// Finds the current fragment and updates the list if needed
// On orientation change loses reference to fragments, that's why every time we find our fragment.
if (MainActivity.getMaxMem() / 1048576 <= 20) {
if (currPos != position) {
// Find the old fragment and clear the list to save up memory
MovieList oldFragment = (MovieList) getFragmentManager().findFragmentByTag(getFragmentTag(currPos));
oldFragment.cleanUp();
oldFragment.setFragmentActive(false);
}
}
currPos = position;
activity.setCurrentMovViewPagerPos(position);
boolean load = true;
if (savedInstanceState != null)
load = savedInstanceState.getBoolean("load");
fragment = (MovieList) getFragmentManager().findFragmentByTag(getFragmentTag(position));
if (fragment != null) {
fragment.setFragmentActive(true);
if (fragment.getMoviesList() != null && fragment.getMoviesList().size() > 0) {
final AbsListView listView = fragment.getListView();
final View toolbarView = activity.findViewById(R.id.toolbar);
if (listView != null) {
listView.post(new Runnable() {
@Override
public void run() {
// check if toolbar is hidden and minThreshold to scroll
if (toolbarView.getTranslationY() == -toolbarView.getHeight() && ((Scrollable) listView).getCurrentScrollY() < minThreshold) {
if (phone)
listView.smoothScrollBy((int) (56 * scale), 0);
else
listView.smoothScrollBy((int) (59 * scale), 0);
}
}
});
}
}
if (load) {
if (fragment.getBackState() == 0)
fragment.updateList();
else
fragment.setAdapter();
} else savedInstanceState.putBoolean("load", true);
}
}
}
/**
* This is used to get the fragment id. This way we know which fragments are active.
*
* @param pos the position we are looking for.
* @return id of the fragment.
*/
public String getFragmentTag(int pos) {
return "android:switcher:" + R.id.moviePager + ":" + pos;
}
/**
* Returns the current position of the Viewpager.
*/
public int getCurrPos() {
return currPos;
}
/**
* Called to ask the fragment to save its current dynamic state,
* so it can later be reconstructed in a new instance of its process is restarted.
*
* @param outState Bundle in which to place your saved state.
*/
// Prevent onPageSelected event to be fired on orientation change
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
boolean load;
if (currPos != 0)
load = false;
else
load = true;
outState.putBoolean("load", load);
}
@Override
public void onScrollChanged(int scrollY, boolean firstScroll, boolean dragging) {
if (dragging) {
View toolbarView = getActivity().findViewById(R.id.toolbar);
if (scrollY > oldScrollY) {
if (upDyKey) {
upDy = scrollY;
upDyKey = false;
} else {
dy = upDy - scrollY;
if (dy >= -toolbarView.getHeight()) {
toolbarView.setTranslationY(dy);
mSlidingTabLayout.setTranslationY(dy);
} else {
toolbarView.setTranslationY(-toolbarView.getHeight());
mSlidingTabLayout.setTranslationY(-toolbarView.getHeight());
}
downDyKey = true;
}
}
if (scrollY < oldScrollY) {
if (downDyKey) {
downDy = scrollY;
downDyTrans = toolbarView.getTranslationY();
downDyKey = false;
} else {
dy = (downDyTrans + (downDy - scrollY));
if (dy <= 0) {
toolbarView.setTranslationY(dy);
mSlidingTabLayout.setTranslationY(dy);
} else {
toolbarView.setTranslationY(0);
mSlidingTabLayout.setTranslationY(0);
}
upDyKey = true;
}
}
}
oldScrollY = scrollY;
}
@Override
public void onDownMotionEvent() {
}
@Override
public void onUpOrCancelMotionEvent(ScrollState scrollState) {
adjustToolbar(scrollState);
}
private Scrollable getCurrentScrollable() {
Fragment fragment = getCurrentFragment();
if (fragment == null) {
return null;
}
View view = fragment.getView();
if (view == null) {
return null;
}
switch (mViewPager.getCurrentItem()) {
case 0:
return (Scrollable) view.findViewById(R.id.movieslist);
case 1:
return (Scrollable) view.findViewById(R.id.nowplaying);
case 2:
return (Scrollable) view.findViewById(R.id.popular);
case 3:
return (Scrollable) view.findViewById(R.id.toprated);
default:
return (Scrollable) view.findViewById(R.id.movieslist);
}
}
/**
* Fixes the position of the toolbar
*
* @param scrollState
*/
private void adjustToolbar(ScrollState scrollState) {
View toolbarView = activity.findViewById(R.id.toolbar);
int toolbarHeight = toolbarView.getHeight();
final Scrollable scrollable = getCurrentScrollable();
if (scrollable == null) {
return;
}
int scrollY = scrollable.getCurrentScrollY();
if (scrollState == ScrollState.DOWN) {
showToolbar();
} else if (scrollState == ScrollState.UP) {
if (toolbarHeight <= scrollY - hideThreshold) {
hideToolbar();
} else {
showToolbar();
}
}
}
/**
* Returns the current active fragment for the given position
*/
private Fragment getCurrentFragment() {
return getFragmentManager().findFragmentByTag(getFragmentTag(mViewPager.getCurrentItem()));
}
private void showToolbar() {
animateToolbar(0);
}
private void hideToolbar() {
View toolbarView = getActivity().findViewById(R.id.toolbar);
animateToolbar(-toolbarView.getHeight());
}
/**
* Animates our toolbar to the given direction
*
* @param toY our translation length.
*/
private void animateToolbar(final float toY) {
if (activity != null) {
View toolbarView = activity.findViewById(R.id.toolbar);
if (toolbarView != null) {
toolbarView.animate().translationY(toY).setInterpolator(new DecelerateInterpolator(2)).setDuration(200).start();
mSlidingTabLayout.animate().translationY(toY).setInterpolator(new DecelerateInterpolator(2)).setDuration(200).start();
if (toY == 0) {
upDyKey = true;
downDyKey = false;
downDy = 9999999;
} else {
downDyKey = true;
upDyKey = false;
upDy = -9999999;
}
}
}
}
/**
* Instant shows our toolbar. Used when click on movie details from movies list and toolbar is hidden.
*/
public void showInstantToolbar() {
if (activity != null) {
View toolbarView = activity.findViewById(R.id.toolbar);
if (toolbarView != null) {
toolbarView.animate().translationY(0).setInterpolator(new DecelerateInterpolator(2)).setDuration(0).start();
mSlidingTabLayout.animate().translationY(0).setInterpolator(new DecelerateInterpolator(2)).setDuration(0).start();
upDyKey = true;
downDyKey = false;
downDy = 9999999;
}
}
}
/**
* Fired when fragment is destroyed.
*/
public void onDestroyView() {
super.onDestroyView();
onPageChangeSelected = null;
}
}