Skip to content
This repository has been archived by the owner on Jan 4, 2018. It is now read-only.

Commit

Permalink
Merge pull request square#234 from square/ray/history-filter-220
Browse files Browse the repository at this point in the history
Ray/history filter 220
  • Loading branch information
rjrjr authored Apr 12, 2017
2 parents 9f9eed7 + a2b4ab3 commit 9386184
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 8 deletions.
17 changes: 11 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
Change Log
==========

Version 1.0-alpha2 *(2017-??-??)*
----------------------------------

* API change: added Flow#setHistoryFilter to address https://github.com/square/flow/issues/220

Version 1.0-alpha2 *(2016-09-02)*
----------------------------------
Important bug fixes for 1.0 alpha.
Expand All @@ -14,10 +19,10 @@ Version 1.0-alpha *(2016-02-18)*
--------------------------------
Presented for review and feedback. API should still be considered unstable, docs incomplete, and functionality buggy. All of the above should be mostly resolved before beta.

1.0 brings major functional improvements and API changes.
1.0 brings major functional improvements and API changes.

* Activity integration has been rewritten and is much simpler. One line to configure and install; one optional line to handle the back button; one optional line to handle new Intent. Flow handles lifecycle internally.
* Resource management (including shared resources) is now natively supported via TreeKeys-- Path has effectively been absorbed and simplified. Contexts are now managed internally and there's much less nesting of wrappers.
* Resource management (including shared resources) is now natively supported via TreeKeys-- Path has effectively been absorbed and simplified. Contexts are now managed internally and there's much less nesting of wrappers.
* Multiple simultaneous states are now supported via MultiKeys-- Flow now works natively with UIs composed of dialogs, sheets, master-detail views, etc. MultiKeys can be composed of TreeKeys for resource sharing.
* Persistence has been expanded and simplified. You can now save a Bundle along with view state, and Flow takes care of all the lifecycle.
* Nested/queued traversals are much safer and more efficient.
Expand All @@ -39,12 +44,12 @@ Version 0.10 *(2015-05-01)*

Version 0.9 *(2015-04-24)*
------
A large number of breaking changes have been made in the interest of focusing
A large number of breaking changes have been made in the interest of focusing
the library.

* Backstack is now called History and has some new method names.
* The `resetTo`, `goTo`, `replaceTo`, `forward`, and `backward` operations are
all gone. In their place are two simple methods: `set(Object)` and
* The `resetTo`, `goTo`, `replaceTo`, `forward`, and `backward` operations are
all gone. In their place are two simple methods: `set(Object)` and
`set(History, Direction)`.
* `HasParent` and `goUp` are gone. "Up" navigation is left as an exercise to app
authors who need it, at least for the time being.
Expand All @@ -53,7 +58,7 @@ the library.
* Listener is now called Dispatcher, and can be set on a Flow after
construction. Dispatcher gets more information than Listener did.

There are also some new features, and more are coming.
There are also some new features, and more are coming.

* Added a Context service for easily obtaining the Flow.
* Added `FlowDelegate` for easier integration into an Activity.
Expand Down
13 changes: 13 additions & 0 deletions flow/src/main/java/flow/Flow.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ public static void addHistory(@NonNull Intent intent, @NonNull History history,
}

private History history;
private HistoryFilter historyFilter = new NotPersistentHistoryFilter();
private Dispatcher dispatcher;
private PendingTraversal pendingTraversal;
private List<Object> tearDownKeys = new ArrayList<>();
Expand All @@ -117,6 +118,10 @@ public static void addHistory(@NonNull Intent intent, @NonNull History history,
return history;
}

History getFilteredHistory() {
return historyFilter.scrubHistory(getHistory());
}

/**
* Set the dispatcher, may receive an immediate call to {@link Dispatcher#dispatch}. If a {@link
* Traversal Traversal} is currently in progress with a previous Dispatcher, that Traversal will
Expand All @@ -126,6 +131,14 @@ public void setDispatcher(@NonNull Dispatcher dispatcher) {
setDispatcher(dispatcher, false);
}

/**
* Set the {@link HistoryFilter}, responsible for scrubbing history before it is persisted.
* Use this to customize the default behavior described on {@link NotPersistent}.
*/
public void setHistoryFilter(@NonNull HistoryFilter historyFilter) {
this.historyFilter = historyFilter;
}

void setDispatcher(@NonNull Dispatcher dispatcher, final boolean restore) {
this.dispatcher = checkNotNull(dispatcher, "dispatcher");

Expand Down
11 changes: 11 additions & 0 deletions flow/src/main/java/flow/HistoryFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package flow;

import android.support.annotation.NonNull;

/**
* An object to which gets a chance to scrub the current {@link History} before
* it is persisted.
*/
public interface HistoryFilter {
@NonNull History scrubHistory(@NonNull History history);
}
2 changes: 1 addition & 1 deletion flow/src/main/java/flow/InternalLifecycleIntegration.java
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ void onNewIntent(Intent intent) {
}

Bundle bundle = new Bundle();
save(bundle, parceler, flow.getHistory(), keyManager);
save(bundle, parceler, flow.getFilteredHistory(), keyManager);
if (!bundle.isEmpty()) {
outState.putParcelable(INTENT_KEY, bundle);
}
Expand Down
1 change: 1 addition & 0 deletions flow/src/main/java/flow/NotPersistent.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

/**
* Applied to a state object, indicates that it should not be persisted with the history.
* This behavior can be changed via {@link Flow#setHistoryFilter}.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
Expand Down
24 changes: 24 additions & 0 deletions flow/src/main/java/flow/NotPersistentHistoryFilter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package flow;

import android.support.annotation.NonNull;
import java.util.Iterator;

/**
* Default implementation of {@link HistoryFilter}, enforces the contract
* documented on {@link NotPersistent}.
*/
class NotPersistentHistoryFilter implements HistoryFilter {
@NonNull @Override public History scrubHistory(@NonNull History history) {
History.Builder builder = History.emptyBuilder();

final Iterator<Object> keys = history.reverseIterator();
while (keys.hasNext()) {
Object key = keys.next();
if (!key.getClass().isAnnotationPresent(NotPersistent.class)) {
builder.push(key);
}
}

return builder.build();
}
}
50 changes: 49 additions & 1 deletion flow/src/test/java/flow/FlowTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@
import android.support.annotation.NonNull;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;

import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.mockito.MockitoAnnotations.initMocks;
Expand All @@ -38,10 +40,17 @@ static class Dos {
static class Tres {
}

@NotPersistent static class NoPersist extends TestKey {
NoPersist() {
super("NoPersist");
}
}

final TestKey able = new TestKey("Able");
final TestKey baker = new TestKey("Baker");
final TestKey charlie = new TestKey("Charlie");
final TestKey delta = new TestKey("Delta");
final TestKey noPersist = new NoPersist();

@Mock KeyManager keyManager;
History lastStack;
Expand Down Expand Up @@ -70,7 +79,8 @@ void fire() {
TraversalCallback oldCallback = callback;
callback = null;
traversal = null;
oldCallback.onTraversalCompleted();;
oldCallback.onTraversalCompleted();
;
}

void assertIdle() {
Expand Down Expand Up @@ -428,4 +438,42 @@ static class Picky {
// That's good!
}
}

@Test public void defaultHistoryFilter() {
History history =
History.emptyBuilder().pushAll(Arrays.<Object>asList(able, noPersist, charlie)).build();

Flow flow = new Flow(keyManager, history);
flow.setDispatcher(new FlowDispatcher());

List<Object> expected = History.emptyBuilder().pushAll(asList(able, charlie)).build().asList();
assertThat(flow.getFilteredHistory().asList()).isEqualTo(expected);
}

@Test public void customHistoryFilter() {
History history =
History.emptyBuilder().pushAll(Arrays.<Object>asList(able, noPersist, charlie)).build();

Flow flow = new Flow(keyManager, history);
flow.setDispatcher(new FlowDispatcher());
flow.setHistoryFilter(new HistoryFilter() {
@NonNull @Override public History scrubHistory(@NonNull History history) {
History.Builder builder = History.emptyBuilder();

final Iterator<Object> keys = history.reverseIterator();
while (keys.hasNext()) {
Object key = keys.next();
if (!key.equals(able)) {
builder.push(key);
}
}

return builder.build();
}
});

List<Object> expected =
History.emptyBuilder().pushAll(asList(noPersist, charlie)).build().asList();
assertThat(flow.getFilteredHistory().asList()).isEqualTo(expected);
}
}

0 comments on commit 9386184

Please sign in to comment.