Skip to content
This repository has been archived by the owner on Mar 16, 2021. It is now read-only.

Add fragment delegate #47

Merged
merged 7 commits into from
Dec 16, 2016
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ public class HelloWorldActivity extends CompositeActivity implements HelloWorldV
}
```


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this line

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Yes you have to extends `CompositeActivity`, but that's the last level of inheritance you'll ever need.

## Versions
Expand Down
Original file line number Diff line number Diff line change
@@ -1,62 +1,53 @@
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;
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.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;

public abstract class TiDialogFragment<P extends TiPresenter<V>, V extends TiView>
extends AppCompatDialogFragment implements TiPresenterProvider<P>, TiLoggingTagProvider,
extends AppCompatDialogFragment
implements DelegatedTiFragment, TiPresenterProvider<P>, TiLoggingTagProvider,
TiViewProvider<V>, InterceptableViewBinder<V> {

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<V> mViewBinder = new PresenterViewBinder<>(this);
private final TiFragmentDelegate<P, V> mDelegate =
new TiFragmentDelegate<>(this, this, this, 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<BindViewInterceptor> getInterceptors(
@NonNull final Filter<BindViewInterceptor> predicate) {
return mViewBinder.getInterceptors(predicate);
return mDelegate.getInterceptors(predicate);
}

@Override
Expand All @@ -65,7 +56,7 @@ public String getLoggingTag() {
}

public P getPresenter() {
return mPresenter;
return mDelegate.getPresenter();
}

/**
Expand All @@ -74,144 +65,83 @@ public P getPresenter() {
*/
@Override
public void invalidateView() {
mViewBinder.invalidateView();
mDelegate.invalidateView();
}

@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
public boolean isActivityChangingConfigurations() {
return getActivity().isChangingConfigurations();
}

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);
}
}
@Override
public boolean isActivityFinishing() {
return getActivity().isFinishing();
}

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();
}
@Override
public boolean isDontKeepActivitiesEnabled() {
return AndroidDeveloperOptions.isDontKeepActivitiesEnabled(getActivity());
}

final TiConfiguration config = mPresenter.getConfig();
if (config.isCallOnMainThreadInterceptorEnabled()) {
addBindViewInterceptor(new CallOnMainThreadInterceptor());
}
@Override
public boolean isFragmentAdded() {
return isAdded();
}

if (config.isDistinctUntilChangedInterceptorEnabled()) {
addBindViewInterceptor(new DistinctUntilChangedInterceptor());
}
@Override
public boolean isFragmentDetached() {
return isDetached();
}

if (config.shouldRetainPresenter()) {
setRetainInstance(true);
}
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mDelegate.onCreate(savedInstanceState);
}

@Nullable
@Override
public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container,
@Nullable final Bundle savedInstanceState) {
mViewBinder.invalidateView();
@Nullable final Bundle savedInstanceState) {
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();
}

@Override
public boolean postToMessageQueue(final Runnable runnable) {
return getActivity().getWindow().getDecorView().post(runnable);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This crashes if no activity isn't attached. Not sure if this can happen, but it's safer to use a Handler.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

true. It works for us because it's only called in onStart(). But sadly this is public API and should always work. I suggest creating a separate PR for this

}

/**
* the default implementation assumes that the fragment is the view and implements the {@link
* TiView} interface. Override this method for a different behaviour.
Expand All @@ -222,11 +152,11 @@ public void onStop() {
public V provideView() {

final Class<?> foundViewInterface = AnnotationUtil
.getInterfaceOfClassExtendingGivenInterface(this.getClass(), TiView.class);
.getInterfaceOfClassExtendingGivenInterface(getClass(), TiView.class);

if (foundViewInterface == null) {
throw new IllegalArgumentException(
"This Fragment doesn't implement a TiView interface. "
"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")) {
Expand All @@ -241,19 +171,19 @@ public V provideView() {
}
}

@Override
public void setFragmentRetainInstance(final boolean retain) {
setRetainInstance(retain);
}

@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();
}
}
Loading