From b5744920265a0e7daa4b067b3e34ba8451fba821 Mon Sep 17 00:00:00 2001 From: Ralf Wondratschek Date: Thu, 15 Dec 2016 16:21:20 +0100 Subject: [PATCH 1/6] Add a fragment delegate and remove duplicated code. This allows reusing the fragment code without extending TiFragment or TiDialogFragment --- README.md | 70 +++++ .../thirtyinch/TiDialogFragment.java | 175 ++--------- .../grandcentrix/thirtyinch/TiFragment.java | 209 ++----------- .../internal/TiFragmentDelegate.java | 291 ++++++++++++++++++ 4 files changed, 403 insertions(+), 342 deletions(-) create mode 100644 thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegate.java diff --git a/README.md b/README.md index 49431193..56a05c1b 100644 --- a/README.md +++ b/README.md @@ -316,6 +316,76 @@ public class HelloWorldActivity extends CompositeActivity implements HelloWorldV } ``` +### TiFragmentDelegate + +If you can't extend `TiFragment` in your existing codebase, then you can create a `TiFragmentDelegate` object yourself. You only need forward all lifecycle callbacks like in the following snippet. + +```java +public class SampleFragment extends Fragment implements TiPresenterProvider, SampleView { + + private final TiFragmentDelegate mDelegate = new TiFragmentDelegate<>(this); + + private TextView mSampleText; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mDelegate.onCreate(savedInstanceState); + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + mDelegate.onCreateView(inflater, container, savedInstanceState); + + final ViewGroup view = (ViewGroup) inflater.inflate(R.layout.fragment_sample, container, false); + mSampleText = (TextView) view.findViewById(R.id.sample_text); + return view; + } + + @Override + public void onStart() { + super.onStart(); + mDelegate.onStart(); + } + + @Override + public void onStop() { + mDelegate.onStop(); + super.onStop(); + } + + @Override + public void onDestroyView() { + mDelegate.onDestroyView(); + super.onDestroyView(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + mDelegate.onDestroy(); + } + + @Override + public void onSaveInstanceState(final Bundle outState) { + super.onSaveInstanceState(outState); + mDelegate.onSaveInstanceState(outState); + } + + @NonNull + @Override + public SamplePresenter providePresenter() { + return new SamplePresenter(); + } + + @Override + public void showText(final String s) { + mSampleText.setText(s); + } +} +``` + Yes you have to extends `CompositeActivity`, but that's the last level of inheritance you'll ever need. ## Versions diff --git a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiDialogFragment.java b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiDialogFragment.java index 7be714f2..999d0c68 100644 --- a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiDialogFragment.java +++ b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiDialogFragment.java @@ -3,22 +3,16 @@ import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.v4.app.FragmentActivity; import android.support.v7.app.AppCompatDialogFragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import net.grandcentrix.thirtyinch.callonmainthread.CallOnMainThreadInterceptor; -import net.grandcentrix.thirtyinch.distinctuntilchanged.DistinctUntilChangedInterceptor; import net.grandcentrix.thirtyinch.internal.InterceptableViewBinder; -import net.grandcentrix.thirtyinch.internal.PresenterSavior; -import net.grandcentrix.thirtyinch.internal.PresenterViewBinder; +import net.grandcentrix.thirtyinch.internal.TiFragmentDelegate; import net.grandcentrix.thirtyinch.internal.TiLoggingTagProvider; import net.grandcentrix.thirtyinch.internal.TiPresenterProvider; import net.grandcentrix.thirtyinch.internal.TiViewProvider; -import net.grandcentrix.thirtyinch.util.AndroidDeveloperOptions; -import net.grandcentrix.thirtyinch.util.AnnotationUtil; import java.util.List; @@ -26,46 +20,33 @@ public abstract class TiDialogFragment

, V extends TiVie extends AppCompatDialogFragment implements TiPresenterProvider

, TiLoggingTagProvider, TiViewProvider, InterceptableViewBinder { - private static final String SAVED_STATE_PRESENTER_ID = "presenter_id"; - - private final String TAG = this.getClass().getSimpleName() - + ":" + TiDialogFragment.class.getSimpleName() - + "@" + Integer.toHexString(this.hashCode()); - - private volatile boolean mActivityStarted = false; - - private P mPresenter; - - private String mPresenterId; - - private PresenterViewBinder mViewBinder = new PresenterViewBinder<>(this); + private final TiFragmentDelegate mDelegate = new TiFragmentDelegate<>(this); @NonNull @Override public Removable addBindViewInterceptor(@NonNull final BindViewInterceptor interceptor) { - return mViewBinder.addBindViewInterceptor(interceptor); + return mDelegate.addBindViewInterceptor(interceptor); } @Nullable @Override public V getInterceptedViewOf(@NonNull final BindViewInterceptor interceptor) { - return mViewBinder.getInterceptedViewOf(interceptor); + return mDelegate.getInterceptedViewOf(interceptor); } @NonNull @Override - public List getInterceptors( - @NonNull final Filter predicate) { - return mViewBinder.getInterceptors(predicate); + public List getInterceptors(@NonNull final Filter predicate) { + return mDelegate.getInterceptors(predicate); } @Override public String getLoggingTag() { - return TAG; + return mDelegate.getLoggingTag(); } public P getPresenter() { - return mPresenter; + return mDelegate.getPresenter(); } /** @@ -74,141 +55,50 @@ public P getPresenter() { */ @Override public void invalidateView() { - mViewBinder.invalidateView(); + mDelegate.invalidateView(); } @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); - - if (mPresenter == null && savedInstanceState != null) { - // recover with Savior - // this should always work. - final String recoveredPresenterId = savedInstanceState - .getString(SAVED_STATE_PRESENTER_ID); - if (recoveredPresenterId != null) { - TiLog.v(TAG, "try to recover Presenter with id: " + recoveredPresenterId); - //noinspection unchecked - mPresenter = (P) PresenterSavior.INSTANCE.recover(recoveredPresenterId); - if (mPresenter != null) { - // save recovered presenter with new id. No other instance of this activity, - // holding the presenter before, is now able to remove the reference to - // this presenter from the savior - PresenterSavior.INSTANCE.free(recoveredPresenterId); - mPresenterId = PresenterSavior.INSTANCE.safe(mPresenter); - } - TiLog.v(TAG, "recovered Presenter " + mPresenter); - } - } - - if (mPresenter == null) { - mPresenter = providePresenter(); - TiLog.v(TAG, "created Presenter: " + mPresenter); - final TiConfiguration config = mPresenter.getConfig(); - if (config.shouldRetainPresenter() && config.useStaticSaviorToRetain()) { - mPresenterId = PresenterSavior.INSTANCE.safe(mPresenter); - } - mPresenter.create(); - } - - final TiConfiguration config = mPresenter.getConfig(); - if (config.isCallOnMainThreadInterceptorEnabled()) { - addBindViewInterceptor(new CallOnMainThreadInterceptor()); - } - - if (config.isDistinctUntilChangedInterceptorEnabled()) { - addBindViewInterceptor(new DistinctUntilChangedInterceptor()); - } - - if (config.shouldRetainPresenter()) { - setRetainInstance(true); - } + mDelegate.onCreate(savedInstanceState); } @Nullable @Override public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) { - mViewBinder.invalidateView(); + mDelegate.invalidateView(); return super.onCreateView(inflater, container, savedInstanceState); } @Override public void onDestroy() { super.onDestroy(); - final FragmentActivity activity = getActivity(); - - boolean destroyPresenter = false; - if (activity.isFinishing()) { - // Probably a backpress and not a configuration change - // Activity will not be recreated and finally destroyed, also destroyed the presenter - destroyPresenter = true; - TiLog.v(TAG, "Activity is finishing, destroying presenter " + mPresenter); - } - - final TiConfiguration config = mPresenter.getConfig(); - if (!destroyPresenter && - !config.shouldRetainPresenter()) { - // configuration says the presenter should not be retained, a new presenter instance - // will be created and the current presenter should be destroyed - destroyPresenter = true; - TiLog.v(TAG, "presenter configured as not retaining, destroying " + mPresenter); - } - - if (!destroyPresenter && - !config.useStaticSaviorToRetain() - && AndroidDeveloperOptions.isDontKeepActivitiesEnabled(getActivity())) { - // configuration says the PresenterSavior should not be used. Retaining the presenter - // relays on the Activity nonConfigurationInstance which is always null when - // "don't keep activities" is enabled. - // a new presenter instance will be created and the current presenter should be destroyed - destroyPresenter = true; - TiLog.v(TAG, "the PresenterSavior is disabled and \"don\'t keep activities\" is " - + "activated. The presenter can't be retained. Destroying " + mPresenter); - } - - if (destroyPresenter) { - mPresenter.destroy(); - PresenterSavior.INSTANCE.free(mPresenterId); - } else { - TiLog.v(TAG, "not destroying " + mPresenter - + " which will be reused by the next Activity instance, recreating..."); - } + mDelegate.onDestroy(); } @Override public void onDestroyView() { - mPresenter.detachView(); + mDelegate.onDestroyView(); super.onDestroyView(); } @Override public void onSaveInstanceState(final Bundle outState) { super.onSaveInstanceState(outState); - outState.putString(SAVED_STATE_PRESENTER_ID, mPresenterId); + mDelegate.onSaveInstanceState(outState); } @Override public void onStart() { super.onStart(); - mActivityStarted = true; - - if (isUiPossible()) { - getActivity().getWindow().getDecorView().post(new Runnable() { - @Override - public void run() { - if (isUiPossible() && mActivityStarted) { - mViewBinder.bindView(mPresenter, TiDialogFragment.this); - } - } - }); - } + mDelegate.onStart(); } @Override public void onStop() { - mActivityStarted = false; - mPresenter.detachView(); + mDelegate.onStop(); super.onStop(); } @@ -220,40 +110,11 @@ public void onStop() { */ @NonNull public V provideView() { - - final Class foundViewInterface = AnnotationUtil - .getInterfaceOfClassExtendingGivenInterface(this.getClass(), TiView.class); - - if (foundViewInterface == null) { - throw new IllegalArgumentException( - "This Fragment doesn't implement a TiView interface. " - + "This is the default behaviour. Override provideView() to explicitly change this."); - } else { - if (foundViewInterface.getSimpleName().equals("TiView")) { - throw new IllegalArgumentException( - "extending TiView doesn't make sense, it's an empty interface." - + " This is the default behaviour. Override provideView() to explicitly change this."); - } else { - // assume that the fragment itself is the view and implements the TiView interface - //noinspection unchecked - return (V) this; - } - } + return mDelegate.provideView(); } @Override public String toString() { - String presenter = getPresenter() == null ? "null" : - getPresenter().getClass().getSimpleName() - + "@" + Integer.toHexString(getPresenter().hashCode()); - - return getClass().getSimpleName() - + ":" + TiDialogFragment.class.getSimpleName() - + "@" + Integer.toHexString(hashCode()) - + "{presenter=" + presenter + "}"; - } - - private boolean isUiPossible() { - return isAdded() && !isDetached(); + return mDelegate.fragmentToString(); } } diff --git a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiFragment.java b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiFragment.java index 342b8a7b..b70f2611 100644 --- a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiFragment.java +++ b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiFragment.java @@ -15,17 +15,6 @@ package net.grandcentrix.thirtyinch; -import net.grandcentrix.thirtyinch.callonmainthread.CallOnMainThreadInterceptor; -import net.grandcentrix.thirtyinch.distinctuntilchanged.DistinctUntilChangedInterceptor; -import net.grandcentrix.thirtyinch.internal.InterceptableViewBinder; -import net.grandcentrix.thirtyinch.internal.PresenterSavior; -import net.grandcentrix.thirtyinch.internal.PresenterViewBinder; -import net.grandcentrix.thirtyinch.internal.TiLoggingTagProvider; -import net.grandcentrix.thirtyinch.internal.TiPresenterProvider; -import net.grandcentrix.thirtyinch.internal.TiViewProvider; -import net.grandcentrix.thirtyinch.util.AndroidDeveloperOptions; -import net.grandcentrix.thirtyinch.util.AnnotationUtil; - import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -34,57 +23,45 @@ import android.view.View; import android.view.ViewGroup; +import net.grandcentrix.thirtyinch.internal.InterceptableViewBinder; +import net.grandcentrix.thirtyinch.internal.TiFragmentDelegate; +import net.grandcentrix.thirtyinch.internal.TiLoggingTagProvider; +import net.grandcentrix.thirtyinch.internal.TiPresenterProvider; +import net.grandcentrix.thirtyinch.internal.TiViewProvider; + import java.util.List; public abstract class TiFragment

, V extends TiView> extends Fragment implements TiPresenterProvider

, TiLoggingTagProvider, TiViewProvider, InterceptableViewBinder { - private static final String SAVED_STATE_PRESENTER_ID = "presenter_id"; - - /** - * enables debug logging during development - */ - private static final boolean ENABLE_DEBUG_LOGGING = false; - - private final String TAG = this.getClass().getSimpleName() - + ":" + TiFragment.class.getSimpleName() - + "@" + Integer.toHexString(this.hashCode()); - - private volatile boolean mActivityStarted = false; - - private P mPresenter; - - private String mPresenterId; - - private PresenterViewBinder mViewBinder = new PresenterViewBinder<>(this); + private final TiFragmentDelegate mDelegate = new TiFragmentDelegate<>(this); @NonNull @Override public Removable addBindViewInterceptor(@NonNull final BindViewInterceptor interceptor) { - return mViewBinder.addBindViewInterceptor(interceptor); + return mDelegate.addBindViewInterceptor(interceptor); } @Nullable @Override public V getInterceptedViewOf(@NonNull final BindViewInterceptor interceptor) { - return mViewBinder.getInterceptedViewOf(interceptor); + return mDelegate.getInterceptedViewOf(interceptor); } @NonNull @Override - public List getInterceptors( - @NonNull final Filter predicate) { - return mViewBinder.getInterceptors(predicate); + public List getInterceptors(@NonNull final Filter predicate) { + return mDelegate.getInterceptors(predicate); } @Override public String getLoggingTag() { - return TAG; + return mDelegate.getLoggingTag(); } public P getPresenter() { - return mPresenter; + return mDelegate.getPresenter(); } /** @@ -93,62 +70,19 @@ public P getPresenter() { */ @Override public void invalidateView() { - mViewBinder.invalidateView(); + mDelegate.invalidateView(); } @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); - - if (mPresenter == null && savedInstanceState != null) { - // recover with Savior - // this should always work. - final String recoveredPresenterId = savedInstanceState - .getString(SAVED_STATE_PRESENTER_ID); - if (recoveredPresenterId != null) { - TiLog.v(TAG, "try to recover Presenter with id: " + recoveredPresenterId); - //noinspection unchecked - mPresenter = (P) PresenterSavior.INSTANCE.recover(recoveredPresenterId); - if (mPresenter != null) { - // save recovered presenter with new id. No other instance of this activity, - // holding the presenter before, is now able to remove the reference to - // this presenter from the savior - PresenterSavior.INSTANCE.free(recoveredPresenterId); - mPresenterId = PresenterSavior.INSTANCE.safe(mPresenter); - } - TiLog.v(TAG, "recovered Presenter " + mPresenter); - } - } - - if (mPresenter == null) { - mPresenter = providePresenter(); - TiLog.v(TAG, "created Presenter: " + mPresenter); - final TiConfiguration config = mPresenter.getConfig(); - if (config.shouldRetainPresenter() && config.useStaticSaviorToRetain()) { - mPresenterId = PresenterSavior.INSTANCE.safe(mPresenter); - } - mPresenter.create(); - } - - final TiConfiguration config = mPresenter.getConfig(); - if (config.isCallOnMainThreadInterceptorEnabled()) { - addBindViewInterceptor(new CallOnMainThreadInterceptor()); - } - - if (config.isDistinctUntilChangedInterceptorEnabled()) { - addBindViewInterceptor(new DistinctUntilChangedInterceptor()); - } - - if (config.shouldRetainPresenter()) { - setRetainInstance(true); - } + mDelegate.onCreate(savedInstanceState); } @Nullable @Override - public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container, - @Nullable final Bundle savedInstanceState) { - mViewBinder.invalidateView(); + public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) { + mDelegate.onCreateView(inflater, container, savedInstanceState); return super.onCreateView(inflater, container, savedInstanceState); } @@ -156,81 +90,30 @@ public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGrou @Override public void onDestroy() { super.onDestroy(); - //FIXME handle attach/detach state - - logState(); - - boolean destroyPresenter = false; - if (getActivity().isFinishing()) { - // Probably a backpress and not a configuration change - // Activity will not be recreated and finally destroyed, also destroyed the presenter - destroyPresenter = true; - TiLog.v(TAG, "Activity is finishing, destroying presenter " + mPresenter); - } - - final TiConfiguration config = mPresenter.getConfig(); - if (!destroyPresenter && - !config.shouldRetainPresenter()) { - // configuration says the presenter should not be retained, a new presenter instance - // will be created and the current presenter should be destroyed - destroyPresenter = true; - TiLog.v(TAG, "presenter configured as not retaining, destroying " + mPresenter); - } - - if (!destroyPresenter && - !config.useStaticSaviorToRetain() - && AndroidDeveloperOptions.isDontKeepActivitiesEnabled(getActivity())) { - // configuration says the PresenterSavior should not be used. Retaining the presenter - // relays on the Activity nonConfigurationInstance which is always null when - // "don't keep activities" is enabled. - // a new presenter instance will be created and the current presenter should be destroyed - destroyPresenter = true; - TiLog.v(TAG, "the PresenterSavior is disabled and \"don\'t keep activities\" is " - + "activated. The presenter can't be retained. Destroying " + mPresenter); - } - - if (destroyPresenter) { - mPresenter.destroy(); - PresenterSavior.INSTANCE.free(mPresenterId); - } else { - TiLog.v(TAG, "not destroying " + mPresenter - + " which will be reused by the next Activity instance, recreating..."); - } + mDelegate.onDestroy(); } @Override public void onDestroyView() { - mPresenter.detachView(); + mDelegate.onDestroyView(); super.onDestroyView(); } @Override public void onSaveInstanceState(final Bundle outState) { super.onSaveInstanceState(outState); - outState.putString(SAVED_STATE_PRESENTER_ID, mPresenterId); + mDelegate.onSaveInstanceState(outState); } @Override public void onStart() { super.onStart(); - mActivityStarted = true; - - if (isUiPossible()) { - getActivity().getWindow().getDecorView().post(new Runnable() { - @Override - public void run() { - if (isUiPossible() && mActivityStarted) { - mViewBinder.bindView(mPresenter, TiFragment.this); - } - } - }); - } + mDelegate.onStart(); } @Override public void onStop() { - mActivityStarted = false; - mPresenter.detachView(); + mDelegate.onStop(); super.onStop(); } @@ -242,55 +125,11 @@ public void onStop() { */ @NonNull public V provideView() { - - final Class foundViewInterface = AnnotationUtil - .getInterfaceOfClassExtendingGivenInterface(this.getClass(), TiView.class); - - if (foundViewInterface == null) { - throw new IllegalArgumentException( - "This Fragment doesn't implement a TiView interface. " - + "This is the default behaviour. Override provideView() to explicitly change this."); - } else { - if (foundViewInterface.getSimpleName().equals("TiView")) { - throw new IllegalArgumentException( - "extending TiView doesn't make sense, it's an empty interface." - + " This is the default behaviour. Override provideView() to explicitly change this."); - } else { - // assume that the fragment itself is the view and implements the TiView interface - //noinspection unchecked - return (V) this; - } - } + return mDelegate.provideView(); } @Override public String toString() { - String presenter = getPresenter() == null ? "null" : - getPresenter().getClass().getSimpleName() - + "@" + Integer.toHexString(getPresenter().hashCode()); - - return getClass().getSimpleName() - + ":" + TiFragment.class.getSimpleName() - + "@" + Integer.toHexString(hashCode()) - + "{presenter=" + presenter + "}"; - } - - private boolean isUiPossible() { - return isAdded() && !isDetached(); - } - - private void logState() { - if (ENABLE_DEBUG_LOGGING) { - TiLog.v(TAG, "isChangingConfigurations = " + getActivity().isChangingConfigurations()); - TiLog.v(TAG, "isActivityFinishing = " + getActivity().isFinishing()); - TiLog.v(TAG, "isAdded = " + isAdded()); - TiLog.v(TAG, "isDetached = " + isDetached()); - TiLog.v(TAG, "isDontKeepActivitiesEnabled = " + AndroidDeveloperOptions - .isDontKeepActivitiesEnabled(getActivity())); - - final TiConfiguration config = mPresenter.getConfig(); - TiLog.v(TAG, "shouldRetain = " + config.shouldRetainPresenter()); - TiLog.v(TAG, "useStaticSavior = " + config.useStaticSaviorToRetain()); - } + return mDelegate.fragmentToString(); } } \ No newline at end of file diff --git a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegate.java b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegate.java new file mode 100644 index 00000000..f76d8c5f --- /dev/null +++ b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegate.java @@ -0,0 +1,291 @@ +package net.grandcentrix.thirtyinch.internal; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import net.grandcentrix.thirtyinch.BindViewInterceptor; +import net.grandcentrix.thirtyinch.Removable; +import net.grandcentrix.thirtyinch.TiConfiguration; +import net.grandcentrix.thirtyinch.TiFragment; +import net.grandcentrix.thirtyinch.TiLog; +import net.grandcentrix.thirtyinch.TiPresenter; +import net.grandcentrix.thirtyinch.TiView; +import net.grandcentrix.thirtyinch.callonmainthread.CallOnMainThreadInterceptor; +import net.grandcentrix.thirtyinch.distinctuntilchanged.DistinctUntilChangedInterceptor; +import net.grandcentrix.thirtyinch.util.AndroidDeveloperOptions; +import net.grandcentrix.thirtyinch.util.AnnotationUtil; + +import java.util.List; + +/** + * This delegate allows sharing the fragment code between the {@link TiFragment} and other + * {@link Fragment} implementations in 3rd party code. + */ +public class TiFragmentDelegate

, V extends TiView, F extends Fragment & TiPresenterProvider

> + implements TiLoggingTagProvider, TiViewProvider, InterceptableViewBinder { + + private static final String SAVED_STATE_PRESENTER_ID = "presenter_id"; + + /** + * enables debug logging during development + */ + private static final boolean ENABLE_DEBUG_LOGGING = false; + + private final String TAG = this.getClass().getSimpleName() + + ":" + TiFragmentDelegate.class.getSimpleName() + + "@" + Integer.toHexString(this.hashCode()); + + private volatile boolean mActivityStarted = false; + + private P mPresenter; + + private String mPresenterId; + + private PresenterViewBinder mViewBinder = new PresenterViewBinder<>(this); + + private final F mFragment; + + public TiFragmentDelegate(F fragment) { + mFragment = fragment; + } + + @NonNull + @Override + public Removable addBindViewInterceptor(@NonNull final BindViewInterceptor interceptor) { + return mViewBinder.addBindViewInterceptor(interceptor); + } + + @Nullable + @Override + public V getInterceptedViewOf(@NonNull final BindViewInterceptor interceptor) { + return mViewBinder.getInterceptedViewOf(interceptor); + } + + @NonNull + @Override + public List getInterceptors( + @NonNull final Filter predicate) { + return mViewBinder.getInterceptors(predicate); + } + + @Override + public String getLoggingTag() { + return TAG; + } + + public P getPresenter() { + return mPresenter; + } + + /** + * Invalidates the cache of the latest bound view. Forces the next binding of the view to run + * through all the interceptors (again). + */ + @Override + public void invalidateView() { + mViewBinder.invalidateView(); + } + + // TODO + public void onCreate(final Bundle savedInstanceState) { + if (mPresenter == null && savedInstanceState != null) { + // recover with Savior + // this should always work. + final String recoveredPresenterId = savedInstanceState + .getString(SAVED_STATE_PRESENTER_ID); + if (recoveredPresenterId != null) { + TiLog.v(TAG, "try to recover Presenter with id: " + recoveredPresenterId); + //noinspection unchecked + mPresenter = (P) PresenterSavior.INSTANCE.recover(recoveredPresenterId); + if (mPresenter != null) { + // save recovered presenter with new id. No other instance of this activity, + // holding the presenter before, is now able to remove the reference to + // this presenter from the savior + PresenterSavior.INSTANCE.free(recoveredPresenterId); + mPresenterId = PresenterSavior.INSTANCE.safe(mPresenter); + } + TiLog.v(TAG, "recovered Presenter " + mPresenter); + } + } + + if (mPresenter == null) { + mPresenter = mFragment.providePresenter(); + TiLog.v(TAG, "created Presenter: " + mPresenter); + final TiConfiguration config = mPresenter.getConfig(); + if (config.shouldRetainPresenter() && config.useStaticSaviorToRetain()) { + mPresenterId = PresenterSavior.INSTANCE.safe(mPresenter); + } + mPresenter.create(); + } + + final TiConfiguration config = mPresenter.getConfig(); + if (config.isCallOnMainThreadInterceptorEnabled()) { + addBindViewInterceptor(new CallOnMainThreadInterceptor()); + } + + if (config.isDistinctUntilChangedInterceptorEnabled()) { + addBindViewInterceptor(new DistinctUntilChangedInterceptor()); + } + + if (config.shouldRetainPresenter()) { + mFragment.setRetainInstance(true); + } + } + + // TODO + @SuppressWarnings("UnusedParameters") + public void onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container, + @Nullable final Bundle savedInstanceState) { + mViewBinder.invalidateView(); + } + + // TODO + public void onDestroy() { + //FIXME handle attach/detach state + + logState(); + + boolean destroyPresenter = false; + if (mFragment.getActivity().isFinishing()) { + // Probably a backpress and not a configuration change + // Activity will not be recreated and finally destroyed, also destroyed the presenter + destroyPresenter = true; + TiLog.v(TAG, "Activity is finishing, destroying presenter " + mPresenter); + } + + final TiConfiguration config = mPresenter.getConfig(); + if (!destroyPresenter && + !config.shouldRetainPresenter()) { + // configuration says the presenter should not be retained, a new presenter instance + // will be created and the current presenter should be destroyed + destroyPresenter = true; + TiLog.v(TAG, "presenter configured as not retaining, destroying " + mPresenter); + } + + if (!destroyPresenter && + !config.useStaticSaviorToRetain() + && AndroidDeveloperOptions.isDontKeepActivitiesEnabled(mFragment.getActivity())) { + // configuration says the PresenterSavior should not be used. Retaining the presenter + // relays on the Activity nonConfigurationInstance which is always null when + // "don't keep activities" is enabled. + // a new presenter instance will be created and the current presenter should be destroyed + destroyPresenter = true; + TiLog.v(TAG, "the PresenterSavior is disabled and \"don\'t keep activities\" is " + + "activated. The presenter can't be retained. Destroying " + mPresenter); + } + + if (destroyPresenter) { + mPresenter.destroy(); + PresenterSavior.INSTANCE.free(mPresenterId); + } else { + TiLog.v(TAG, "not destroying " + mPresenter + + " which will be reused by the next Activity instance, recreating..."); + } + } + + // TODO + public void onDestroyView() { + mPresenter.detachView(); + } + + // TODO + public void onSaveInstanceState(final Bundle outState) { + outState.putString(SAVED_STATE_PRESENTER_ID, mPresenterId); + } + + // TODO + public void onStart() { + mActivityStarted = true; + + if (isUiPossible()) { + mFragment.getActivity().getWindow().getDecorView().post(new Runnable() { + @Override + public void run() { + if (isUiPossible() && mActivityStarted) { + mViewBinder.bindView(mPresenter, TiFragmentDelegate.this); + } + } + }); + } + } + + // TODO + public void onStop() { + mActivityStarted = false; + mPresenter.detachView(); + } + + /** + * the default implementation assumes that the fragment is the view and implements the {@link + * TiView} interface. Override this method for a different behaviour. + * + * @return the object implementing the TiView interface + */ + @NonNull + public V provideView() { + + final Class foundViewInterface = AnnotationUtil + .getInterfaceOfClassExtendingGivenInterface(mFragment.getClass(), TiView.class); + + if (foundViewInterface == null) { + throw new IllegalArgumentException( + "This Fragment doesn't implement a TiView interface. " + + "This is the default behaviour. Override provideView() to explicitly change this."); + } else { + if (foundViewInterface.getSimpleName().equals("TiView")) { + throw new IllegalArgumentException( + "extending TiView doesn't make sense, it's an empty interface." + + " This is the default behaviour. Override provideView() to explicitly change this."); + } else { + // assume that the fragment itself is the view and implements the TiView interface + //noinspection unchecked + return (V) mFragment; + } + } + } + + @Override + public String toString() { + String presenter = getPresenter() == null ? "null" : + getPresenter().getClass().getSimpleName() + + "@" + Integer.toHexString(getPresenter().hashCode()); + + return getClass().getSimpleName() + + ":" + TiFragmentDelegate.class.getSimpleName() + + "@" + Integer.toHexString(hashCode()) + + "{presenter=" + presenter + "}"; + } + + public String fragmentToString() { + String presenter = getPresenter() == null ? "null" : + getPresenter().getClass().getSimpleName() + + "@" + Integer.toHexString(getPresenter().hashCode()); + + return mFragment.getClass().getSimpleName() + + "@" + Integer.toHexString(hashCode()) + + "{presenter=" + presenter + "}"; + } + + private boolean isUiPossible() { + return mFragment.isAdded() && !mFragment.isDetached(); + } + + private void logState() { + if (ENABLE_DEBUG_LOGGING) { + TiLog.v(TAG, "isChangingConfigurations = " + mFragment.getActivity().isChangingConfigurations()); + TiLog.v(TAG, "isActivityFinishing = " + mFragment.getActivity().isFinishing()); + TiLog.v(TAG, "isAdded = " + mFragment.isAdded()); + TiLog.v(TAG, "isDetached = " + mFragment.isDetached()); + TiLog.v(TAG, "isDontKeepActivitiesEnabled = " + AndroidDeveloperOptions + .isDontKeepActivitiesEnabled(mFragment.getActivity())); + + final TiConfiguration config = mPresenter.getConfig(); + TiLog.v(TAG, "shouldRetain = " + config.shouldRetainPresenter()); + TiLog.v(TAG, "useStaticSavior = " + config.useStaticSaviorToRetain()); + } + } +} \ No newline at end of file From 0d5f8c3d033697c7447a1155da7c05ffcff9a282 Mon Sep 17 00:00:00 2001 From: Ralf Wondratschek Date: Thu, 15 Dec 2016 16:40:50 +0100 Subject: [PATCH 2/6] Remove leftover TODOs --- .../thirtyinch/internal/TiFragmentDelegate.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegate.java b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegate.java index f76d8c5f..56020697 100644 --- a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegate.java +++ b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegate.java @@ -90,7 +90,6 @@ public void invalidateView() { mViewBinder.invalidateView(); } - // TODO public void onCreate(final Bundle savedInstanceState) { if (mPresenter == null && savedInstanceState != null) { // recover with Savior @@ -136,14 +135,12 @@ public void onCreate(final Bundle savedInstanceState) { } } - // TODO @SuppressWarnings("UnusedParameters") public void onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) { mViewBinder.invalidateView(); } - // TODO public void onDestroy() { //FIXME handle attach/detach state @@ -187,17 +184,14 @@ public void onDestroy() { } } - // TODO public void onDestroyView() { mPresenter.detachView(); } - // TODO public void onSaveInstanceState(final Bundle outState) { outState.putString(SAVED_STATE_PRESENTER_ID, mPresenterId); } - // TODO public void onStart() { mActivityStarted = true; @@ -213,7 +207,6 @@ public void run() { } } - // TODO public void onStop() { mActivityStarted = false; mPresenter.detachView(); From 5ed90916c5ddda15b353997fe78e5afbec0f5bc4 Mon Sep 17 00:00:00 2001 From: Pascal Welsch Date: Thu, 15 Dec 2016 18:42:46 +0100 Subject: [PATCH 3/6] Remove TiFragmentDelegate extending tutorial from README.md it better belongs to the wiki, the README.md shouldn't be cluttered --- README.md | 69 ------------------------------------------------------- 1 file changed, 69 deletions(-) diff --git a/README.md b/README.md index 56a05c1b..be701177 100644 --- a/README.md +++ b/README.md @@ -316,75 +316,6 @@ public class HelloWorldActivity extends CompositeActivity implements HelloWorldV } ``` -### TiFragmentDelegate - -If you can't extend `TiFragment` in your existing codebase, then you can create a `TiFragmentDelegate` object yourself. You only need forward all lifecycle callbacks like in the following snippet. - -```java -public class SampleFragment extends Fragment implements TiPresenterProvider, SampleView { - - private final TiFragmentDelegate mDelegate = new TiFragmentDelegate<>(this); - - private TextView mSampleText; - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mDelegate.onCreate(savedInstanceState); - } - - @Nullable - @Override - public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - mDelegate.onCreateView(inflater, container, savedInstanceState); - - final ViewGroup view = (ViewGroup) inflater.inflate(R.layout.fragment_sample, container, false); - mSampleText = (TextView) view.findViewById(R.id.sample_text); - return view; - } - - @Override - public void onStart() { - super.onStart(); - mDelegate.onStart(); - } - - @Override - public void onStop() { - mDelegate.onStop(); - super.onStop(); - } - - @Override - public void onDestroyView() { - mDelegate.onDestroyView(); - super.onDestroyView(); - } - - @Override - public void onDestroy() { - super.onDestroy(); - mDelegate.onDestroy(); - } - - @Override - public void onSaveInstanceState(final Bundle outState) { - super.onSaveInstanceState(outState); - mDelegate.onSaveInstanceState(outState); - } - - @NonNull - @Override - public SamplePresenter providePresenter() { - return new SamplePresenter(); - } - - @Override - public void showText(final String s) { - mSampleText.setText(s); - } -} -``` Yes you have to extends `CompositeActivity`, but that's the last level of inheritance you'll ever need. From 5c54a3a0f7f5a1ab63a2dfdd28e0c05ee7105500 Mon Sep 17 00:00:00 2001 From: Pascal Welsch Date: Thu, 15 Dec 2016 18:52:51 +0100 Subject: [PATCH 4/6] Implement TiFragmentDelegate analog to TiActivityDelegate It's not possible to pass a single Object into the delegate because in CompositeAndroid the TiViewProvider and the Fragment are different objects. Adding all interfaces one by one helps when testing. There is no god object required which implements all interfaces at once. Logging tag and toString belong in the Fragments itself rather than the delegate. Before it was using the logging tag "TiFragmentDelegate" instead of "SampleFragment" which doesn't help a lot when debugging provideView was moved into the Fragments because this works differently between Compositeandroid and the Ti implementations --- .../thirtyinch/TiDialogFragment.java | 95 +++++++++-- .../grandcentrix/thirtyinch/TiFragment.java | 98 +++++++++-- .../internal/DelegatedTiFragment.java | 57 +++++++ .../internal/TiFragmentDelegate.java | 152 +++++++----------- 4 files changed, 284 insertions(+), 118 deletions(-) create mode 100644 thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/DelegatedTiFragment.java diff --git a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiDialogFragment.java b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiDialogFragment.java index 999d0c68..c363ca41 100644 --- a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiDialogFragment.java +++ b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiDialogFragment.java @@ -1,5 +1,14 @@ package net.grandcentrix.thirtyinch; +import net.grandcentrix.thirtyinch.internal.DelegatedTiFragment; +import net.grandcentrix.thirtyinch.internal.InterceptableViewBinder; +import net.grandcentrix.thirtyinch.internal.TiFragmentDelegate; +import net.grandcentrix.thirtyinch.internal.TiLoggingTagProvider; +import net.grandcentrix.thirtyinch.internal.TiPresenterProvider; +import net.grandcentrix.thirtyinch.internal.TiViewProvider; +import net.grandcentrix.thirtyinch.util.AndroidDeveloperOptions; +import net.grandcentrix.thirtyinch.util.AnnotationUtil; + import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -8,19 +17,19 @@ import android.view.View; import android.view.ViewGroup; -import net.grandcentrix.thirtyinch.internal.InterceptableViewBinder; -import net.grandcentrix.thirtyinch.internal.TiFragmentDelegate; -import net.grandcentrix.thirtyinch.internal.TiLoggingTagProvider; -import net.grandcentrix.thirtyinch.internal.TiPresenterProvider; -import net.grandcentrix.thirtyinch.internal.TiViewProvider; - import java.util.List; public abstract class TiDialogFragment

, V extends TiView> - extends AppCompatDialogFragment implements TiPresenterProvider

, TiLoggingTagProvider, + extends AppCompatDialogFragment + implements DelegatedTiFragment, TiPresenterProvider

, TiLoggingTagProvider, TiViewProvider, InterceptableViewBinder { - private final TiFragmentDelegate mDelegate = new TiFragmentDelegate<>(this); + private final String TAG = this.getClass().getSimpleName() + + ":" + TiDialogFragment.class.getSimpleName() + + "@" + Integer.toHexString(this.hashCode()); + + private final TiFragmentDelegate mDelegate = + new TiFragmentDelegate<>(this, this, this, this); @NonNull @Override @@ -36,13 +45,14 @@ public V getInterceptedViewOf(@NonNull final BindViewInterceptor interceptor) { @NonNull @Override - public List getInterceptors(@NonNull final Filter predicate) { + public List getInterceptors( + @NonNull final Filter predicate) { return mDelegate.getInterceptors(predicate); } @Override public String getLoggingTag() { - return mDelegate.getLoggingTag(); + return TAG; } public P getPresenter() { @@ -58,6 +68,31 @@ public void invalidateView() { mDelegate.invalidateView(); } + @Override + public boolean isActivityChangingConfigurations() { + return getActivity().isChangingConfigurations(); + } + + @Override + public boolean isActivityFinishing() { + return getActivity().isFinishing(); + } + + @Override + public boolean isDontKeepActivitiesEnabled() { + return AndroidDeveloperOptions.isDontKeepActivitiesEnabled(getActivity()); + } + + @Override + public boolean isFragmentAdded() { + return isAdded(); + } + + @Override + public boolean isFragmentDetached() { + return isDetached(); + } + @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -67,7 +102,7 @@ public void onCreate(final Bundle savedInstanceState) { @Nullable @Override public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container, - @Nullable final Bundle savedInstanceState) { + @Nullable final Bundle savedInstanceState) { mDelegate.invalidateView(); return super.onCreateView(inflater, container, savedInstanceState); } @@ -102,6 +137,11 @@ public void onStop() { super.onStop(); } + @Override + public boolean postToMessageQueue(final Runnable runnable) { + return getActivity().getWindow().getDecorView().post(runnable); + } + /** * the default implementation assumes that the fragment is the view and implements the {@link * TiView} interface. Override this method for a different behaviour. @@ -110,11 +150,40 @@ public void onStop() { */ @NonNull public V provideView() { - return mDelegate.provideView(); + + final Class foundViewInterface = AnnotationUtil + .getInterfaceOfClassExtendingGivenInterface(getClass(), TiView.class); + + if (foundViewInterface == null) { + throw new IllegalArgumentException( + "This DialogFragment doesn't implement a TiView interface. " + + "This is the default behaviour. Override provideView() to explicitly change this."); + } else { + if (foundViewInterface.getSimpleName().equals("TiView")) { + throw new IllegalArgumentException( + "extending TiView doesn't make sense, it's an empty interface." + + " This is the default behaviour. Override provideView() to explicitly change this."); + } else { + // assume that the fragment itself is the view and implements the TiView interface + //noinspection unchecked + return (V) this; + } + } + } + + @Override + public void setFragmentRetainInstance(final boolean retain) { + setRetainInstance(retain); } @Override public String toString() { - return mDelegate.fragmentToString(); + String presenter = getPresenter() == null ? "null" : + getPresenter().getClass().getSimpleName() + + "@" + Integer.toHexString(getPresenter().hashCode()); + + return getClass().getSimpleName() + + "@" + Integer.toHexString(hashCode()) + + "{presenter=" + presenter + "}"; } } diff --git a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiFragment.java b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiFragment.java index b70f2611..610ae9e4 100644 --- a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiFragment.java +++ b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiFragment.java @@ -15,6 +15,15 @@ package net.grandcentrix.thirtyinch; +import net.grandcentrix.thirtyinch.internal.DelegatedTiFragment; +import net.grandcentrix.thirtyinch.internal.InterceptableViewBinder; +import net.grandcentrix.thirtyinch.internal.TiFragmentDelegate; +import net.grandcentrix.thirtyinch.internal.TiLoggingTagProvider; +import net.grandcentrix.thirtyinch.internal.TiPresenterProvider; +import net.grandcentrix.thirtyinch.internal.TiViewProvider; +import net.grandcentrix.thirtyinch.util.AndroidDeveloperOptions; +import net.grandcentrix.thirtyinch.util.AnnotationUtil; + import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -23,19 +32,18 @@ import android.view.View; import android.view.ViewGroup; -import net.grandcentrix.thirtyinch.internal.InterceptableViewBinder; -import net.grandcentrix.thirtyinch.internal.TiFragmentDelegate; -import net.grandcentrix.thirtyinch.internal.TiLoggingTagProvider; -import net.grandcentrix.thirtyinch.internal.TiPresenterProvider; -import net.grandcentrix.thirtyinch.internal.TiViewProvider; - import java.util.List; -public abstract class TiFragment

, V extends TiView> - extends Fragment implements TiPresenterProvider

, TiLoggingTagProvider, +public abstract class TiFragment

, V extends TiView> extends Fragment + implements DelegatedTiFragment, TiPresenterProvider

, TiLoggingTagProvider, TiViewProvider, InterceptableViewBinder { - private final TiFragmentDelegate mDelegate = new TiFragmentDelegate<>(this); + private final String TAG = this.getClass().getSimpleName() + + ":" + TiFragment.class.getSimpleName() + + "@" + Integer.toHexString(this.hashCode()); + + private final TiFragmentDelegate mDelegate = + new TiFragmentDelegate<>(this, this, this, this); @NonNull @Override @@ -51,13 +59,14 @@ public V getInterceptedViewOf(@NonNull final BindViewInterceptor interceptor) { @NonNull @Override - public List getInterceptors(@NonNull final Filter predicate) { + public List getInterceptors( + @NonNull final Filter predicate) { return mDelegate.getInterceptors(predicate); } @Override public String getLoggingTag() { - return mDelegate.getLoggingTag(); + return TAG; } public P getPresenter() { @@ -73,6 +82,31 @@ public void invalidateView() { mDelegate.invalidateView(); } + @Override + public boolean isActivityChangingConfigurations() { + return getActivity().isChangingConfigurations(); + } + + @Override + public boolean isActivityFinishing() { + return getActivity().isFinishing(); + } + + @Override + public boolean isDontKeepActivitiesEnabled() { + return AndroidDeveloperOptions.isDontKeepActivitiesEnabled(getActivity()); + } + + @Override + public boolean isFragmentAdded() { + return isAdded(); + } + + @Override + public boolean isFragmentDetached() { + return isDetached(); + } + @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -81,12 +115,12 @@ public void onCreate(final Bundle savedInstanceState) { @Nullable @Override - public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) { + public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container, + @Nullable final Bundle savedInstanceState) { mDelegate.onCreateView(inflater, container, savedInstanceState); return super.onCreateView(inflater, container, savedInstanceState); } - @Override public void onDestroy() { super.onDestroy(); @@ -117,6 +151,11 @@ public void onStop() { super.onStop(); } + @Override + public boolean postToMessageQueue(final Runnable runnable) { + return getActivity().getWindow().getDecorView().post(runnable); + } + /** * the default implementation assumes that the fragment is the view and implements the {@link * TiView} interface. Override this method for a different behaviour. @@ -125,11 +164,40 @@ public void onStop() { */ @NonNull public V provideView() { - return mDelegate.provideView(); + + final Class foundViewInterface = AnnotationUtil + .getInterfaceOfClassExtendingGivenInterface(getClass(), TiView.class); + + if (foundViewInterface == null) { + throw new IllegalArgumentException( + "This Fragment doesn't implement a TiView interface. " + + "This is the default behaviour. Override provideView() to explicitly change this."); + } else { + if (foundViewInterface.getSimpleName().equals("TiView")) { + throw new IllegalArgumentException( + "extending TiView doesn't make sense, it's an empty interface." + + " This is the default behaviour. Override provideView() to explicitly change this."); + } else { + // assume that the fragment itself is the view and implements the TiView interface + //noinspection unchecked + return (V) this; + } + } + } + + @Override + public void setFragmentRetainInstance(final boolean retain) { + setRetainInstance(retain); } @Override public String toString() { - return mDelegate.fragmentToString(); + String presenter = getPresenter() == null ? "null" : + getPresenter().getClass().getSimpleName() + + "@" + Integer.toHexString(getPresenter().hashCode()); + + return getClass().getSimpleName() + + "@" + Integer.toHexString(hashCode()) + + "{presenter=" + presenter + "}"; } } \ No newline at end of file diff --git a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/DelegatedTiFragment.java b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/DelegatedTiFragment.java new file mode 100644 index 00000000..c3c85e84 --- /dev/null +++ b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/DelegatedTiFragment.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2016 grandcentrix 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 net.grandcentrix.thirtyinch.internal; + +import android.app.Activity; +import android.support.v4.app.Fragment; + +public interface DelegatedTiFragment { + + /** + * @return {@link Activity#isChangingConfigurations()} + */ + boolean isActivityChangingConfigurations(); + + /** + * @return {@link Activity#isFinishing()} + */ + boolean isActivityFinishing(); + + /** + * @return true when the developer option "Don't keep Activities" is enabled + */ + boolean isDontKeepActivitiesEnabled(); + + /** + * @return {@link Fragment#isAdded()} + */ + boolean isFragmentAdded(); + + /** + * @return {@link Fragment#isDetached()} + */ + boolean isFragmentDetached(); + + /** + * Post the runnable on the UI queue + */ + boolean postToMessageQueue(Runnable runnable); + + /** + * Call {@link Fragment#setRetainInstance(boolean)} + */ + void setFragmentRetainInstance(final boolean retain); +} diff --git a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegate.java b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegate.java index 56020697..9d7c00cc 100644 --- a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegate.java +++ b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegate.java @@ -1,32 +1,32 @@ package net.grandcentrix.thirtyinch.internal; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.ViewGroup; - import net.grandcentrix.thirtyinch.BindViewInterceptor; import net.grandcentrix.thirtyinch.Removable; import net.grandcentrix.thirtyinch.TiConfiguration; +import net.grandcentrix.thirtyinch.TiDialogFragment; import net.grandcentrix.thirtyinch.TiFragment; import net.grandcentrix.thirtyinch.TiLog; import net.grandcentrix.thirtyinch.TiPresenter; import net.grandcentrix.thirtyinch.TiView; import net.grandcentrix.thirtyinch.callonmainthread.CallOnMainThreadInterceptor; import net.grandcentrix.thirtyinch.distinctuntilchanged.DistinctUntilChangedInterceptor; -import net.grandcentrix.thirtyinch.util.AndroidDeveloperOptions; -import net.grandcentrix.thirtyinch.util.AnnotationUtil; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.ViewGroup; import java.util.List; /** - * This delegate allows sharing the fragment code between the {@link TiFragment} and other - * {@link Fragment} implementations in 3rd party code. + * This delegate allows sharing the fragment code between the {@link TiFragment}, + * {@link TiDialogFragment}, {@code TiFragmentPlugin} and {@code TiDialogFragmentPlugin}. + *

+ * It also allows 3rd party developers do add this delegate to other Fragments using composition. */ -public class TiFragmentDelegate

, V extends TiView, F extends Fragment & TiPresenterProvider

> - implements TiLoggingTagProvider, TiViewProvider, InterceptableViewBinder { +public class TiFragmentDelegate

, V extends TiView> + implements InterceptableViewBinder { private static final String SAVED_STATE_PRESENTER_ID = "presenter_id"; @@ -35,22 +35,31 @@ public class TiFragmentDelegate

, V extends TiView, F ex */ private static final boolean ENABLE_DEBUG_LOGGING = false; - private final String TAG = this.getClass().getSimpleName() - + ":" + TiFragmentDelegate.class.getSimpleName() - + "@" + Integer.toHexString(this.hashCode()); - private volatile boolean mActivityStarted = false; + private final TiLoggingTagProvider mLogTag; + private P mPresenter; private String mPresenterId; - private PresenterViewBinder mViewBinder = new PresenterViewBinder<>(this); + private final TiPresenterProvider

mPresenterProvider; + + private final DelegatedTiFragment mTiFragment; - private final F mFragment; + private final PresenterViewBinder mViewBinder; - public TiFragmentDelegate(F fragment) { - mFragment = fragment; + private final TiViewProvider mViewProvider; + + public TiFragmentDelegate(final DelegatedTiFragment fragmentProvider, + final TiViewProvider viewProvider, + final TiPresenterProvider

presenterProvider, + final TiLoggingTagProvider logTag) { + mTiFragment = fragmentProvider; + mViewProvider = viewProvider; + mPresenterProvider = presenterProvider; + mLogTag = logTag; + mViewBinder = new PresenterViewBinder<>(logTag); } @NonNull @@ -72,11 +81,6 @@ public List getInterceptors( return mViewBinder.getInterceptors(predicate); } - @Override - public String getLoggingTag() { - return TAG; - } - public P getPresenter() { return mPresenter; } @@ -97,7 +101,8 @@ public void onCreate(final Bundle savedInstanceState) { final String recoveredPresenterId = savedInstanceState .getString(SAVED_STATE_PRESENTER_ID); if (recoveredPresenterId != null) { - TiLog.v(TAG, "try to recover Presenter with id: " + recoveredPresenterId); + TiLog.v(mLogTag.getLoggingTag(), + "try to recover Presenter with id: " + recoveredPresenterId); //noinspection unchecked mPresenter = (P) PresenterSavior.INSTANCE.recover(recoveredPresenterId); if (mPresenter != null) { @@ -107,13 +112,13 @@ public void onCreate(final Bundle savedInstanceState) { PresenterSavior.INSTANCE.free(recoveredPresenterId); mPresenterId = PresenterSavior.INSTANCE.safe(mPresenter); } - TiLog.v(TAG, "recovered Presenter " + mPresenter); + TiLog.v(mLogTag.getLoggingTag(), "recovered Presenter " + mPresenter); } } if (mPresenter == null) { - mPresenter = mFragment.providePresenter(); - TiLog.v(TAG, "created Presenter: " + mPresenter); + mPresenter = mPresenterProvider.providePresenter(); + TiLog.v(mLogTag.getLoggingTag(), "created Presenter: " + mPresenter); final TiConfiguration config = mPresenter.getConfig(); if (config.shouldRetainPresenter() && config.useStaticSaviorToRetain()) { mPresenterId = PresenterSavior.INSTANCE.safe(mPresenter); @@ -131,13 +136,13 @@ public void onCreate(final Bundle savedInstanceState) { } if (config.shouldRetainPresenter()) { - mFragment.setRetainInstance(true); + mTiFragment.setFragmentRetainInstance(true); } } @SuppressWarnings("UnusedParameters") public void onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container, - @Nullable final Bundle savedInstanceState) { + @Nullable final Bundle savedInstanceState) { mViewBinder.invalidateView(); } @@ -147,11 +152,12 @@ public void onDestroy() { logState(); boolean destroyPresenter = false; - if (mFragment.getActivity().isFinishing()) { + if (mTiFragment.isActivityFinishing()) { // Probably a backpress and not a configuration change // Activity will not be recreated and finally destroyed, also destroyed the presenter destroyPresenter = true; - TiLog.v(TAG, "Activity is finishing, destroying presenter " + mPresenter); + TiLog.v(mLogTag.getLoggingTag(), + "Activity is finishing, destroying presenter " + mPresenter); } final TiConfiguration config = mPresenter.getConfig(); @@ -160,26 +166,28 @@ public void onDestroy() { // configuration says the presenter should not be retained, a new presenter instance // will be created and the current presenter should be destroyed destroyPresenter = true; - TiLog.v(TAG, "presenter configured as not retaining, destroying " + mPresenter); + TiLog.v(mLogTag.getLoggingTag(), + "presenter configured as not retaining, destroying " + mPresenter); } if (!destroyPresenter && - !config.useStaticSaviorToRetain() - && AndroidDeveloperOptions.isDontKeepActivitiesEnabled(mFragment.getActivity())) { + !config.useStaticSaviorToRetain() && mTiFragment.isDontKeepActivitiesEnabled()) { // configuration says the PresenterSavior should not be used. Retaining the presenter // relays on the Activity nonConfigurationInstance which is always null when // "don't keep activities" is enabled. // a new presenter instance will be created and the current presenter should be destroyed destroyPresenter = true; - TiLog.v(TAG, "the PresenterSavior is disabled and \"don\'t keep activities\" is " - + "activated. The presenter can't be retained. Destroying " + mPresenter); + TiLog.v(mLogTag.getLoggingTag(), + "the PresenterSavior is disabled and \"don\'t keep activities\" is " + + "activated. The presenter can't be retained. Destroying " + + mPresenter); } if (destroyPresenter) { mPresenter.destroy(); PresenterSavior.INSTANCE.free(mPresenterId); } else { - TiLog.v(TAG, "not destroying " + mPresenter + TiLog.v(mLogTag.getLoggingTag(), "not destroying " + mPresenter + " which will be reused by the next Activity instance, recreating..."); } } @@ -196,11 +204,11 @@ public void onStart() { mActivityStarted = true; if (isUiPossible()) { - mFragment.getActivity().getWindow().getDecorView().post(new Runnable() { + mTiFragment.postToMessageQueue(new Runnable() { @Override public void run() { if (isUiPossible() && mActivityStarted) { - mViewBinder.bindView(mPresenter, TiFragmentDelegate.this); + mViewBinder.bindView(mPresenter, mViewProvider); } } }); @@ -212,35 +220,6 @@ public void onStop() { mPresenter.detachView(); } - /** - * the default implementation assumes that the fragment is the view and implements the {@link - * TiView} interface. Override this method for a different behaviour. - * - * @return the object implementing the TiView interface - */ - @NonNull - public V provideView() { - - final Class foundViewInterface = AnnotationUtil - .getInterfaceOfClassExtendingGivenInterface(mFragment.getClass(), TiView.class); - - if (foundViewInterface == null) { - throw new IllegalArgumentException( - "This Fragment doesn't implement a TiView interface. " - + "This is the default behaviour. Override provideView() to explicitly change this."); - } else { - if (foundViewInterface.getSimpleName().equals("TiView")) { - throw new IllegalArgumentException( - "extending TiView doesn't make sense, it's an empty interface." - + " This is the default behaviour. Override provideView() to explicitly change this."); - } else { - // assume that the fragment itself is the view and implements the TiView interface - //noinspection unchecked - return (V) mFragment; - } - } - } - @Override public String toString() { String presenter = getPresenter() == null ? "null" : @@ -253,32 +232,25 @@ public String toString() { + "{presenter=" + presenter + "}"; } - public String fragmentToString() { - String presenter = getPresenter() == null ? "null" : - getPresenter().getClass().getSimpleName() - + "@" + Integer.toHexString(getPresenter().hashCode()); - - return mFragment.getClass().getSimpleName() - + "@" + Integer.toHexString(hashCode()) - + "{presenter=" + presenter + "}"; - } - private boolean isUiPossible() { - return mFragment.isAdded() && !mFragment.isDetached(); + return mTiFragment.isFragmentAdded() && !mTiFragment.isFragmentDetached(); } private void logState() { if (ENABLE_DEBUG_LOGGING) { - TiLog.v(TAG, "isChangingConfigurations = " + mFragment.getActivity().isChangingConfigurations()); - TiLog.v(TAG, "isActivityFinishing = " + mFragment.getActivity().isFinishing()); - TiLog.v(TAG, "isAdded = " + mFragment.isAdded()); - TiLog.v(TAG, "isDetached = " + mFragment.isDetached()); - TiLog.v(TAG, "isDontKeepActivitiesEnabled = " + AndroidDeveloperOptions - .isDontKeepActivitiesEnabled(mFragment.getActivity())); + TiLog.v(mLogTag.getLoggingTag(), + "isChangingConfigurations = " + mTiFragment.isActivityChangingConfigurations()); + TiLog.v(mLogTag.getLoggingTag(), + "isActivityFinishing = " + mTiFragment.isActivityFinishing()); + TiLog.v(mLogTag.getLoggingTag(), "isAdded = " + mTiFragment.isFragmentAdded()); + TiLog.v(mLogTag.getLoggingTag(), "isDetached = " + mTiFragment.isFragmentDetached()); + TiLog.v(mLogTag.getLoggingTag(), + "isDontKeepActivitiesEnabled = " + mTiFragment.isDontKeepActivitiesEnabled()); final TiConfiguration config = mPresenter.getConfig(); - TiLog.v(TAG, "shouldRetain = " + config.shouldRetainPresenter()); - TiLog.v(TAG, "useStaticSavior = " + config.useStaticSaviorToRetain()); + TiLog.v(mLogTag.getLoggingTag(), "shouldRetain = " + config.shouldRetainPresenter()); + TiLog.v(mLogTag.getLoggingTag(), + "useStaticSavior = " + config.useStaticSaviorToRetain()); } } } \ No newline at end of file From 687a9ec508b5367f44f63c50149d420fff439bc8 Mon Sep 17 00:00:00 2001 From: Ralf Wondratschek Date: Fri, 16 Dec 2016 10:17:17 +0100 Subject: [PATCH 5/6] Add method suffix to indicate when delegate methods should be called --- README.md | 1 - .../thirtyinch/TiDialogFragment.java | 28 ++++++++--------- .../grandcentrix/thirtyinch/TiFragment.java | 30 +++++++++---------- .../internal/TiFragmentDelegate.java | 28 ++++++++--------- 4 files changed, 43 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index be701177..49431193 100644 --- a/README.md +++ b/README.md @@ -316,7 +316,6 @@ public class HelloWorldActivity extends CompositeActivity implements HelloWorldV } ``` - Yes you have to extends `CompositeActivity`, but that's the last level of inheritance you'll ever need. ## Versions diff --git a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiDialogFragment.java b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiDialogFragment.java index c363ca41..602f59af 100644 --- a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiDialogFragment.java +++ b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiDialogFragment.java @@ -1,5 +1,13 @@ package net.grandcentrix.thirtyinch; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatDialogFragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + import net.grandcentrix.thirtyinch.internal.DelegatedTiFragment; import net.grandcentrix.thirtyinch.internal.InterceptableViewBinder; import net.grandcentrix.thirtyinch.internal.TiFragmentDelegate; @@ -9,14 +17,6 @@ import net.grandcentrix.thirtyinch.util.AndroidDeveloperOptions; import net.grandcentrix.thirtyinch.util.AnnotationUtil; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatDialogFragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - import java.util.List; public abstract class TiDialogFragment

, V extends TiView> @@ -96,7 +96,7 @@ public boolean isFragmentDetached() { @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mDelegate.onCreate(savedInstanceState); + mDelegate.onCreate_afterSuper(savedInstanceState); } @Nullable @@ -110,30 +110,30 @@ public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGrou @Override public void onDestroy() { super.onDestroy(); - mDelegate.onDestroy(); + mDelegate.onDestroy_afterSuper(); } @Override public void onDestroyView() { - mDelegate.onDestroyView(); + mDelegate.onDestroyView_beforeSuper(); super.onDestroyView(); } @Override public void onSaveInstanceState(final Bundle outState) { super.onSaveInstanceState(outState); - mDelegate.onSaveInstanceState(outState); + mDelegate.onSaveInstanceState_afterSuper(outState); } @Override public void onStart() { super.onStart(); - mDelegate.onStart(); + mDelegate.onStart_afterSuper(); } @Override public void onStop() { - mDelegate.onStop(); + mDelegate.onStop_beforeSuper(); super.onStop(); } diff --git a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiFragment.java b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiFragment.java index 610ae9e4..7fe0d736 100644 --- a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiFragment.java +++ b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiFragment.java @@ -15,6 +15,14 @@ package net.grandcentrix.thirtyinch; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + import net.grandcentrix.thirtyinch.internal.DelegatedTiFragment; import net.grandcentrix.thirtyinch.internal.InterceptableViewBinder; import net.grandcentrix.thirtyinch.internal.TiFragmentDelegate; @@ -24,14 +32,6 @@ import net.grandcentrix.thirtyinch.util.AndroidDeveloperOptions; import net.grandcentrix.thirtyinch.util.AnnotationUtil; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - import java.util.List; public abstract class TiFragment

, V extends TiView> extends Fragment @@ -110,44 +110,44 @@ public boolean isFragmentDetached() { @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mDelegate.onCreate(savedInstanceState); + mDelegate.onCreate_afterSuper(savedInstanceState); } @Nullable @Override public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container, @Nullable final Bundle savedInstanceState) { - mDelegate.onCreateView(inflater, container, savedInstanceState); + mDelegate.onCreateView_beforeSuper(inflater, container, savedInstanceState); return super.onCreateView(inflater, container, savedInstanceState); } @Override public void onDestroy() { super.onDestroy(); - mDelegate.onDestroy(); + mDelegate.onDestroy_afterSuper(); } @Override public void onDestroyView() { - mDelegate.onDestroyView(); + mDelegate.onDestroyView_beforeSuper(); super.onDestroyView(); } @Override public void onSaveInstanceState(final Bundle outState) { super.onSaveInstanceState(outState); - mDelegate.onSaveInstanceState(outState); + mDelegate.onSaveInstanceState_afterSuper(outState); } @Override public void onStart() { super.onStart(); - mDelegate.onStart(); + mDelegate.onStart_afterSuper(); } @Override public void onStop() { - mDelegate.onStop(); + mDelegate.onStop_beforeSuper(); super.onStop(); } diff --git a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegate.java b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegate.java index 9d7c00cc..0fb0bb95 100644 --- a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegate.java +++ b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegate.java @@ -1,5 +1,11 @@ package net.grandcentrix.thirtyinch.internal; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.ViewGroup; + import net.grandcentrix.thirtyinch.BindViewInterceptor; import net.grandcentrix.thirtyinch.Removable; import net.grandcentrix.thirtyinch.TiConfiguration; @@ -11,12 +17,6 @@ import net.grandcentrix.thirtyinch.callonmainthread.CallOnMainThreadInterceptor; import net.grandcentrix.thirtyinch.distinctuntilchanged.DistinctUntilChangedInterceptor; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.view.LayoutInflater; -import android.view.ViewGroup; - import java.util.List; /** @@ -94,7 +94,7 @@ public void invalidateView() { mViewBinder.invalidateView(); } - public void onCreate(final Bundle savedInstanceState) { + public void onCreate_afterSuper(final Bundle savedInstanceState) { if (mPresenter == null && savedInstanceState != null) { // recover with Savior // this should always work. @@ -141,12 +141,12 @@ public void onCreate(final Bundle savedInstanceState) { } @SuppressWarnings("UnusedParameters") - public void onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container, - @Nullable final Bundle savedInstanceState) { + public void onCreateView_beforeSuper(final LayoutInflater inflater, @Nullable final ViewGroup container, + @Nullable final Bundle savedInstanceState) { mViewBinder.invalidateView(); } - public void onDestroy() { + public void onDestroy_afterSuper() { //FIXME handle attach/detach state logState(); @@ -192,15 +192,15 @@ public void onDestroy() { } } - public void onDestroyView() { + public void onDestroyView_beforeSuper() { mPresenter.detachView(); } - public void onSaveInstanceState(final Bundle outState) { + public void onSaveInstanceState_afterSuper(final Bundle outState) { outState.putString(SAVED_STATE_PRESENTER_ID, mPresenterId); } - public void onStart() { + public void onStart_afterSuper() { mActivityStarted = true; if (isUiPossible()) { @@ -215,7 +215,7 @@ public void run() { } } - public void onStop() { + public void onStop_beforeSuper() { mActivityStarted = false; mPresenter.detachView(); } From 0e7a537a65c946f50a45eee20c42b822b9f9410d Mon Sep 17 00:00:00 2001 From: Pascal Welsch Date: Fri, 16 Dec 2016 14:17:29 +0100 Subject: [PATCH 6/6] Rename DelegatedTiFragment isActivity* methods to isHostingActivity* reformat code --- .../thirtyinch/TiDialogFragment.java | 36 +++++++-------- .../grandcentrix/thirtyinch/TiFragment.java | 36 +++++++-------- .../internal/DelegatedTiFragment.java | 20 ++++----- .../internal/TiFragmentDelegate.java | 44 ++++++++++--------- 4 files changed, 70 insertions(+), 66 deletions(-) diff --git a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiDialogFragment.java b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiDialogFragment.java index 602f59af..b21f184d 100644 --- a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiDialogFragment.java +++ b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiDialogFragment.java @@ -1,13 +1,5 @@ package net.grandcentrix.thirtyinch; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatDialogFragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - import net.grandcentrix.thirtyinch.internal.DelegatedTiFragment; import net.grandcentrix.thirtyinch.internal.InterceptableViewBinder; import net.grandcentrix.thirtyinch.internal.TiFragmentDelegate; @@ -17,6 +9,14 @@ import net.grandcentrix.thirtyinch.util.AndroidDeveloperOptions; import net.grandcentrix.thirtyinch.util.AnnotationUtil; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatDialogFragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + import java.util.List; public abstract class TiDialogFragment

, V extends TiView> @@ -68,16 +68,6 @@ public void invalidateView() { mDelegate.invalidateView(); } - @Override - public boolean isActivityChangingConfigurations() { - return getActivity().isChangingConfigurations(); - } - - @Override - public boolean isActivityFinishing() { - return getActivity().isFinishing(); - } - @Override public boolean isDontKeepActivitiesEnabled() { return AndroidDeveloperOptions.isDontKeepActivitiesEnabled(getActivity()); @@ -93,6 +83,16 @@ public boolean isFragmentDetached() { return isDetached(); } + @Override + public boolean isHostingActivityChangingConfigurations() { + return getActivity().isChangingConfigurations(); + } + + @Override + public boolean isHostingActivityFinishing() { + return getActivity().isFinishing(); + } + @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); diff --git a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiFragment.java b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiFragment.java index 7fe0d736..9c598f07 100644 --- a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiFragment.java +++ b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/TiFragment.java @@ -15,14 +15,6 @@ package net.grandcentrix.thirtyinch; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - import net.grandcentrix.thirtyinch.internal.DelegatedTiFragment; import net.grandcentrix.thirtyinch.internal.InterceptableViewBinder; import net.grandcentrix.thirtyinch.internal.TiFragmentDelegate; @@ -32,6 +24,14 @@ import net.grandcentrix.thirtyinch.util.AndroidDeveloperOptions; import net.grandcentrix.thirtyinch.util.AnnotationUtil; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + import java.util.List; public abstract class TiFragment

, V extends TiView> extends Fragment @@ -82,16 +82,6 @@ public void invalidateView() { mDelegate.invalidateView(); } - @Override - public boolean isActivityChangingConfigurations() { - return getActivity().isChangingConfigurations(); - } - - @Override - public boolean isActivityFinishing() { - return getActivity().isFinishing(); - } - @Override public boolean isDontKeepActivitiesEnabled() { return AndroidDeveloperOptions.isDontKeepActivitiesEnabled(getActivity()); @@ -107,6 +97,16 @@ public boolean isFragmentDetached() { return isDetached(); } + @Override + public boolean isHostingActivityChangingConfigurations() { + return getActivity().isChangingConfigurations(); + } + + @Override + public boolean isHostingActivityFinishing() { + return getActivity().isFinishing(); + } + @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); diff --git a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/DelegatedTiFragment.java b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/DelegatedTiFragment.java index c3c85e84..808c1354 100644 --- a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/DelegatedTiFragment.java +++ b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/DelegatedTiFragment.java @@ -20,16 +20,6 @@ public interface DelegatedTiFragment { - /** - * @return {@link Activity#isChangingConfigurations()} - */ - boolean isActivityChangingConfigurations(); - - /** - * @return {@link Activity#isFinishing()} - */ - boolean isActivityFinishing(); - /** * @return true when the developer option "Don't keep Activities" is enabled */ @@ -45,6 +35,16 @@ public interface DelegatedTiFragment { */ boolean isFragmentDetached(); + /** + * @return {@link Activity#isChangingConfigurations()} + */ + boolean isHostingActivityChangingConfigurations(); + + /** + * @return {@link Activity#isFinishing()} + */ + boolean isHostingActivityFinishing(); + /** * Post the runnable on the UI queue */ diff --git a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegate.java b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegate.java index 0fb0bb95..cb25366b 100644 --- a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegate.java +++ b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegate.java @@ -1,11 +1,5 @@ package net.grandcentrix.thirtyinch.internal; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.view.LayoutInflater; -import android.view.ViewGroup; - import net.grandcentrix.thirtyinch.BindViewInterceptor; import net.grandcentrix.thirtyinch.Removable; import net.grandcentrix.thirtyinch.TiConfiguration; @@ -17,6 +11,12 @@ import net.grandcentrix.thirtyinch.callonmainthread.CallOnMainThreadInterceptor; import net.grandcentrix.thirtyinch.distinctuntilchanged.DistinctUntilChangedInterceptor; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.ViewGroup; + import java.util.List; /** @@ -94,6 +94,13 @@ public void invalidateView() { mViewBinder.invalidateView(); } + @SuppressWarnings("UnusedParameters") + public void onCreateView_beforeSuper(final LayoutInflater inflater, + @Nullable final ViewGroup container, + @Nullable final Bundle savedInstanceState) { + mViewBinder.invalidateView(); + } + public void onCreate_afterSuper(final Bundle savedInstanceState) { if (mPresenter == null && savedInstanceState != null) { // recover with Savior @@ -140,10 +147,8 @@ public void onCreate_afterSuper(final Bundle savedInstanceState) { } } - @SuppressWarnings("UnusedParameters") - public void onCreateView_beforeSuper(final LayoutInflater inflater, @Nullable final ViewGroup container, - @Nullable final Bundle savedInstanceState) { - mViewBinder.invalidateView(); + public void onDestroyView_beforeSuper() { + mPresenter.detachView(); } public void onDestroy_afterSuper() { @@ -152,7 +157,7 @@ public void onDestroy_afterSuper() { logState(); boolean destroyPresenter = false; - if (mTiFragment.isActivityFinishing()) { + if (mTiFragment.isHostingActivityFinishing()) { // Probably a backpress and not a configuration change // Activity will not be recreated and finally destroyed, also destroyed the presenter destroyPresenter = true; @@ -192,10 +197,6 @@ public void onDestroy_afterSuper() { } } - public void onDestroyView_beforeSuper() { - mPresenter.detachView(); - } - public void onSaveInstanceState_afterSuper(final Bundle outState) { outState.putString(SAVED_STATE_PRESENTER_ID, mPresenterId); } @@ -238,17 +239,20 @@ private boolean isUiPossible() { private void logState() { if (ENABLE_DEBUG_LOGGING) { + TiLog.v(mLogTag.getLoggingTag(), "isChangingConfigurations = " + + mTiFragment.isHostingActivityChangingConfigurations()); + TiLog.v(mLogTag.getLoggingTag(), + "isHostingActivityFinishing = " + mTiFragment.isHostingActivityFinishing()); TiLog.v(mLogTag.getLoggingTag(), - "isChangingConfigurations = " + mTiFragment.isActivityChangingConfigurations()); + "isAdded = " + mTiFragment.isFragmentAdded()); TiLog.v(mLogTag.getLoggingTag(), - "isActivityFinishing = " + mTiFragment.isActivityFinishing()); - TiLog.v(mLogTag.getLoggingTag(), "isAdded = " + mTiFragment.isFragmentAdded()); - TiLog.v(mLogTag.getLoggingTag(), "isDetached = " + mTiFragment.isFragmentDetached()); + "isDetached = " + mTiFragment.isFragmentDetached()); TiLog.v(mLogTag.getLoggingTag(), "isDontKeepActivitiesEnabled = " + mTiFragment.isDontKeepActivitiesEnabled()); final TiConfiguration config = mPresenter.getConfig(); - TiLog.v(mLogTag.getLoggingTag(), "shouldRetain = " + config.shouldRetainPresenter()); + TiLog.v(mLogTag.getLoggingTag(), + "shouldRetain = " + config.shouldRetainPresenter()); TiLog.v(mLogTag.getLoggingTag(), "useStaticSavior = " + config.useStaticSaviorToRetain()); }