From f0b5a3d5698b533d914b82878f3dd2551527b6aa Mon Sep 17 00:00:00 2001 From: Robert Berghegger Date: Fri, 10 Mar 2017 14:38:32 +0100 Subject: [PATCH 01/80] Adds a TiFragmentDelegateBuilder. --- .../internal/TiFragmentDelegateBuilder.java | 145 ++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegateBuilder.java diff --git a/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegateBuilder.java b/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegateBuilder.java new file mode 100644 index 00000000..08cf62f4 --- /dev/null +++ b/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegateBuilder.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2017 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 net.grandcentrix.thirtyinch.TiPresenter; +import net.grandcentrix.thirtyinch.TiView; + +import android.support.annotation.NonNull; + +import java.util.concurrent.Executor; + +import static org.mockito.Mockito.mock; + +public class TiFragmentDelegateBuilder { + + private boolean mIsAdded; + + private boolean mIsChangingConfigurations = false; + + private boolean mIsDetached; + + private boolean mIsDontKeepActivitiesEnabled = false; + + private boolean mIsFinishing = false; + + private TiPresenter mPresenter; + + private TiPresenterProvider> mPresenterProvider; + + public TiFragmentDelegate, TiView> build() { + TiPresenterProvider> presenterProvider = mPresenterProvider; + if (presenterProvider == null) { + presenterProvider = new TiPresenterProvider>() { + @NonNull + @Override + public TiPresenter providePresenter() { + return mPresenter; + } + }; + } + + return new TiFragmentDelegate<>(new DelegatedTiFragment() { + @Override + public Executor getUiThreadExecutor() { + return new Executor() { + @Override + public void execute(@NonNull final Runnable action) { + action.run(); + } + }; + } + + @Override + public boolean isDontKeepActivitiesEnabled() { + return mIsDontKeepActivitiesEnabled; + } + + @Override + public boolean isFragmentAdded() { + return mIsAdded; + } + + @Override + public boolean isFragmentDetached() { + return mIsDetached; + } + + @Override + public boolean isHostingActivityChangingConfigurations() { + return mIsChangingConfigurations; + } + + @Override + public boolean isHostingActivityFinishing() { + return mIsFinishing; + } + + @Override + public void setFragmentRetainInstance(final boolean retain) { + // nothing to do + } + + }, new TiViewProvider() { + @NonNull + @Override + public TiView provideView() { + return mock(TiView.class); + } + }, presenterProvider, new TiLoggingTagProvider() { + @Override + public String getLoggingTag() { + return "TestLogTag"; + } + }); + } + + public TiFragmentDelegateBuilder setDontKeepActivitiesEnabled(final boolean enabled) { + mIsDontKeepActivitiesEnabled = enabled; + return this; + } + + public TiFragmentDelegateBuilder setIsAdded(boolean isAdded) { + mIsAdded = isAdded; + return this; + } + + public TiFragmentDelegateBuilder setIsChangingConfigurations(final boolean changing) { + mIsChangingConfigurations = changing; + return this; + } + + public TiFragmentDelegateBuilder setIsDetached(boolean isDetached) { + mIsDetached = isDetached; + return this; + } + + public TiFragmentDelegateBuilder setIsFinishing(final boolean finishing) { + mIsFinishing = finishing; + return this; + } + + public TiFragmentDelegateBuilder setPresenter(TiPresenter presenter) { + mPresenter = presenter; + return this; + } + + public TiFragmentDelegateBuilder setPresenterProvider( + TiPresenterProvider> provider) { + mPresenterProvider = provider; + return this; + } +} \ No newline at end of file From d482f019591bb020c64af6f02c7877031d4e8081 Mon Sep 17 00:00:00 2001 From: Robert Berghegger Date: Sun, 12 Mar 2017 16:31:24 +0100 Subject: [PATCH 02/80] Changes the visibility of the SAVED_PRESENTER_ID class variable in the TiFragmentDelegate class. --- .../grandcentrix/thirtyinch/internal/TiFragmentDelegate.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 2924933e..5a728813 100644 --- a/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegate.java +++ b/thirtyinch/src/main/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegate.java @@ -29,6 +29,7 @@ import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.annotation.VisibleForTesting; import android.view.LayoutInflater; import android.view.ViewGroup; @@ -43,7 +44,8 @@ public class TiFragmentDelegate

, V extends TiView> implements InterceptableViewBinder { - private static final String SAVED_STATE_PRESENTER_ID = "presenter_id"; + @VisibleForTesting + static final String SAVED_STATE_PRESENTER_ID = "presenter_id"; /** * enables debug logging during development From b6e0f6744b9099ecc7a83ed3026ab69c052fecb7 Mon Sep 17 00:00:00 2001 From: Robert Berghegger Date: Sun, 12 Mar 2017 16:32:06 +0100 Subject: [PATCH 03/80] Adds a TiFragmentPresenterDestroyTest class with a first test implementation. --- .../TiFragmentPresenterDestroyTest.java | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentPresenterDestroyTest.java diff --git a/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentPresenterDestroyTest.java b/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentPresenterDestroyTest.java new file mode 100644 index 00000000..93764e8f --- /dev/null +++ b/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentPresenterDestroyTest.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2017 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 net.grandcentrix.thirtyinch.TiConfiguration; +import net.grandcentrix.thirtyinch.TiPresenter; +import net.grandcentrix.thirtyinch.TiView; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.view.LayoutInflater; + +import java.util.HashMap; + +import static org.assertj.core.api.Java6Assertions.assertThat; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; + +public class TiFragmentPresenterDestroyTest { + + private class PutInMapAnswer implements Answer { + + final HashMap map = new HashMap<>(); + + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + final Object[] args = invocation.getArguments(); + map.put((String) args[0], (String) args[1]); + return null; + } + } + + private class TestPresenter extends TiPresenter { + + TestPresenter(TiConfiguration config) { + super(config); + } + } + + @Test + public void saviorTrue_retainTrue_dontKeepActivitiesTrue_fragmentAttached_activityFinish() { + + final TestPresenter presenter = new TestPresenter(new TiConfiguration.Builder() + .setUseStaticSaviorToRetain(true) + .setRetainPresenterEnabled(true) + .build()); + + final TiFragmentDelegate, TiView> delegate + = new TiFragmentDelegateBuilder() + .setDontKeepActivitiesEnabled(true) + .setIsAdded(true) + .setIsFinishing(true) + .setIsChangingConfigurations(false) + .setPresenter(presenter) + .build(); + + final Bundle savedState = mock(Bundle.class); + final TiFragmentPresenterDestroyTest.PutInMapAnswer putInMap = putInMap(); + doAnswer(putInMap).when(savedState).putString(anyString(), anyString()); + + delegate.onCreate_afterSuper(null); + + assertThat(delegate.getPresenter().isInitialized()).isTrue(); + + delegate.onCreateView_beforeSuper(mock(LayoutInflater.class), null, null); + + delegate.onStart_afterSuper(); + + delegate.onSaveInstanceState_afterSuper(savedState); + + assertThat(putInMap.map).containsKey(TiFragmentDelegate.SAVED_STATE_PRESENTER_ID); + assertThat(putInMap.map.get(TiFragmentDelegate.SAVED_STATE_PRESENTER_ID)).isNotNull(); + + delegate.onStop_beforeSuper(); + + delegate.onDestroyView_beforeSuper(); + + delegate.onDestroy_afterSuper(); + + assertThat(delegate.getPresenter().isDestroyed()).isTrue(); + assertThat(PresenterSaviorTestHelper.presenterCount()).isEqualTo(0); + } + + @Before + public void setUp() throws Exception { + PresenterSaviorTestHelper.clear(); + } + + @NonNull + private PutInMapAnswer putInMap() { + return new PutInMapAnswer(); + } +} From c41603c3c2f45840ce4c1d7f8c7fd68814c5fdeb Mon Sep 17 00:00:00 2001 From: Pascal Welsch Date: Wed, 15 Mar 2017 13:50:16 +0100 Subject: [PATCH 04/80] Inject savior with fragment delegate builder --- .../internal/TiFragmentDelegateBuilder.java | 10 +++++++++- .../internal/TiFragmentPresenterDestroyTest.java | 15 +++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegateBuilder.java b/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegateBuilder.java index 08cf62f4..be0fd1c8 100644 --- a/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegateBuilder.java +++ b/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegateBuilder.java @@ -40,6 +40,8 @@ public class TiFragmentDelegateBuilder { private TiPresenterProvider> mPresenterProvider; + private TiPresenterSavior mSavior = new MockSavior(); + public TiFragmentDelegate, TiView> build() { TiPresenterProvider> presenterProvider = mPresenterProvider; if (presenterProvider == null) { @@ -104,7 +106,7 @@ public TiView provideView() { public String getLoggingTag() { return "TestLogTag"; } - }); + }, mSavior); } public TiFragmentDelegateBuilder setDontKeepActivitiesEnabled(final boolean enabled) { @@ -142,4 +144,10 @@ public TiFragmentDelegateBuilder setPresenterProvider( mPresenterProvider = provider; return this; } + + + public TiFragmentDelegateBuilder setSavior(final TiPresenterSavior savior) { + mSavior = savior; + return this; + } } \ No newline at end of file diff --git a/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentPresenterDestroyTest.java b/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentPresenterDestroyTest.java index 93764e8f..b19bd6ea 100644 --- a/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentPresenterDestroyTest.java +++ b/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentPresenterDestroyTest.java @@ -19,6 +19,7 @@ import net.grandcentrix.thirtyinch.TiPresenter; import net.grandcentrix.thirtyinch.TiView; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; @@ -56,6 +57,8 @@ private class TestPresenter extends TiPresenter { } } + private MockSavior mSavior; + @Test public void saviorTrue_retainTrue_dontKeepActivitiesTrue_fragmentAttached_activityFinish() { @@ -70,6 +73,7 @@ public void saviorTrue_retainTrue_dontKeepActivitiesTrue_fragmentAttached_activi .setIsAdded(true) .setIsFinishing(true) .setIsChangingConfigurations(false) + .setSavior(mSavior) .setPresenter(presenter) .build(); @@ -79,6 +83,8 @@ public void saviorTrue_retainTrue_dontKeepActivitiesTrue_fragmentAttached_activi delegate.onCreate_afterSuper(null); + assertThat(mSavior.presenterCount()).isEqualTo(1); + assertThat(delegate.getPresenter().isInitialized()).isTrue(); delegate.onCreateView_beforeSuper(mock(LayoutInflater.class), null, null); @@ -97,12 +103,17 @@ public void saviorTrue_retainTrue_dontKeepActivitiesTrue_fragmentAttached_activi delegate.onDestroy_afterSuper(); assertThat(delegate.getPresenter().isDestroyed()).isTrue(); - assertThat(PresenterSaviorTestHelper.presenterCount()).isEqualTo(0); + assertThat(mSavior.presenterCount()).isEqualTo(0); } @Before public void setUp() throws Exception { - PresenterSaviorTestHelper.clear(); + mSavior = new MockSavior(); + } + + @After + public void tearDown() throws Exception { + mSavior.clear(); } @NonNull From 81323af07694c4358a8f6fb55cd94fd5acc852f5 Mon Sep 17 00:00:00 2001 From: Robert Berghegger Date: Thu, 16 Mar 2017 09:44:38 +0100 Subject: [PATCH 05/80] Adds more tests to the TiFragmentPresenterDestroyTest class. --- .../internal/TiFragmentDelegateBuilder.java | 1 - .../TiFragmentPresenterDestroyTest.java | 154 +++++++++++++++++- 2 files changed, 153 insertions(+), 2 deletions(-) diff --git a/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegateBuilder.java b/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegateBuilder.java index be0fd1c8..ed723ff6 100644 --- a/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegateBuilder.java +++ b/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegateBuilder.java @@ -145,7 +145,6 @@ public TiFragmentDelegateBuilder setPresenterProvider( return this; } - public TiFragmentDelegateBuilder setSavior(final TiPresenterSavior savior) { mSavior = savior; return this; diff --git a/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentPresenterDestroyTest.java b/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentPresenterDestroyTest.java index b19bd6ea..014972cc 100644 --- a/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentPresenterDestroyTest.java +++ b/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentPresenterDestroyTest.java @@ -60,7 +60,159 @@ private class TestPresenter extends TiPresenter { private MockSavior mSavior; @Test - public void saviorTrue_retainTrue_dontKeepActivitiesTrue_fragmentAttached_activityFinish() { + public void saviorFalse_retainFalse_dontKeepActivitiesFalse_backStackFalse_activityChangingConfiguration() { + + // Given a Presenter that does not use a static savior and does not retain itself. + final TestPresenter presenter = new TestPresenter(new TiConfiguration.Builder() + .setUseStaticSaviorToRetain(false) + .setRetainPresenterEnabled(false) + .build()); + + // And given a Fragment. + final TiFragmentDelegate, TiView> delegate + = new TiFragmentDelegateBuilder() + .setDontKeepActivitiesEnabled(false) + .setIsAdded(true) + .setIsFinishing(false) + .setIsChangingConfigurations(true) + .setSavior(mSavior) + .setPresenter(presenter) + .build(); + + final Bundle savedState = mock(Bundle.class); + final TiFragmentPresenterDestroyTest.PutInMapAnswer putInMap = putInMap(); + doAnswer(putInMap).when(savedState).putString(anyString(), anyString()); + + // TODO: rberghegger 16.03.17 call onAttach()? + + // When the fragment is added to the activity. + delegate.onCreate_afterSuper(savedState); + + delegate.onCreateView_beforeSuper(mock(LayoutInflater.class), null, null); + + delegate.onStart_afterSuper(); + + // And when the activity is changing configurations. + delegate.onSaveInstanceState_afterSuper(savedState); + + delegate.onStop_beforeSuper(); + + delegate.onDestroyView_beforeSuper(); + + delegate.onDestroy_afterSuper(); + + // TODO: rberghegger 16.03.17 call on detach? + + // TODO: rberghegger 16.03.17 call onAttach()? + + delegate.onCreate_afterSuper(savedState); + + delegate.onCreateView_beforeSuper(mock(LayoutInflater.class), null, null); + + delegate.onStart_afterSuper(); + + // Then assert that the presenter is destroyed and not saved in the savior. + assertThat(delegate.getPresenter().isDestroyed()).isTrue(); + assertThat(mSavior.presenterCount()).isEqualTo(0); + } + + @Test + public void saviorFalse_retainFalse_dontKeepActivitiesFalse_backStackFalse_activityFinishing() { + + // Given a Presenter that does not use a static savior and does not retain itself. + final TestPresenter presenter = new TestPresenter(new TiConfiguration.Builder() + .setUseStaticSaviorToRetain(false) + .setRetainPresenterEnabled(false) + .build()); + + // And given a Fragment. + final TiFragmentDelegate, TiView> delegate + = new TiFragmentDelegateBuilder() + .setDontKeepActivitiesEnabled(false) + .setIsAdded(true) + .setIsFinishing(true) + .setIsChangingConfigurations(false) + .setSavior(mSavior) + .setPresenter(presenter) + .build(); + + final Bundle savedState = mock(Bundle.class); + final TiFragmentPresenterDestroyTest.PutInMapAnswer putInMap = putInMap(); + doAnswer(putInMap).when(savedState).putString(anyString(), anyString()); + + // TODO: rberghegger 16.03.17 call onAttach()? + + // When the fragment is added to the activity. + delegate.onCreate_afterSuper(savedState); + + delegate.onCreateView_beforeSuper(mock(LayoutInflater.class), null, null); + + delegate.onStart_afterSuper(); + + // And when the activity is finishing. + delegate.onStop_beforeSuper(); + + delegate.onDestroyView_beforeSuper(); + + delegate.onDestroy_afterSuper(); + + // TODO: rberghegger 16.03.17 call onDetach()? + + // Then assert that the presenter is destroyed and not saved in the savior. + assertThat(delegate.getPresenter().isDestroyed()).isTrue(); + assertThat(mSavior.presenterCount()).isEqualTo(0); + } + + @Test + public void saviorFalse_retainFalse_dontKeepActivitiesFalse_backStackTrue_activityFinishing() { + + // Given a Presenter that does not use a static savior and does not retain itself. + final TestPresenter presenter = new TestPresenter(new TiConfiguration.Builder() + .setUseStaticSaviorToRetain(false) + .setRetainPresenterEnabled(false) + .build()); + + // And given a Fragment. + final TiFragmentDelegate, TiView> delegate + = new TiFragmentDelegateBuilder() + .setDontKeepActivitiesEnabled(false) + .setIsAdded(true) + .setIsFinishing(true) + .setIsChangingConfigurations(false) + .setSavior(mSavior) + .setPresenter(presenter) + .build(); + + final Bundle savedState = mock(Bundle.class); + final TiFragmentPresenterDestroyTest.PutInMapAnswer putInMap = putInMap(); + doAnswer(putInMap).when(savedState).putString(anyString(), anyString()); + + // TODO: rberghegger 16.03.17 call onAttach()? + + // When the fragment is added to the activity. + delegate.onCreate_afterSuper(savedState); + + delegate.onCreateView_beforeSuper(mock(LayoutInflater.class), null, null); + + delegate.onStart_afterSuper(); + + // And when it is replaced by another Fragment. + delegate.onStop_beforeSuper(); + + delegate.onDestroyView_beforeSuper(); + + // And when the activity is finishing. + delegate.onDestroy_afterSuper(); + + // TODO: rberghegger 16.03.17 call onDetach()? + + // Then assert that the presenter is destroyed and not saved in the savior. + assertThat(delegate.getPresenter().isDestroyed()).isTrue(); + assertThat(mSavior.presenterCount()).isEqualTo(0); + } + + @Test + public void saviorTrue_retainTrue_dontKeepActivitiesTrue_fragmentAttached_activityFinishing() { final TestPresenter presenter = new TestPresenter(new TiConfiguration.Builder() .setUseStaticSaviorToRetain(true) From 0f877760b6977c10db221f3484baeae2a38a3339 Mon Sep 17 00:00:00 2001 From: Robert Berghegger Date: Thu, 16 Mar 2017 10:33:57 +0100 Subject: [PATCH 06/80] Refactors method names in the TiFragmentDelegateBuilder class. --- .../internal/TiFragmentDelegateBuilder.java | 23 ++++++++++--------- .../TiFragmentPresenterDestroyTest.java | 16 ++++++------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegateBuilder.java b/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegateBuilder.java index ed723ff6..7eaa6a92 100644 --- a/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegateBuilder.java +++ b/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentDelegateBuilder.java @@ -28,13 +28,13 @@ public class TiFragmentDelegateBuilder { private boolean mIsAdded; - private boolean mIsChangingConfigurations = false; - private boolean mIsDetached; private boolean mIsDontKeepActivitiesEnabled = false; - private boolean mIsFinishing = false; + private boolean mIsHostingActivityChangingConfiguration = false; + + private boolean mIsHostingActivityFinishing = false; private TiPresenter mPresenter; @@ -82,12 +82,12 @@ public boolean isFragmentDetached() { @Override public boolean isHostingActivityChangingConfigurations() { - return mIsChangingConfigurations; + return mIsHostingActivityChangingConfiguration; } @Override public boolean isHostingActivityFinishing() { - return mIsFinishing; + return mIsHostingActivityFinishing; } @Override @@ -119,18 +119,19 @@ public TiFragmentDelegateBuilder setIsAdded(boolean isAdded) { return this; } - public TiFragmentDelegateBuilder setIsChangingConfigurations(final boolean changing) { - mIsChangingConfigurations = changing; + public TiFragmentDelegateBuilder setIsDetached(boolean isDetached) { + mIsDetached = isDetached; return this; } - public TiFragmentDelegateBuilder setIsDetached(boolean isDetached) { - mIsDetached = isDetached; + public TiFragmentDelegateBuilder setIsHostingActivityChangingConfiguration( + final boolean isChangingConfiguration) { + mIsHostingActivityChangingConfiguration = isChangingConfiguration; return this; } - public TiFragmentDelegateBuilder setIsFinishing(final boolean finishing) { - mIsFinishing = finishing; + public TiFragmentDelegateBuilder setIsHostingActivityFinishing(final boolean isFinishing) { + mIsHostingActivityFinishing = isFinishing; return this; } diff --git a/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentPresenterDestroyTest.java b/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentPresenterDestroyTest.java index 014972cc..abd3d393 100644 --- a/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentPresenterDestroyTest.java +++ b/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentPresenterDestroyTest.java @@ -73,8 +73,8 @@ public void saviorFalse_retainFalse_dontKeepActivitiesFalse_backStackFalse_activ = new TiFragmentDelegateBuilder() .setDontKeepActivitiesEnabled(false) .setIsAdded(true) - .setIsFinishing(false) - .setIsChangingConfigurations(true) + .setIsHostingActivityFinishing(false) + .setIsHostingActivityChangingConfiguration(true) .setSavior(mSavior) .setPresenter(presenter) .build(); @@ -130,8 +130,8 @@ public void saviorFalse_retainFalse_dontKeepActivitiesFalse_backStackFalse_activ = new TiFragmentDelegateBuilder() .setDontKeepActivitiesEnabled(false) .setIsAdded(true) - .setIsFinishing(true) - .setIsChangingConfigurations(false) + .setIsHostingActivityFinishing(true) + .setIsHostingActivityChangingConfiguration(false) .setSavior(mSavior) .setPresenter(presenter) .build(); @@ -177,8 +177,8 @@ public void saviorFalse_retainFalse_dontKeepActivitiesFalse_backStackTrue_activi = new TiFragmentDelegateBuilder() .setDontKeepActivitiesEnabled(false) .setIsAdded(true) - .setIsFinishing(true) - .setIsChangingConfigurations(false) + .setIsHostingActivityFinishing(true) + .setIsHostingActivityChangingConfiguration(false) .setSavior(mSavior) .setPresenter(presenter) .build(); @@ -223,8 +223,8 @@ public void saviorTrue_retainTrue_dontKeepActivitiesTrue_fragmentAttached_activi = new TiFragmentDelegateBuilder() .setDontKeepActivitiesEnabled(true) .setIsAdded(true) - .setIsFinishing(true) - .setIsChangingConfigurations(false) + .setIsHostingActivityFinishing(true) + .setIsHostingActivityChangingConfiguration(false) .setSavior(mSavior) .setPresenter(presenter) .build(); From b1c9b706eb4034ca6f959349b208ad45d7304b78 Mon Sep 17 00:00:00 2001 From: Robert Berghegger Date: Thu, 16 Mar 2017 10:53:00 +0100 Subject: [PATCH 07/80] Changes the back stack test case. --- .../internal/TiFragmentPresenterDestroyTest.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentPresenterDestroyTest.java b/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentPresenterDestroyTest.java index abd3d393..7479d8ef 100644 --- a/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentPresenterDestroyTest.java +++ b/thirtyinch/src/test/java/net/grandcentrix/thirtyinch/internal/TiFragmentPresenterDestroyTest.java @@ -60,7 +60,7 @@ private class TestPresenter extends TiPresenter { private MockSavior mSavior; @Test - public void saviorFalse_retainFalse_dontKeepActivitiesFalse_backStackFalse_activityChangingConfiguration() { + public void saviorFalse_retainFalse_dontKeepActivitiesFalse_activityChangingConfiguration() { // Given a Presenter that does not use a static savior and does not retain itself. final TestPresenter presenter = new TestPresenter(new TiConfiguration.Builder() @@ -117,7 +117,7 @@ public void saviorFalse_retainFalse_dontKeepActivitiesFalse_backStackFalse_activ } @Test - public void saviorFalse_retainFalse_dontKeepActivitiesFalse_backStackFalse_activityFinishing() { + public void saviorFalse_retainFalse_dontKeepActivitiesFalse_activityFinishing() { // Given a Presenter that does not use a static savior and does not retain itself. final TestPresenter presenter = new TestPresenter(new TiConfiguration.Builder() @@ -164,7 +164,7 @@ public void saviorFalse_retainFalse_dontKeepActivitiesFalse_backStackFalse_activ } @Test - public void saviorFalse_retainFalse_dontKeepActivitiesFalse_backStackTrue_activityFinishing() { + public void saviorFalse_retainFalse_dontKeepActivitiesFalse_backStack() { // Given a Presenter that does not use a static savior and does not retain itself. final TestPresenter presenter = new TestPresenter(new TiConfiguration.Builder() @@ -201,13 +201,13 @@ public void saviorFalse_retainFalse_dontKeepActivitiesFalse_backStackTrue_activi delegate.onDestroyView_beforeSuper(); - // And when the activity is finishing. - delegate.onDestroy_afterSuper(); + // And when the back stack is popped. + delegate.onCreateView_beforeSuper(mock(LayoutInflater.class), null, null); - // TODO: rberghegger 16.03.17 call onDetach()? + delegate.onStart_afterSuper(); // Then assert that the presenter is destroyed and not saved in the savior. - assertThat(delegate.getPresenter().isDestroyed()).isTrue(); + assertThat(delegate.getPresenter().isDestroyed()).isFalse(); assertThat(mSavior.presenterCount()).isEqualTo(0); } From 788f4c447fa2cc2352927b9d6374d9a6b03984f9 Mon Sep 17 00:00:00 2001 From: Robert Berghegger Date: Fri, 17 Mar 2017 18:05:40 +0100 Subject: [PATCH 08/80] Adds a fragment lifecycle test to the sample application. --- sample/src/main/AndroidManifest.xml | 2 + .../thirtyinch/sample/HelloWorldActivity.java | 15 ++ .../FragmentLifecycleActivity.java | 96 ++++++++ .../fragmentlifecycle/TestFragment.java | 231 ++++++++++++++++++ .../fragmentlifecycle/TestFragmentA.java | 11 + .../fragmentlifecycle/TestFragmentB.java | 11 + .../layout/activity_fragment_lifecycle.xml | 58 +++++ .../src/main/res/layout/fragment_a_test.xml | 16 ++ .../src/main/res/layout/fragment_b_test.xml | 16 ++ sample/src/main/res/menu/menu_hello_world.xml | 7 + sample/src/main/res/values/strings.xml | 10 + 11 files changed, 473 insertions(+) create mode 100644 sample/src/main/java/net/grandcentrix/thirtyinch/sample/fragmentlifecycle/FragmentLifecycleActivity.java create mode 100644 sample/src/main/java/net/grandcentrix/thirtyinch/sample/fragmentlifecycle/TestFragment.java create mode 100644 sample/src/main/java/net/grandcentrix/thirtyinch/sample/fragmentlifecycle/TestFragmentA.java create mode 100644 sample/src/main/java/net/grandcentrix/thirtyinch/sample/fragmentlifecycle/TestFragmentB.java create mode 100644 sample/src/main/res/layout/activity_fragment_lifecycle.xml create mode 100644 sample/src/main/res/layout/fragment_a_test.xml create mode 100644 sample/src/main/res/layout/fragment_b_test.xml create mode 100644 sample/src/main/res/menu/menu_hello_world.xml diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index 37fda4c0..a09d96f1 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -16,6 +16,8 @@ + + diff --git a/sample/src/main/java/net/grandcentrix/thirtyinch/sample/HelloWorldActivity.java b/sample/src/main/java/net/grandcentrix/thirtyinch/sample/HelloWorldActivity.java index 0566822d..e2f00e89 100644 --- a/sample/src/main/java/net/grandcentrix/thirtyinch/sample/HelloWorldActivity.java +++ b/sample/src/main/java/net/grandcentrix/thirtyinch/sample/HelloWorldActivity.java @@ -19,9 +19,13 @@ import com.jakewharton.rxbinding.view.RxView; import net.grandcentrix.thirtyinch.TiActivity; +import net.grandcentrix.thirtyinch.sample.fragmentlifecycle.FragmentLifecycleActivity; +import android.content.Intent; import android.os.Bundle; import android.support.annotation.NonNull; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.TextView; @@ -42,6 +46,12 @@ public Observable onButtonClicked() { return RxView.clicks(mButton); } + @Override + public boolean onCreateOptionsMenu(final Menu menu) { + getMenuInflater().inflate(R.menu.menu_hello_world, menu); + return true; + } + @NonNull @Override public HelloWorldPresenter providePresenter() { @@ -58,6 +68,11 @@ public void showText(final String text) { mOutput.setText(text); } + public void startFragmentLifecycleTest(MenuItem item) { + final Intent intent = new Intent(this, FragmentLifecycleActivity.class); + startActivity(intent); + } + @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); diff --git a/sample/src/main/java/net/grandcentrix/thirtyinch/sample/fragmentlifecycle/FragmentLifecycleActivity.java b/sample/src/main/java/net/grandcentrix/thirtyinch/sample/fragmentlifecycle/FragmentLifecycleActivity.java new file mode 100644 index 00000000..f3a1f73a --- /dev/null +++ b/sample/src/main/java/net/grandcentrix/thirtyinch/sample/fragmentlifecycle/FragmentLifecycleActivity.java @@ -0,0 +1,96 @@ +package net.grandcentrix.thirtyinch.sample.fragmentlifecycle; + +import net.grandcentrix.thirtyinch.sample.R; + +import android.os.Bundle; +import android.provider.Settings; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.SwitchCompat; +import android.util.Log; +import android.view.View; +import android.widget.TextView; + +import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES; + +public class FragmentLifecycleActivity extends AppCompatActivity { + + private static final String TAG = FragmentLifecycleActivity.class.getSimpleName(); + + private SwitchCompat mSwitchAddToBackStack; + + public void addFragmentA(View view) { + final TestFragmentA fragment = new TestFragmentA(); + Log.v(TAG, "adding FragmentA"); + addFragment(fragment); + } + + public void addFragmentB(View view) { + final TestFragmentB fragment = new TestFragmentB(); + Log.v(TAG, "adding FragmentB"); + addFragment(fragment); + } + + public void finishActivity(View view) { + finish(); + } + + @Override + public void onBackPressed() { + final FragmentManager fragmentManager = getSupportFragmentManager(); + if (fragmentManager.getBackStackEntryCount() > 0) { + fragmentManager.popBackStack(); + } else { + super.onBackPressed(); + } + } + + public void recreateActivity(View view) { + recreate(); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_fragment_lifecycle); + + mSwitchAddToBackStack = (SwitchCompat) findViewById(R.id.switch_add_back_stack); + final TextView textDontKeepActivities = (TextView) findViewById( + R.id.text_dont_keep_activities); + textDontKeepActivities.setText( + isDontKeepActivities() ? R.string.dont_keep_activities_enabled + : R.string.dont_keep_activities_disabled); + } + + private void addFragment(final Fragment fragment) { + final FragmentManager fragmentManager = getSupportFragmentManager(); + final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); + fragmentTransaction.replace(R.id.fragment_placeholder, fragment); + if (isAddToBackStack()) { + Log.v(TAG, "adding transaction to the back stack"); + fragmentTransaction.addToBackStack(null); + } + final int backStackId = fragmentTransaction.commit(); + if (backStackId >= 0) { + Log.v(TAG, "Back stack ID: " + String.valueOf(backStackId)); + } + } + + private boolean isAddToBackStack() { + return mSwitchAddToBackStack.isChecked(); + } + + private boolean isDontKeepActivities() { + // default behaviour + int dontKeepActivities = 0; + try { + dontKeepActivities = Settings.Global + .getInt(getContentResolver(), ALWAYS_FINISH_ACTIVITIES); + } catch (Settings.SettingNotFoundException e) { + e.printStackTrace(); + } + return dontKeepActivities != 0; + } +} diff --git a/sample/src/main/java/net/grandcentrix/thirtyinch/sample/fragmentlifecycle/TestFragment.java b/sample/src/main/java/net/grandcentrix/thirtyinch/sample/fragmentlifecycle/TestFragment.java new file mode 100644 index 00000000..1ba2f048 --- /dev/null +++ b/sample/src/main/java/net/grandcentrix/thirtyinch/sample/fragmentlifecycle/TestFragment.java @@ -0,0 +1,231 @@ +package net.grandcentrix.thirtyinch.sample.fragmentlifecycle; + +import net.grandcentrix.thirtyinch.sample.R; + +import android.content.Context; +import android.content.Intent; +import android.content.res.Configuration; +import android.os.Bundle; +import android.support.annotation.LayoutRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.util.AttributeSet; +import android.util.Log; +import android.view.ContextMenu; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.Animation; +import android.widget.TextView; + +public abstract class TestFragment extends Fragment { + + private final String TAG = this.getClass().getSimpleName() + + "@" + Integer.toHexString(this.hashCode()); + + @Override + public void onActivityCreated(@Nullable final Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + Log.d(getFragmentTag(), "onViewCreated"); + } + + @Override + public void onActivityResult(final int requestCode, final int resultCode, final Intent data) { + super.onActivityResult(requestCode, resultCode, data); + Log.d(getFragmentTag(), "onActivityResult"); + } + + @Override + public void onAttach(final Context context) { + super.onAttach(context); + Log.d(getFragmentTag(), "onAttach"); + } + + @Override + public void onAttachFragment(final Fragment childFragment) { + super.onAttachFragment(childFragment); + Log.d(getFragmentTag(), "onAttachFragment"); + } + + @Override + public void onConfigurationChanged(final Configuration newConfig) { + super.onConfigurationChanged(newConfig); + Log.d(getFragmentTag(), "onConfigurationChanged"); + } + + @Override + public boolean onContextItemSelected(final MenuItem item) { + Log.d(getFragmentTag(), "onContextItemSelected"); + return super.onContextItemSelected(item); + } + + @Override + public void onCreate(@Nullable final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Log.d(getFragmentTag(), "onCreate"); + } + + @Override + public Animation onCreateAnimation(final int transit, final boolean enter, final int nextAnim) { + Log.d(getFragmentTag(), "onCreateAnimation"); + return super.onCreateAnimation(transit, enter, nextAnim); + } + + @Override + public void onCreateContextMenu(final ContextMenu menu, final View v, + final ContextMenu.ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + Log.d(getFragmentTag(), "onCreateContextMenu"); + } + + @Override + public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + Log.d(getFragmentTag(), "onCreateOptionsMenu"); + } + + @Nullable + @Override + public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container, + @Nullable final Bundle savedInstanceState) { + Log.d(getFragmentTag(), "onCreateView"); + return inflater.inflate(getLayoutResId(), container, false); + } + + @Override + public void onDestroy() { + super.onDestroy(); + Log.d(getFragmentTag(), "onDestroy"); + } + + @Override + public void onDestroyOptionsMenu() { + super.onDestroyOptionsMenu(); + Log.d(getFragmentTag(), "onDestroyOptionsMenu"); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + Log.d(getFragmentTag(), "onDestroyView"); + } + + @Override + public void onDetach() { + super.onDetach(); + Log.d(getFragmentTag(), "onDetach"); + } + + @Override + public void onHiddenChanged(final boolean hidden) { + super.onHiddenChanged(hidden); + Log.d(getFragmentTag(), "onHiddenChanged"); + } + + @Override + public void onInflate(final Context context, final AttributeSet attrs, + final Bundle savedInstanceState) { + super.onInflate(context, attrs, savedInstanceState); + Log.d(getFragmentTag(), "onInflate"); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + Log.d(getFragmentTag(), "onLowMemory"); + } + + @Override + public void onMultiWindowModeChanged(final boolean isInMultiWindowMode) { + super.onMultiWindowModeChanged(isInMultiWindowMode); + Log.d(getFragmentTag(), "onMultiWindowModeChanged"); + } + + @Override + public boolean onOptionsItemSelected(final MenuItem item) { + Log.d(getFragmentTag(), "onOptionsItemSelected"); + return super.onOptionsItemSelected(item); + } + + @Override + public void onOptionsMenuClosed(final Menu menu) { + super.onOptionsMenuClosed(menu); + Log.d(getFragmentTag(), "onOptionsMenuClosed"); + } + + @Override + public void onPause() { + super.onPause(); + Log.d(getFragmentTag(), "onPause"); + } + + @Override + public void onPictureInPictureModeChanged(final boolean isInPictureInPictureMode) { + super.onPictureInPictureModeChanged(isInPictureInPictureMode); + Log.d(getFragmentTag(), "onPictureInPictureModeChanged"); + } + + @Override + public void onPrepareOptionsMenu(final Menu menu) { + super.onPrepareOptionsMenu(menu); + Log.d(getFragmentTag(), "onPrepareOptionsMenu"); + } + + @Override + public void onRequestPermissionsResult(final int requestCode, + @NonNull final String[] permissions, + @NonNull final int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + Log.d(getFragmentTag(), "onRequestPermissionsResult"); + } + + @Override + public void onResume() { + super.onResume(); + Log.d(getFragmentTag(), "onResume"); + } + + @Override + public void onSaveInstanceState(final Bundle outState) { + super.onSaveInstanceState(outState); + Log.d(getFragmentTag(), "onSaveInstanceState"); + } + + @Override + public void onStart() { + super.onStart(); + Log.d(getFragmentTag(), "onStart"); + } + + @Override + public void onStop() { + super.onStop(); + Log.d(getFragmentTag(), "onStop"); + } + + @Override + public void onViewCreated(final View view, @Nullable final Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + Log.d(getFragmentTag(), "onViewCreated"); + + final TextView fragmentTag = (TextView) view.findViewById(R.id.sample_text); + fragmentTag.setText(TAG); + } + + @Override + public void onViewStateRestored(@Nullable final Bundle savedInstanceState) { + super.onViewStateRestored(savedInstanceState); + Log.d(getFragmentTag(), "onViewStateRestored"); + } + + @LayoutRes + abstract int getLayoutResId(); + + private String getFragmentTag() { + return TAG; + } +} diff --git a/sample/src/main/java/net/grandcentrix/thirtyinch/sample/fragmentlifecycle/TestFragmentA.java b/sample/src/main/java/net/grandcentrix/thirtyinch/sample/fragmentlifecycle/TestFragmentA.java new file mode 100644 index 00000000..2027fcfa --- /dev/null +++ b/sample/src/main/java/net/grandcentrix/thirtyinch/sample/fragmentlifecycle/TestFragmentA.java @@ -0,0 +1,11 @@ +package net.grandcentrix.thirtyinch.sample.fragmentlifecycle; + +import net.grandcentrix.thirtyinch.sample.R; + +public class TestFragmentA extends TestFragment { + + @Override + int getLayoutResId() { + return R.layout.fragment_a_test; + } +} diff --git a/sample/src/main/java/net/grandcentrix/thirtyinch/sample/fragmentlifecycle/TestFragmentB.java b/sample/src/main/java/net/grandcentrix/thirtyinch/sample/fragmentlifecycle/TestFragmentB.java new file mode 100644 index 00000000..91dcef92 --- /dev/null +++ b/sample/src/main/java/net/grandcentrix/thirtyinch/sample/fragmentlifecycle/TestFragmentB.java @@ -0,0 +1,11 @@ +package net.grandcentrix.thirtyinch.sample.fragmentlifecycle; + +import net.grandcentrix.thirtyinch.sample.R; + +public class TestFragmentB extends TestFragment { + + @Override + int getLayoutResId() { + return R.layout.fragment_b_test; + } +} diff --git a/sample/src/main/res/layout/activity_fragment_lifecycle.xml b/sample/src/main/res/layout/activity_fragment_lifecycle.xml new file mode 100644 index 00000000..68ea701a --- /dev/null +++ b/sample/src/main/res/layout/activity_fragment_lifecycle.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + +