/*
* Copyright (C) 2013 Evgeny Shishkin
*
* 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.minggo.pluto.fragment;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.widget.TextView;
import com.minggo.pluto.R;
/**
* The implementation of the fragment to display content. Based on {@link android.support.v4.app.ListFragment}.
* If you are waiting for the initial data, you'll can displaying during this time an indeterminate progress indicator.
*
* @author Evgeny Shishkin
*/
public class ProgressFragment extends Fragment {
private View mProgressContainer;
private View mContentContainer;
private View mContentView;
private View mEmptyView;
private boolean mContentShown;
private boolean mIsContentEmpty;
public ProgressFragment() {
}
/**
* Provide default implementation to return a simple view. Subclasses
* can override to replace with their own layout. If doing so, the
* returned view hierarchy <em>must</em> have a progress container whose id
* is {@link com.devspark.progressfragment.R.id#progress_container R.id.progress_container}, content container whose id
* is {@link com.devspark.progressfragment.R.id#content_container R.id.content_container} and can optionally
* have a sibling view id {@link android.R.id#empty android.R.id.empty}
* that is to be shown when the content is empty.
* <p/>
* <p>If you are overriding this method with your own custom content,
* consider including the standard layout {@link com.devspark.progressfragment.R.layout#fragment_progress}
* in your layout file, so that you continue to retain all of the standard
* behavior of ProgressFragment. In particular, this is currently the only
* way to have the built-in indeterminant progress state be shown.
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_progress, container, false);
}
/**
* Attach to view once the view hierarchy has been created.
*/
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ensureContent();
}
/**
* Detach from view.
*/
@Override
public void onDestroyView() {
mContentShown = false;
mIsContentEmpty = false;
mProgressContainer = mContentContainer = mContentView = mEmptyView = null;
super.onDestroyView();
}
/**
* Return content view or null if the content view has not been initialized.
*
* @return content view or null
* @see #setContentView(View)
* @see #setContentView(int)
*/
public View getContentView() {
return mContentView;
}
/**
* Set the content content from a layout resource.
*
* @param layoutResId Resource ID to be inflated.
* @see #setContentView(View)
* @see #getContentView()
*/
public void setContentView(int layoutResId) {
LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
View contentView = layoutInflater.inflate(layoutResId, null);
setContentView(contentView);
}
/**
* Set the content view to an explicit view. If the content view was installed earlier,
* the content will be replaced with a new view.
*
* @param view The desired content to display. Value can't be null.
* @see #setContentView(int)
* @see #getContentView()
*/
public void setContentView(View view) {
ensureContent();
if (view == null) {
throw new IllegalArgumentException("Content view can't be null");
}
if (mContentContainer instanceof ViewGroup) {
ViewGroup contentContainer = (ViewGroup) mContentContainer;
if (mContentView == null) {
contentContainer.addView(view);
} else {
int index = contentContainer.indexOfChild(mContentView);
// replace content view
contentContainer.removeView(mContentView);
contentContainer.addView(view, index);
}
mContentView = view;
} else {
throw new IllegalStateException("Can't be used with a custom content view");
}
}
/**
* The default content for a ProgressFragment has a TextView that can be shown when
* the content is empty {@link #setContentEmpty(boolean)}.
* If you would like to have it shown, call this method to supply the text it should use.
*
* @param resId Identification of string from a resources
* @see #setEmptyText(CharSequence)
*/
public void setEmptyText(int resId) {
setEmptyText(getString(resId));
}
/**
* The default content for a ProgressFragment has a TextView that can be shown when
* the content is empty {@link #setContentEmpty(boolean)}.
* If you would like to have it shown, call this method to supply the text it should use.
*
* @param text Text for empty view
* @see #setEmptyText(int)
*/
public void setEmptyText(CharSequence text) {
ensureContent();
if (mEmptyView != null && mEmptyView instanceof TextView) {
((TextView) mEmptyView).setText(text);
} else {
throw new IllegalStateException("Can't be used with a custom content view");
}
}
/**
* Control whether the content is being displayed. You can make it not
* displayed if you are waiting for the initial data to show in it. During
* this time an indeterminant progress indicator will be shown instead.
*
* @param shown If true, the content view is shown; if false, the progress
* indicator. The initial value is true.
* @see #setContentShownNoAnimation(boolean)
*/
public void setContentShown(boolean shown) {
setContentShown(shown, true);
}
/**
* Like {@link #setContentShown(boolean)}, but no animation is used when
* transitioning from the previous state.
*
* @param shown If true, the content view is shown; if false, the progress
* indicator. The initial value is true.
* @see #setContentShown(boolean)
*/
public void setContentShownNoAnimation(boolean shown) {
setContentShown(shown, false);
}
/**
* Control whether the content is being displayed. You can make it not
* displayed if you are waiting for the initial data to show in it. During
* this time an indeterminant progress indicator will be shown instead.
*
* @param shown If true, the content view is shown; if false, the progress
* indicator. The initial value is true.
* @param animate If true, an animation will be used to transition to the
* new state.
*/
private void setContentShown(boolean shown, boolean animate) {
ensureContent();
if (mContentShown == shown) {
return;
}
mContentShown = shown;
if (shown) {
if (animate) {
mProgressContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_out));
mContentContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_in));
} else {
mProgressContainer.clearAnimation();
mContentContainer.clearAnimation();
}
mProgressContainer.setVisibility(View.GONE);
mContentContainer.setVisibility(View.VISIBLE);
} else {
if (animate) {
mProgressContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_in));
mContentContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_out));
} else {
mProgressContainer.clearAnimation();
mContentContainer.clearAnimation();
}
mProgressContainer.setVisibility(View.VISIBLE);
mContentContainer.setVisibility(View.GONE);
}
}
/**
* Returns true if content is empty. The default content is not empty.
*
* @return true if content is null or empty
* @see #setContentEmpty(boolean)
*/
public boolean isContentEmpty() {
return mIsContentEmpty;
}
/**
* If the content is empty, then set true otherwise false. The default content is not empty.
* You can't call this method if the content view has not been initialized before
* {@link #setContentView(View)} and content view not null.
*
* @param isEmpty true if content is empty else false
* @see #isContentEmpty()
*/
public void setContentEmpty(boolean isEmpty) {
ensureContent();
if (mContentView == null) {
throw new IllegalStateException("Content view must be initialized before");
}
if (isEmpty) {
mEmptyView.setVisibility(View.VISIBLE);
mContentView.setVisibility(View.GONE);
} else {
mEmptyView.setVisibility(View.GONE);
mContentView.setVisibility(View.VISIBLE);
}
mIsContentEmpty = isEmpty;
}
/**
* Initialization views.
*/
private void ensureContent() {
if (mContentContainer != null && mProgressContainer != null) {
return;
}
View root = getView();
if (root == null) {
throw new IllegalStateException("Content view not yet created");
}
mProgressContainer = root.findViewById(R.id.progress_container);
if (mProgressContainer == null) {
throw new RuntimeException("Your content must have a ViewGroup whose id attribute is 'R.id.progress_container'");
}
mContentContainer = root.findViewById(R.id.content_container);
if (mContentContainer == null) {
throw new RuntimeException("Your content must have a ViewGroup whose id attribute is 'R.id.content_container'");
}
mEmptyView = root.findViewById(android.R.id.empty);
if (mEmptyView != null) {
mEmptyView.setVisibility(View.GONE);
}
mContentShown = true;
// We are starting without a content, so assume we won't
// have our data right away and start with the progress indicator.
if (mContentView == null) {
setContentShown(false, false);
}
}
}