package com.cusnews.utils;
import java.lang.ref.WeakReference;
import java.util.LinkedList;
import java.util.List;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TabLayout;
import android.support.design.widget.TabLayout.Tab;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import com.cusnews.R;
import com.cusnews.app.App;
import com.cusnews.ds.TabLabel;
import cn.bmob.v3.BmobQuery;
import cn.bmob.v3.listener.DeleteListener;
import cn.bmob.v3.listener.FindListener;
import cn.bmob.v3.listener.SaveListener;
/**
* A manager to control adding, removing, loading {@link android.support.design.widget.TabLayout.Tab}s.
*
* @author Xinyue Zhao
*/
public class TabLabelManager {
public interface TabLabelManagerUIHelper {
/**
* Add customized , default, first {@link Tab}.
*/
void addDefaultTab();
/**
* Add customized {@link Tab}.
*
* @param tabLabel
* {@link TabLabel}.
*
* @return The added new {@link Tab}.
*/
Tab addTab( TabLabel tabLabel );
/**
* Remove a {@link Tab} from {@link Tab}s.
*
* @param tab
* {@link Tab}
*/
void removeTab( Tab tab );
}
/**
* Cached list of all {@link TabLabel}s from backend.
*/
private List<TabLabel> mCachedTabLabels = new LinkedList<>();
/**
* Singleton.
*/
private static TabLabelManager sInstance = new TabLabelManager();
private Handler mHandler = new Handler();
/**
* @return The instance of singleton pattern.
*/
public static TabLabelManager getInstance() {
return sInstance;
}
/**
* No one can create this class.
*/
private TabLabelManager() {
}
/**
* For initialize the {@link TabLayout} when host {@link android.app.Activity} is being created.
*
* @param helper
* {@link TabLabelManagerUIHelper}.
* @param loadDefault
* {@code true} if the first default will also be loaded.
*/
public void init( final TabLabelManagerUIHelper helper, boolean loadDefault ) {
//Default page.
if( loadDefault ) {
helper.addDefaultTab();
}
//Load from cache.
for( TabLabel cached : mCachedTabLabels ) {
helper.addTab( cached );
}
//Load from backend and refresh tabs.
BmobQuery<TabLabel> queryTabLabels = new BmobQuery<>();
queryTabLabels.addWhereEqualTo( "mUID", Prefs.getInstance().getGoogleId() );
queryTabLabels.findObjects( App.Instance, new FindListener<TabLabel>() {
@Override
public void onSuccess( List<TabLabel> list ) {
for( TabLabel tabLabel : list ) {
boolean found = false;
for( TabLabel cached : mCachedTabLabels ) {
if( cached.equals( tabLabel ) ) {
found = true;
break;
}
}
if( !found ) {
mCachedTabLabels.add( tabLabel );
helper.addTab( tabLabel );
}
}
}
@Override
public void onError( int i, String s ) {
}
} );
}
/**
* Add a new {@link TabLabel}.
*
* @param newTabLabel
* The new {@link TabLabel}.
* @param helper
* Use helper to refresh UI before removing {@link TabLabel}.
* @param viewForSnack
* The anchor for {@link Snackbar} for result-messages.
*
* @return A {@link Tab} that hosts the new {@link TabLabel}. It might be {@code null} if the {@code newTabLabel} has same wording(label) equal to
* label of an existing {@link TabLabel} in {@link #mCachedTabLabels}.
*/
public
@Nullable
Tab addNewRemoteTab( TabLabel newTabLabel, TabLabelManagerUIHelper helper, View viewForSnack ) {
//Same label should not be added again.
for( TabLabel cached : mCachedTabLabels ) {
if( cached.equals( newTabLabel ) ) {
Snackbar.make( viewForSnack, viewForSnack.getContext().getString( R.string.lbl_sync_same_label, newTabLabel.getLabel() ),
Snackbar.LENGTH_SHORT
).show();
return null;
}
}
final Tab tab = helper.addTab( newTabLabel );
mHandler.postDelayed( new Runnable() {
@Override
public void run() {
tab.select();
}
}, 300 );
mCachedTabLabels.add( newTabLabel );
addNewRemoteTabInternal( newTabLabel, viewForSnack );
return tab;
}
/**
* Save a new {@link TabLabel} to backend.
*
* @param newTabLabel
* New {@link TabLabel}.
* @param viewForSnack
* The anchor for {@link Snackbar} for result-messages.
*/
private void addNewRemoteTabInternal( final TabLabel newTabLabel, View viewForSnack ) {
final WeakReference<View> anchor = new WeakReference<>( viewForSnack );
newTabLabel.save( App.Instance, new SaveListener() {
@Override
public void onSuccess() {
View anchorV = anchor.get();
if( anchorV != null ) {
Snackbar.make(
anchorV, anchorV.getContext().getString( R.string.lbl_sync_label_added, newTabLabel.getLabel() ), Snackbar.LENGTH_SHORT )
.show();
}
}
@Override
public void onFailure( int i, String s ) {
View anchorV = anchor.get();
if( anchorV != null ) {
Snackbar.make( anchorV, R.string.lbl_sync_fail, Snackbar.LENGTH_LONG ).setAction( R.string.btn_retry, new OnClickListener() {
@Override
public void onClick( View v ) {
addNewRemoteTabInternal( newTabLabel, anchor.get() );
}
} ).show();
}
}
} );
}
/**
* Remove a {@link TabLabel} and its host {@link Tab}. It delete cached item and them remove from backend.
*
* @param tab
* {@link Tab} that hosts {@code tabLabel}.
* @param tabLabel
* {@link TabLabel} to remove.
* @param helper
* Use helper to refresh UI before removing {@link TabLabel}.
* @param viewForSnack
* The anchor for {@link Snackbar} for result-messages.
*/
public void removeRemoteTab( Tab tab, TabLabel tabLabel, TabLabelManagerUIHelper helper, View viewForSnack ) {
helper.removeTab( tab );
for( TabLabel cached : mCachedTabLabels ) {
if( TextUtils.equals( cached.getObjectId(), tabLabel.getObjectId() ) ) {
mCachedTabLabels.remove( cached );
removeRemoteTabInternal( tabLabel, viewForSnack );
break;
}
}
}
/**
* Remove a {@link TabLabel} from backend.
*
* @param tabLabel
* Existed {@link TabLabel}.
* @param viewForSnack
* The anchor for {@link Snackbar} for result-messages.
*/
private void removeRemoteTabInternal( final TabLabel tabLabel, View viewForSnack ) {
final WeakReference<View> anchor = new WeakReference<>( viewForSnack );
tabLabel.delete( App.Instance, new DeleteListener() {
@Override
public void onSuccess() {
View anchorV = anchor.get();
if( anchorV != null ) {
Snackbar.make( anchorV, R.string.lbl_sync_label_removed, Snackbar.LENGTH_SHORT ).show();
}
}
@Override
public void onFailure( int i, String s ) {
View anchorV = anchor.get();
if( anchorV != null ) {
Snackbar.make( anchorV, R.string.lbl_sync_fail, Snackbar.LENGTH_LONG ).setAction( R.string.btn_retry, new OnClickListener() {
@Override
public void onClick( View v ) {
removeRemoteTabInternal( tabLabel, anchor.get() );
}
} ).show();
}
}
} );
}
/**
* Clean all tabs.
*/
public void clean() {
mCachedTabLabels.clear();
}
}