Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce partial impression visibility states #973

Merged
merged 13 commits into from
May 19, 2020

Conversation

mediavrog
Copy link
Contributor

@mediavrog mediavrog commented May 15, 2020

fixes #838

  • introduces PARTIAL_IMPRESSION_VISIBLE and PARTIAL_IMPRESSION_INVISIBLE visibility states
  • threshold for partial impression can be set via EpoxyVisibilityTracker#setPartialImpressionThresholdPercentage
  • the threshold will be automatically applied to nested trackers as well if they do not have their own tracker set
  • no event is fired if the threshold cannot be met due to the element being larger than the view port and the set threshold too high to trigger. This is consistent with the behavior of FULL_IMPRESSION_VISIBLE
  • updated :kotlinsample to reflect the addition

Tests

  • I added some tolerance to the AssertHelper:
  • TOLERANCE_PIXELS (value=1) was currently not being tolerant, as it was compared using < instead of <=
  • for visibleHeight and visibleWidth I added a delta to cover the 49% case to test for an element that got VISIBLE, but not PARTIAL_IMPRESSION_VISIBLE

As for tests, since setting the threshold will add a new dimension to the test matrix, I was not sure if I should adjust the whole testsuite to cope for this. Basically all of

testDataAttachedToRecyclerView, testScrollToPosition, testMoveDataDown, testInsertData, testScrollBy, testDeleteData, testMoveDataUp

could be tested once with a threshold set, and once without.
Please let me know if I should cope for this.

Wiki Content Suggestion

Visibility Events

  • Partial Impression Visible: Triggered when the percentage of the visible area of a view is
    at least the specified threshold. This threshold can be set via EpoxyVisibilityTracker#setPartialImpressionThresholdPercentage. This event does not fire if the threshold is set to null or if the threshold cannot be met due to the element being larger than the viewport and the set threshold too high.
  • Partial Impression Invisible: Triggered when the percentage of the visible area of a view is
    no longer at least the specified threshold. Also see Partial Impression Visible.

@@ -82,6 +83,8 @@ private static void setTracker(

private boolean onChangedEnabled = true;

private int partialImpressionThresholdPercentage = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

should it default to something, or default to null in which case handlePartialImpressionVisible is not called?

Copy link
Contributor Author

@mediavrog mediavrog May 15, 2020

Choose a reason for hiding this comment

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

Good point. Let's default to null and not call the method as you described.
Only question is if there should be a separate method from setPartialImpressionThresholdPercentage then to disable partial threshold (and internally set to null) or if setPartialImpressionThresholdPercentage should accept a nullable Integer instead of simple int

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess I see that it shortcircuits to doing nothing when set to 0, in which case the listeners are never called. i think this is a little confusing since a threshold of 0 seems like it would always trigger it to be called. maybe use -1 or null instead?

Copy link
Contributor Author

@mediavrog mediavrog May 15, 2020

Choose a reason for hiding this comment

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

The other idea is to just not call handlePartialImpressionVisible in case of 0

Saw your update now. Good point about 0 as a threshold that should always trigger.

Copy link
Contributor Author

@mediavrog mediavrog May 16, 2020

Choose a reason for hiding this comment

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

If using -1 to disable the events, would you prefer a separate method to disable the events then in regards to public API? Or just annotate setPartialImpressionThresholdPercentage's parameter with @IntRange(from = -1, to = 100) (seems a bit odd). I guess null is better in that case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A value of 0 remains a special case, as it would have to fire if at least one pixel of the element is visible on the screen (would act the same as VISIBLE).

Copy link
Contributor Author

@mediavrog mediavrog May 17, 2020

Choose a reason for hiding this comment

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

How about 0b6c6c2

Copy link
Contributor

Choose a reason for hiding this comment

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

How about @IntRange(from = 1, to = 99), possible values would be null or any int from 1 to 99
Btw, if we have 100 we could use the full impression event so 100 is not really needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I would leave this decision to you as the maintainers of the library as it is more about the public API and what is expected here.

for sure the extremes will basically duplicate existing events:

  • 0 acts like VISIBLE
  • 100 acts like FULL_IMPRESSION_VISIBLE (with an added _INVISIBLE state)

I think having the full range from 0 to 100 grew on me. I think of it this way: If other visibility events were to drop out and be removed, this one should still be fully functional and in that case 0 and 100 are valid use cases.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't have a strong opinion on it and since you handle 0 and 100 having @IntRange(from = 0 to = 100) is fine.

@@ -100,6 +103,19 @@ public void setOnChangedEnabled(boolean enabled) {
onChangedEnabled = enabled;
}

/**
* Set the threshold of percentage visible area to identify the partial impression view state.
Copy link
Contributor

@elihart elihart May 15, 2020

Choose a reason for hiding this comment

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

maybe note that the default makes partial impression events never called?

Copy link
Contributor Author

@mediavrog mediavrog May 17, 2020

Choose a reason for hiding this comment

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

adressed also in 0b6c6c2

@mediavrog mediavrog force-pushed the 838-partial-visibility branch from b6d43a5 to 0b6c6c2 Compare May 17, 2020 23:57
@@ -687,6 +882,7 @@ class EpoxyVisibilityTrackerTest {
fun setup() {
Robolectric.setupActivity(Activity::class.java).apply {
setContentView(EpoxyRecyclerView(this).apply {
epoxyVisibilityTracker.setPartialImpressionThresholdPercentage(50)
Copy link
Contributor

Choose a reason for hiding this comment

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

That's nice to update all the tests with a partialImpressionThresholdPercentage value !

I would have let the existing tests as "it is" and added more test to validate the different partialImpressionThresholdPercentage scenarios.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah that's why I asked about the test matrix. If necessary, I can write some code to dupe the cases once with and once without partial imps.

As for tests, since setting the threshold will add a new dimension to the test matrix, I was not sure if I should adjust the whole testsuite to cope for this. Basically all of
testDataAttachedToRecyclerView, testScrollToPosition, testMoveDataDown, testInsertData, testScrollBy, testDeleteData, testMoveDataUp
could be tested once with a threshold set, and once without.

* {@link VisibilityState#PARTIAL_IMPRESSION_VISIBLE} events.
*/
public void setPartialImpressionThresholdPercentage(
@IntRange(from = 0, to = 100) int thresholdPercentage
Copy link
Contributor

Choose a reason for hiding this comment

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

I am wondering if 0 should be considered as null. Is it even a valid threshold for this use case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See this discussion
#973 (comment)

Copy link
Contributor

@eboudrant eboudrant left a comment

Choose a reason for hiding this comment

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

Looks good to me, thanks !

Copy link
Contributor

@elihart elihart left a comment

Choose a reason for hiding this comment

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

great, thanks for this addition :) I'll make a release with it soon

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Handle partial visibility state
3 participants