Skip to content

Commit

Permalink
2.6.1: Allow setting scoped services after setup() before `setState…
Browse files Browse the repository at this point in the history
…Changer()`
  • Loading branch information
Gabor Varadi committed May 3, 2021
1 parent 2f2419a commit 0fcd49a
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 12 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Change log

-Simple Stack 2.6.1 (2021-05-03)
--------------------------------

- CHANGE: `Backstack.setScopedServices(ScopedServices)`, `Backstack.setGlobalServices(GlobalServices)`, and `Backstack.setGlobalServices(GlobalServices.Factory)` can now be called after `setup()`, but before `setStateChanger()`.

This allows setting the scoped services on the backstack instance, when using deferred initialization, before the initial state change is run.

-Simple Stack 2.6.0 (2021-03-08)
--------------------------------

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ and then, add the dependency to your module's `build.gradle.kts` (or `build.grad

``` kotlin
// build.gradle.kts
implementation("com.github.Zhuinden:simple-stack:2.6.0")
implementation("com.github.Zhuinden:simple-stack:2.6.1")
implementation("com.github.Zhuinden:simple-stack-extensions:2.2.0")
```

or

``` groovy
// build.gradle
implementation 'com.github.Zhuinden:simple-stack:2.6.0'
implementation 'com.github.Zhuinden:simple-stack:2.6.1'
implementation 'com.github.Zhuinden:simple-stack-extensions:2.2.0'
```

Expand Down Expand Up @@ -227,7 +227,7 @@ See https://github.com/Zhuinden/simple-stack-compose-integration/ for a default

## License

Copyright 2017-2020 Gabor Varadi
Copyright 2017-2021 Gabor Varadi

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
24 changes: 18 additions & 6 deletions simple-stack/src/main/java/com/zhuinden/simplestack/Backstack.java
Original file line number Diff line number Diff line change
Expand Up @@ -244,12 +244,14 @@ public void setStateClearStrategy(@Nonnull StateClearStrategy stateClearStrategy

/**
* Specifies a {@link ScopedServices} to allow handling the creation of scoped services.
* <p>
* Must be called before the initial state change.
*
* @param scopedServices the {@link ScopedServices}.
*/
public void setScopedServices(@Nonnull ScopedServices scopedServices) {
if(core != null) {
throw new IllegalStateException("Scope provider should be set before calling `setup()`");
if(didRunInitialStateChange) {
throw new IllegalStateException("Scope provider should be set before the initial state change!");
}
if(scopedServices == null) {
throw new IllegalArgumentException("The scope provider cannot be null!");
Expand All @@ -260,13 +262,15 @@ public void setScopedServices(@Nonnull ScopedServices scopedServices) {
/**
* Specifies a {@link GlobalServices} that describes the services of the global scope.
*
* Must be called before the initial state change.
*
* Please note that setting a {@link GlobalServices.Factory} overrides this configuration option.
*
* @param globalServices the {@link GlobalServices}.
*/
public void setGlobalServices(@Nonnull GlobalServices globalServices) {
if(core != null) {
throw new IllegalStateException("Global scope services should be set before calling `setup()`");
if(didRunInitialStateChange) {
throw new IllegalStateException("Scope provider should be set before the initial state change!");
}
if(globalServices == null) {
throw new IllegalArgumentException("The global services cannot be null!");
Expand All @@ -277,6 +281,8 @@ public void setGlobalServices(@Nonnull GlobalServices globalServices) {
/**
* Specifies a {@link GlobalServices.Factory} that describes the services of the global scope that are deferred until first creation.
*
* Must be called before the initial state change.
*
* Please note that a strong reference is kept to the factory, and the {@link Backstack} is typically preserved across configuration change.
* It is recommended that it is NOT an anonymous inner class or normal inner class in an Activity,
* because that could cause memory leaks.
Expand All @@ -286,8 +292,8 @@ public void setGlobalServices(@Nonnull GlobalServices globalServices) {
* @param globalServiceFactory the {@link GlobalServices.Factory}.
*/
public void setGlobalServices(@Nonnull GlobalServices.Factory globalServiceFactory) {
if(core != null) {
throw new IllegalStateException("Global scope service factory should be set before calling `setup()`");
if(didRunInitialStateChange) {
throw new IllegalStateException("Scope provider should be set before the initial state change!");
}
if(globalServiceFactory == null) {
throw new IllegalArgumentException("The global service factory cannot be null!");
Expand All @@ -309,6 +315,8 @@ public void setGlobalServices(@Nonnull GlobalServices.Factory globalServiceFacto

private boolean isStateChangerAttached = false; // tracked to ensure enqueue behavior during activation dispatch.

private boolean didRunInitialStateChange = false;

/**
* Setup creates the {@link Backstack} with the specified initial keys.
*
Expand All @@ -331,6 +339,10 @@ public boolean isInitialized() {

private void initializeBackstack(StateChanger stateChanger) {
if(stateChanger != null) {
if(!didRunInitialStateChange) {
didRunInitialStateChange = true;
}

isStateChangerAttached = true;
core.setStateChanger(managedStateChanger);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,19 @@ public void scopedServicesShouldNotBeNull() {
}

@Test
public void scopedServicesShouldBeSetBeforeSetup() {
public void scopedServicesCanBeSetBeforeInitialStateChange() {
Backstack backstack = new Backstack();
backstack.setup(History.of(testKey1));

backstack.setScopedServices(new ServiceProvider());

backstack.setStateChanger(new StateChanger() {
@Override
public void handleStateChange(@Nonnull StateChange stateChange, @Nonnull Callback completionCallback) {
completionCallback.stateChangeComplete();
}
});

try {
backstack.setScopedServices(new ServiceProvider());
Assert.fail();
Expand Down Expand Up @@ -360,8 +370,45 @@ public void handleStateChange(@Nonnull StateChange stateChange, @Nonnull Callbac
StateBundle stateBundle = backstack.toBundle();

//noinspection ConstantConditions
assertThat(stateBundle.getBundle(Backstack.getScopesTag()).getBundle(testKeyWithScope.getScopeTag()).getBundle(SERVICE_TAG).getInt("blah"))
.isEqualTo(5); // backstack.getScopesTag() is internal
assertThat(stateBundle.getBundle(Backstack.getScopesTag()).getBundle(testKeyWithScope.getScopeTag()).getBundle(SERVICE_TAG).getInt("blah")).isEqualTo(5); // backstack.getScopesTag() is internal
}

@Test
public void scopeServicesArePersistedToStateBundleDelayedScopedServicesCall() {
final Backstack backstack = new Backstack();

final Service service = new Service();
TestKeyWithScope testKeyWithScope = new TestKeyWithScope("blah") {
@Override
public void bindServices(ServiceBinder serviceBinder) {
assertThat(serviceBinder.getScopeTag()).isEqualTo(getScopeTag());
serviceBinder.addService(SERVICE_TAG, service);
}

@Nonnull
@Override
public String getScopeTag() {
return "beep";
}
};

backstack.setup(History.of(testKeyWithScope));

backstack.setScopedServices(new ServiceProvider()); // !

backstack.setStateChanger(new StateChanger() {
@Override
public void handleStateChange(@Nonnull StateChange stateChange, @Nonnull Callback completionCallback) {
completionCallback.stateChangeComplete();
}
});

assertThat(backstack.hasService(testKeyWithScope.getScopeTag(), SERVICE_TAG)).isTrue();

StateBundle stateBundle = backstack.toBundle();

//noinspection ConstantConditions
assertThat(stateBundle.getBundle(Backstack.getScopesTag()).getBundle(testKeyWithScope.getScopeTag()).getBundle(SERVICE_TAG).getInt("blah")).isEqualTo(5); // backstack.getScopesTag() is internal
}

@Test
Expand Down

0 comments on commit 0fcd49a

Please sign in to comment.