package com.jdroid.android.fragment;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import com.jdroid.android.R;
import com.jdroid.android.activity.AbstractFragmentActivity;
import com.jdroid.android.activity.ActivityIf;
import com.jdroid.android.application.AbstractApplication;
import com.jdroid.android.application.AppModule;
import com.jdroid.android.concurrent.SafeExecuteWrapperRunnable;
import com.jdroid.android.exception.AbstractErrorDisplayer;
import com.jdroid.android.exception.ErrorDisplayer;
import com.jdroid.android.loading.FragmentLoading;
import com.jdroid.android.usecase.AbstractUseCase;
import com.jdroid.android.usecase.listener.UseCaseListener;
import com.jdroid.java.collections.Maps;
import com.jdroid.java.concurrent.ExecutorUtils;
import com.jdroid.java.exception.AbstractException;
import com.jdroid.java.utils.LoggerUtils;
import org.slf4j.Logger;
import java.util.Map;
public class FragmentHelper implements FragmentIf {
private final static Logger LOGGER = LoggerUtils.getLogger(FragmentHelper.class);
private Fragment fragment;
private Map<AppModule, FragmentDelegate> fragmentDelegatesMap;
private FragmentLoading loading;
private Toolbar appBar;
public FragmentHelper(Fragment fragment) {
this.fragment = fragment;
}
public FragmentIf getFragmentIf() {
return (FragmentIf)fragment;
}
protected Fragment getFragment() {
return fragment;
}
@Override
public ActivityIf getActivityIf() {
return (ActivityIf)fragment.getActivity();
}
// //////////////////////// Layout //////////////////////// //
@Override
public Integer getBaseFragmentLayout() {
return R.layout.jdroid_base_fragment;
}
@Override
public Integer getContentFragmentLayout() {
return null;
}
@SuppressWarnings("unchecked")
@Override
public <V extends View> V findView(int id) {
return (V)fragment.getView().findViewById(id);
}
@SuppressWarnings("unchecked")
@Override
public <V extends View> V findViewOnActivity(int id) {
return (V)fragment.getActivity().findViewById(id);
}
@Override
public Integer getMenuResourceId() {
return null;
}
@Override
public View inflate(int resource) {
return getActivityIf().inflate(resource);
}
public Boolean isSecondaryFragment() {
return false;
}
// //////////////////////// Life cycle //////////////////////// //
public void onCreate(Bundle savedInstanceState) {
LOGGER.debug("Executing onCreate on " + fragment);
fragment.setRetainInstance(getFragmentIf().shouldRetainInstance());
fragmentDelegatesMap = Maps.newHashMap();
for (AppModule appModule : AbstractApplication.get().getAppModules()) {
FragmentDelegate fragmentDelegate = getFragmentIf().createFragmentDelegate(appModule);
if (fragmentDelegate != null) {
fragmentDelegatesMap.put(appModule, fragmentDelegate);
fragmentDelegate.onCreate(savedInstanceState);
}
}
}
public void onNewIntent(Intent intent) {
LOGGER.debug("Executing onNewIntent on " + fragment);
}
@Override
public Boolean shouldRetainInstance() {
return true;
}
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
LOGGER.debug("Executing onCreateView on " + fragment);
Integer contentFragmentLayout = getFragmentIf().getContentFragmentLayout();
if (contentFragmentLayout != null) {
View view = inflater.inflate(getFragmentIf().getBaseFragmentLayout(), container, false);
ViewGroup viewGroup = (ViewGroup)view.findViewById(R.id.content);
viewGroup.addView(inflater.inflate(contentFragmentLayout, null, false));
return view;
}
return null;
}
public void onViewCreated(View view, Bundle savedInstanceState) {
LOGGER.debug("Executing onViewCreated on " + fragment);
appBar = findView(R.id.appBar);
if (appBar != null && getActivityIf() instanceof AbstractFragmentActivity) {
getFragmentIf().beforeInitAppBar(appBar);
if (getFragmentIf().isSecondaryFragment()) {
if (getFragmentIf().getMenuResourceId() != null) {
appBar.inflateMenu(getFragmentIf().getMenuResourceId());
appBar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
return getFragmentIf().onOptionsItemSelected(item);
}
});
}
} else {
((AbstractFragmentActivity)getActivityIf()).setSupportActionBar(appBar);
appBar.setNavigationIcon(R.drawable.jdroid_ic_arrow_back_white_24dp);
getActivityIf().initNavDrawer(appBar);
}
getFragmentIf().afterInitAppBar(appBar);
}
for (FragmentDelegate each : fragmentDelegatesMap.values()) {
each.onViewCreated(view, savedInstanceState);
}
if (loading == null) {
loading = getFragmentIf().getDefaultLoading();
}
if (loading != null) {
loading.onViewCreated(getFragmentIf());
}
}
public void onActivityCreated(Bundle savedInstanceState) {
LOGGER.debug("Executing onActivityCreated on " + fragment);
}
public void onStart() {
LOGGER.debug("Executing onStart on " + fragment);
FragmentIf fragmentIf = getFragmentIf();
if (fragmentIf.shouldTrackOnFragmentStart()) {
AbstractApplication.get().getAnalyticsSender().onFragmentStart(fragmentIf.getScreenViewName());
}
}
public void onResume() {
LOGGER.debug("Executing onResume on " + fragment);
for (FragmentDelegate each : fragmentDelegatesMap.values()) {
each.onResume();
}
}
public void onBeforePause() {
for (FragmentDelegate each : fragmentDelegatesMap.values()) {
each.onBeforePause();
}
}
public void onPause() {
LOGGER.debug("Executing onPause on " + fragment);
}
public void onStop() {
LOGGER.debug("Executing onStop on " + fragment);
}
public void onDestroyView() {
LOGGER.debug("Executing onDestroyView on " + fragment);
}
public void onBeforeDestroy() {
for (FragmentDelegate each : fragmentDelegatesMap.values()) {
each.onBeforeDestroy();
}
}
public void onDestroy() {
LOGGER.debug("Executing onDestroy on " + fragment);
}
@Override
public <E> E getExtra(String key) {
return getActivityIf().getExtra(key);
}
@Override
public <E> E getArgument(String key) {
return getArgument(key, null);
}
@Override
@SuppressWarnings("unchecked")
public <E> E getArgument(String key, E defaultValue) {
Bundle arguments = fragment.getArguments();
E value = (arguments != null) && arguments.containsKey(key) ? (E)arguments.get(key) : null;
return value != null ? value : defaultValue;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
return false;
}
@Override
public Boolean onBackPressedHandled() {
return false;
}
// //////////////////////// App bar //////////////////////// //
@Override
public void beforeInitAppBar(Toolbar appBar) {
// Do nothing
}
@Override
public void afterInitAppBar(Toolbar appBar) {
// Do nothing
}
@Override
public Toolbar getAppBar() {
return appBar;
}
// //////////////////////// Error Handling //////////////////////// //
@Override
public ErrorDisplayer createErrorDisplayer(AbstractException abstractException) {
return AbstractErrorDisplayer.getErrorDisplayer(abstractException);
}
// //////////////////////// Use cases //////////////////////// //
@Override
public void registerUseCase(AbstractUseCase useCase, UseCaseListener listener) {
registerUseCase(useCase, listener, UseCaseTrigger.MANUAL);
}
public void registerUseCase(final AbstractUseCase useCase, final UseCaseListener listener,
final UseCaseTrigger useCaseTrigger) {
if (useCase != null) {
ExecutorUtils.execute(new Runnable() {
@Override
public void run() {
useCase.addListener(listener);
if (useCase.isInProgress()) {
if (listener != null && !useCase.isNotified()) {
listener.onStartUseCase();
}
} else if (useCase.isFinishSuccessful()) {
if (listener != null && !useCase.isNotified()) {
listener.onFinishUseCase();
useCase.markAsNotified();
}
if (useCaseTrigger.equals(UseCaseTrigger.ALWAYS)) {
useCase.run();
}
} else if (useCase.isFinishFailed()) {
if (listener != null && !useCase.isNotified()) {
try {
listener.onFinishFailedUseCase(useCase.getAbstractException());
} finally {
useCase.markAsNotified();
}
}
if (useCaseTrigger.equals(UseCaseTrigger.ALWAYS)) {
useCase.run();
}
} else if (useCase.isNotInvoked()
&& (useCaseTrigger.equals(UseCaseTrigger.ONCE) || useCaseTrigger.equals(UseCaseTrigger.ALWAYS))) {
useCase.run();
}
}
});
}
}
public enum UseCaseTrigger {
MANUAL,
ONCE,
ALWAYS;
}
@Override
public void unregisterUseCase(final AbstractUseCase userCase, final UseCaseListener listener) {
if (userCase != null) {
userCase.removeListener(listener);
}
}
@Override
public void executeUseCase(AbstractUseCase useCase) {
ExecutorUtils.execute(useCase);
}
@Override
public void executeUseCase(AbstractUseCase useCase, Long delaySeconds) {
ExecutorUtils.schedule(useCase, delaySeconds);
}
@Override
public void onStartUseCase() {
getFragmentIf().showLoading();
}
@Override
public void onUpdateUseCase() {
// Do nothing by default
}
@Override
public void onFinishUseCase() {
getFragmentIf().dismissLoading();
}
@Override
public void onFinishFailedUseCase(AbstractException abstractException) {
getFragmentIf().dismissLoading();
// TODO This line shouldn't be executed outside the onStart/onStop cycle, to avoid IllegalStateException: Can not perform this action after onSaveInstanceState
getFragmentIf().createErrorDisplayer(abstractException).displayError(abstractException);
}
// //////////////////////// Loading //////////////////////// //
@Override
public void showLoading() {
final FragmentIf fragmentIf = getFragmentIf();
fragmentIf.executeOnUIThread(new Runnable() {
@Override
public void run() {
if (loading != null) {
loading.show(fragmentIf);
}
}
});
}
@Override
public void dismissLoading() {
final FragmentIf fragmentIf = getFragmentIf();
fragmentIf.executeOnUIThread(new Runnable() {
@Override
public void run() {
if (loading != null) {
loading.dismiss(fragmentIf);
}
}
});
}
@Override
public FragmentLoading getDefaultLoading() {
return null;
}
@Override
public void setLoading(FragmentLoading loading) {
this.loading = loading;
}
// //////////////////////// Delegates //////////////////////// //
@Override
public FragmentDelegate createFragmentDelegate(AppModule appModule) {
return appModule.createFragmentDelegate(fragment);
}
@Override
public FragmentDelegate getFragmentDelegate(AppModule appModule) {
return fragmentDelegatesMap.get(appModule);
}
// //////////////////////// Analytics //////////////////////// //
@Override
public Boolean shouldTrackOnFragmentStart() {
return false;
}
@NonNull
@Override
public String getScreenViewName() {
return fragment.getClass().getSimpleName();
}
// //////////////////////// Others //////////////////////// //
@Override
public void executeOnUIThread(Runnable runnable) {
Activity activity = fragment.getActivity();
if ((activity != null) && activity.equals(AbstractApplication.get().getCurrentActivity())) {
activity.runOnUiThread(new SafeExecuteWrapperRunnable(fragment, runnable));
}
}
}