Skip to content

Commit

Permalink
Expose RecyclerBinder's Commit Policy through DynamicConfig
Browse files Browse the repository at this point in the history
Summary:
# Problem

Today we don't have the ability to modify any configurations we set to the `RecyclerBinder` once they have been initialized. We don't want to allow all the configurations to be mutable, but `rangeRatio` and `commitPolicy` should be allowed to change throughout a production's interaction.

# Solution
Expose the configurations we allow to change through `DynamicConfig`. Today I'm only going to allow `commitPolicy` to be modified.

I recommend that the configuration changed to be exposed through `RecyclerCollectionComponent` through `Trigger`.

Reviewed By: astreet

Differential Revision: D16674728

fbshipit-source-id: 89277591adddcaae9dc23fcbf905c3e069410fd9
  • Loading branch information
Desmond Ng authored and facebook-github-bot committed Aug 9, 2019
1 parent 1171ae0 commit ac513f3
Show file tree
Hide file tree
Showing 9 changed files with 130 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1125,5 +1125,8 @@ public void requestFocusWithOffset(int index, int offset) {}
public boolean supportsBackgroundChangeSets() {
return mSupportsBackgroundChangeSets;
}

@Override
public void changeConfig(DynamicConfig dynamicConfig) {}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2526,6 +2526,52 @@ public void testInsertAsync_AsyncMode() {
assertThat(holder.isTreeValid()).isTrue();
}

@Test
public void testInsertAsync_PolicyChangeMode() {
final RecyclerBinder recyclerBinder =
new RecyclerBinder.Builder().rangeRatio(RANGE_RATIO).build(mComponentContext);
recyclerBinder.setCommitPolicy(RecyclerBinder.CommitPolicy.LAYOUT_BEFORE_INSERT);

final Component component =
TestDrawableComponent.create(mComponentContext).widthPx(100).heightPx(100).build();
final ComponentRenderInfo renderInfo =
ComponentRenderInfo.create().component(component).build();

recyclerBinder.measure(
new Size(), makeSizeSpec(1000, EXACTLY), makeSizeSpec(1000, EXACTLY), null);
recyclerBinder.insertItemAtAsync(0, renderInfo);

final Component secondComponent =
TestDrawableComponent.create(mComponentContext).widthPx(100).heightPx(100).build();
final ComponentRenderInfo secondRenderInfo =
ComponentRenderInfo.create().component(secondComponent).build();
recyclerBinder.setCommitPolicy(RecyclerBinder.CommitPolicy.IMMEDIATE);
recyclerBinder.insertItemAtAsync(1, secondRenderInfo);

recyclerBinder.notifyChangeSetCompleteAsync(true, NO_OP_CHANGE_SET_COMPLETE_CALLBACK);

assertThat(recyclerBinder.getItemCount()).isEqualTo(0);
assertThat(recyclerBinder.getRangeCalculationResult()).isNull();

mLayoutThreadShadowLooper.runToEndOfTasks();

assertThat(recyclerBinder.getItemCount()).isEqualTo(2);
assertThat(recyclerBinder.getRangeCalculationResult()).isNotNull();

final ComponentTreeHolder holder = recyclerBinder.getComponentTreeHolderAt(0);
assertThat(holder.getRenderInfo().getComponent()).isEqualTo(component);
assertThat(holder.hasCompletedLatestLayout()).isTrue();
assertThat(holder.isTreeValid()).isTrue();

final Component thirdComponent =
TestDrawableComponent.create(mComponentContext).widthPx(100).heightPx(100).build();
final ComponentRenderInfo thirdRenderInfo =
ComponentRenderInfo.create().component(thirdComponent).build();
recyclerBinder.insertItemAtAsync(2, thirdRenderInfo);
recyclerBinder.notifyChangeSetCompleteAsync(true, NO_OP_CHANGE_SET_COMPLETE_CALLBACK);
assertThat(recyclerBinder.getItemCount()).isEqualTo(3);
}

@Test
public void testMultipleInsertAsyncs_AsyncMode() {
final RecyclerBinder recyclerBinder =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,11 @@ public boolean supportsBackgroundChangeSets() {
return mTarget.supportsBackgroundChangeSets();
}

@Override
public void changeConfig(DynamicConfig dynamicConfig) {
mTarget.changeConfig(dynamicConfig);
}

private void maybeLogRequestFocusWithOffset(int index, int offset) {
if (ENABLE_LOGGER && mComponentInfoSparseArray.size() != 0) {
mSectionsDebugLogger.logRequestFocusWithOffset(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import com.facebook.litho.sections.config.SectionsConfiguration;
import com.facebook.litho.sections.logger.SectionsDebugLogger;
import com.facebook.litho.widget.ChangeSetCompleteCallback;
import com.facebook.litho.widget.RecyclerBinder.CommitPolicy;
import com.facebook.litho.widget.RenderInfo;
import com.facebook.litho.widget.SectionsDebug;
import com.facebook.litho.widget.SmoothScrollAlignmentType;
Expand Down Expand Up @@ -142,6 +143,18 @@ void notifyChangeSetComplete(
* @return whether this target supports applying change sets from a background thread.
*/
boolean supportsBackgroundChangeSets();

/** Notify this target that a new set of configurations is applied. */
void changeConfig(DynamicConfig dynamicConfig);

class DynamicConfig {

public final @CommitPolicy int mChangeSetsCommitPolicy;

public DynamicConfig(@CommitPolicy int changeSetsCommitPolicy) {
mChangeSetsCommitPolicy = changeSetsCommitPolicy;
}
}
}

private static final String EMPTY_STRING = "";
Expand Down Expand Up @@ -426,6 +439,20 @@ public void setLoadEventsHandler(LoadEventsHandler loadEventsHandler) {
mLoadEventsHandler = loadEventsHandler;
}

/**
* Set a new set of configurations to the {@link Target}. Only those allowed to be modified will
* be included in {@link Target.DynamicConfig}.
*/
public void setTargetConfig(Target.DynamicConfig dynamicConfig) {
if (mUseBackgroundChangeSets) {
synchronized (this) {
// applyChangeSetsToTargetUnchecked() in background will be computed in a synchronized
// block, and the config is only updated after the ongoing computation is completed.
mTarget.changeConfig(dynamicConfig);
}
}
}

private void refreshRecursive(Section section) {
section.refresh(section.getScopedContext());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
import com.facebook.litho.widget.PTRRefreshEvent;
import com.facebook.litho.widget.Recycler;
import com.facebook.litho.widget.RecyclerBinder;
import com.facebook.litho.widget.RecyclerBinder.CommitPolicy;
import com.facebook.litho.widget.RecyclerEventsController;
import com.facebook.litho.widget.StickyHeaderControllerFactory;
import com.facebook.litho.widget.ViewportInfo;
Expand Down Expand Up @@ -407,6 +408,14 @@ static void onScroll(
sectionTree.requestFocusOnRoot(position);
}

@OnTrigger(RecyclerDynamicConfigEvent.class)
static void onRecyclerConfigChanged(
ComponentContext c,
@FromTrigger @CommitPolicy int commitPolicy,
@State SectionTree sectionTree) {
sectionTree.setTargetConfig(new SectionTree.Target.DynamicConfig(commitPolicy));
}

@OnDetached
static void onDetached(ComponentContext c, @State Binder<RecyclerView> binder) {
binder.detach();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2019-present Facebook, Inc.
*
* 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 com.facebook.litho.sections.widget;

import com.facebook.litho.annotations.Event;
import com.facebook.litho.widget.RecyclerBinder.CommitPolicy;

/**
* An event that is triggered when a new set of configurations, which are allowed to be dynamic, is
* applied to {@link com.facebook.litho.sections.SectionTree.Target}.
*/
@Event
public class RecyclerDynamicConfigEvent {
public @CommitPolicy int commitPolicy;
}
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,11 @@ public boolean supportsBackgroundChangeSets() {
return mUseBackgroundChangeSets;
}

@Override
public void changeConfig(DynamicConfig dynamicConfig) {
mRecyclerBinder.setCommitPolicy(dynamicConfig.mChangeSetsCommitPolicy);
}

@Override
public void detach() {
mRecyclerBinder.detach();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,9 @@ public boolean supportsBackgroundChangeSets() {
return false;
}

@Override
public void changeConfig(DynamicConfig dynamicConfig) {}

public void clear() {
mOperations.clear();
mNumChanges = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import androidx.annotation.AnyThread;
import androidx.annotation.IntDef;
import androidx.annotation.UiThread;
import androidx.annotation.VisibleForTesting;
Expand Down Expand Up @@ -3273,8 +3274,8 @@ private int getActualChildrenHeightSpec(final ComponentTreeHolder treeHolder) {
return mLayoutInfo.getChildHeightSpec(mLastHeightSpec, treeHolder.getRenderInfo());
}

@VisibleForTesting
void setCommitPolicy(@CommitPolicy int commitPolicy) {
@AnyThread
public void setCommitPolicy(@CommitPolicy int commitPolicy) {
mCommitPolicy = commitPolicy;
}

Expand Down Expand Up @@ -3310,7 +3311,7 @@ private AsyncInsertOperation createAsyncInsertOperation(int position, RenderInfo
*/
@IntDef({CommitPolicy.IMMEDIATE, CommitPolicy.LAYOUT_BEFORE_INSERT})
@Retention(RetentionPolicy.SOURCE)
@interface CommitPolicy {
public @interface CommitPolicy {
int IMMEDIATE = 0;
int LAYOUT_BEFORE_INSERT = 1;
}
Expand Down

0 comments on commit ac513f3

Please sign in to comment.