content: package com.cy.tablayoutniubility;
  
  import android.os.Bundle;
  import android.os.Handler;
  import android.os.Looper;
  import android.os.Parcelable;
  import android.view.View;
  import android.view.ViewGroup;
  import android.view.ViewParent;
  import android.widget.FrameLayout;
  
  import androidx.annotation.CallSuper;
  import androidx.annotation.NonNull;
  import androidx.annotation.Nullable;
  import androidx.collection.ArraySet;
  import androidx.collection.LongSparseArray;
  import androidx.core.view.ViewCompat;
  import androidx.fragment.app.Fragment;
  import androidx.fragment.app.FragmentActivity;
  import androidx.fragment.app.FragmentManager;
  import androidx.fragment.app.FragmentTransaction;
  import androidx.lifecycle.Lifecycle;
  import androidx.lifecycle.LifecycleEventObserver;
  import androidx.lifecycle.LifecycleOwner;
  import androidx.recyclerview.widget.RecyclerView;
  import androidx.viewpager2.adapter.StatefulAdapter;
  import androidx.viewpager2.widget.ViewPager2;
  
  import java.util.Set;
  
  import static androidx.core.util.Preconditions.checkArgument;
  import static androidx.lifecycle.Lifecycle.State.RESUMED;
  import static androidx.lifecycle.Lifecycle.State.STARTED;
  import static androidx.recyclerview.widget.RecyclerView.NO_ID;
  
  /**
   * @Description:
   * @Author: cy
   * @CreateDate: 2020/10/7 21:57
   * @UpdateUser:
   * @UpdateDate: 2020/10/7 21:57
   * @UpdateRemark:
   * @Version: 1.0
   */
  public abstract class CyFragStatePageAdapterVp2 extends
          RecyclerView.Adapter<CyFragmentViewHolderVp2> implements StatefulAdapter {
      // State saving config
      private static final String KEY_PREFIX_FRAGMENT = "f#";
      private static final String KEY_PREFIX_STATE = "s#";
  
      // Fragment GC config
      private static final long GRACE_WINDOW_TIME_MS = 10_000; // 10 seconds
  
      @SuppressWarnings("WeakerAccess") // to avoid creation of a synthetic accessor
      final Lifecycle mLifecycle;
      @SuppressWarnings("WeakerAccess") // to avoid creation of a synthetic accessor
      final FragmentManager mFragmentManager;
  
      // Fragment bookkeeping
      @SuppressWarnings("WeakerAccess") // to avoid creation of a synthetic accessor
      final LongSparseArray<Fragment> mFragments = new LongSparseArray<>();
      private final LongSparseArray<Fragment.SavedState> mSavedStates = new LongSparseArray<>();
      private final LongSparseArray<Integer> mItemIdToViewHolder = new LongSparseArray<>();
  
      private CyFragStatePageAdapterVp2.FragmentMaxLifecycleEnforcer mFragmentMaxLifecycleEnforcer;
  
      // Fragment GC
      @SuppressWarnings("WeakerAccess") // to avoid creation of a synthetic accessor
              boolean mIsInGracePeriod = false;
      private boolean mHasStaleFragments = false;
  
      public CyFragStatePageAdapterVp2(@NonNull FragmentActivity fragmentActivity) {
          this(fragmentActivity.getSupportFragmentManager(), fragmentActivity.getLifecycle());
      }
  
      public CyFragStatePageAdapterVp2(@NonNull Fragment fragment) {
          this(fragment.getChildFragmentManager(), fragment.getLifecycle());
      }
  
      public CyFragStatePageAdapterVp2(@NonNull FragmentManager fragmentManager,
                                       @NonNull Lifecycle lifecycle) {
          mFragmentManager = fragmentManager;
          mLifecycle = lifecycle;
          super.setHasStableIds(true);
      }
  
      @CallSuper
      @Override
      public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
          checkArgument(mFragmentMaxLifecycleEnforcer == null);
          mFragmentMaxLifecycleEnforcer = new CyFragStatePageAdapterVp2.FragmentMaxLifecycleEnforcer();
          mFragmentMaxLifecycleEnforcer.register(recyclerView);
      }
  
      @CallSuper
      @Override
      public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
          mFragmentMaxLifecycleEnforcer.unregister(recyclerView);
          mFragmentMaxLifecycleEnforcer = null;
      }
  
      /**
       * Provide a new Fragment associated with the specified position.
       * <p>
       * The adapter will be responsible for the Fragment lifecycle:
       * <ul>
       *     <li>The Fragment will be used to display an item.</li>
       *     <li>The Fragment will be destroyed when it gets too far from the viewport, and its state
       *     will be saved. When the item is close to the viewport again, a new Fragment will be
       *     requested, and a previously saved state will be used to initialize it.
       * </ul>
       * @see ViewPager2#setOffscreenPageLimit
       */
      public abstract @NonNull Fragment createFragment(int position);
  
      @NonNull
      @Override
      public final CyFragmentViewHolderVp2 onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
          return CyFragmentViewHolderVp2.create(parent);
      }
  
      @Override
      public final void onBindViewHolder(final @NonNull CyFragmentViewHolderVp2 holder, int position) {
          final long itemId = holder.getItemId();
          final int viewHolderId = holder.getContainer().getId();
          final Long boundItemId = itemForViewHolder(viewHolderId); // item currently bound to the VH
          if (boundItemId != null && boundItemId != itemId) {
              removeFragment(boundItemId);
              mItemIdToViewHolder.remove(boundItemId);
          }
  
          mItemIdToViewHolder.put(itemId, viewHolderId); // this might overwrite an existing entry
          ensureFragment(position);
  
          /** Special case when {@link RecyclerView} decides to keep the {@link container}
           * attached to the window, but not to the view hierarchy (i.e. parent is null) */
          final FrameLayout container = holder.getContainer();
          if (ViewCompat.isAttachedToWindow(container)) {
              if (container.getParent() != null) {
                  throw new IllegalStateException("Design assumption violated.");
              }
              container.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
                  @Override
                  public void onLayoutChange(View v, int left, int top, int right, int bottom,
                                             int oldLeft, int oldTop, int oldRight, int oldBottom) {
                      if (container.getParent() != null) {
                          container.removeOnLayoutChangeListener(this);
                          placeFragmentInViewHolder(holder);
                      }
                  }
              });
          }
  
          gcFragments();
      }
  
      @SuppressWarnings("WeakerAccess") // to avoid creation of a synthetic accessor
      void gcFragments() {
          if (!mHasStaleFragments || shouldDelayFragmentTransactions()) {
              return;
          }
  
          // Remove Fragments for items that are no longer part of the data-set
          Set<Long> toRemove = new ArraySet<>();
          for (int ix = 0; ix < mFragments.size(); ix++) {
              long itemId = mFragments.keyAt(ix);
              if (!containsItem(itemId)) {
                  toRemove.add(itemId);
                  mItemIdToViewHolder.remove(itemId); // in case they're still bound
              }
          }
  
          // Remove Fragments that are not bound anywhere -- pending a grace period
          if (!mIsInGracePeriod) {
              mHasStaleFragments = false; // we've executed all GC checks
  
              for (int ix = 0; ix < mFragments.size(); ix++) {
                  long itemId = mFragments.keyAt(ix);
                  if (!isFragmentViewBound(itemId)) {
                      toRemove.add(itemId);
                  }
              }
          }
  
          for (Long itemId : toRemove) {
              removeFragment(itemId);
          }
      }
  
      private boolean isFragmentViewBound(long itemId) {
          if (mItemIdToViewHolder.containsKey(itemId)) {
              return true;
          }
  
          Fragment fragment = mFragments.get(itemId);
          if (fragment == null) {
              return false;
          }
  
          View view = fragment.getView();
          if (view == null) {
              return false;
          }
  
          return view.getParent() != null;
      }
  
      private Long itemForViewHolder(int viewHolderId) {
          Long boundItemId = null;
          for (int ix = 0; ix < mItemIdToViewHolder.size(); ix++) {
              if (mItemIdToViewHolder.valueAt(ix) == viewHolderId) {
                  if (boundItemId != null) {
                      throw new IllegalStateException("Design assumption violated: "
                              + "a ViewHolder can only be bound to one item at a time.");
                  }
                  boundItemId = mItemIdToViewHolder.keyAt(ix);
              }
          }
          return boundItemId;
      }
  
      private void ensureFragment(int position) {
          long itemId = getItemId(position);
  //        if (!mFragments.containsKey(itemId)) {
              // TODO(133419201): check if a Fragment provided here is a new Fragment
              Fragment newFragment = createFragment(position);
              newFragment.setInitialSavedState(mSavedStates.get(itemId));
              mFragments.put(itemId, newFragment);
  //        }
      }
  
      @Override
      public final void onViewAttachedToWindow(@NonNull final CyFragmentViewHolderVp2 holder) {
          placeFragmentInViewHolder(holder);
          gcFragments();
      }
  
      /**
       * @param holder that has been bound to a Fragment in the {@link #onBindViewHolder} stage.
       */
      @SuppressWarnings("WeakerAccess") // to avoid creation of a synthetic accessor
      void placeFragmentInViewHolder(@NonNull final CyFragmentViewHolderVp2 holder) {
          Fragment fragment = mFragments.get(holder.getItemId());
          if (fragment == null) {
              throw new IllegalStateException("Design assumption violated.");
          }
          FrameLayout container = holder.getContainer();
          View view = fragment.getView();
  
          /*
          possible states:
          - fragment: { added, notAdded }
          - view: { created, notCreated }
          - view: { attached, notAttached }
  
          combinations:
          - { f:added, v:created, v:attached } -> check if attached to the right container
          - { f:added, v:created, v:notAttached} -> attach view to container
          - { f:added, v:notCreated, v:attached } -> impossible
          - { f:added, v:notCreated, v:notAttached} -> schedule callback for when created
          - { f:notAdded, v:created, v:attached } -> illegal state
          - { f:notAdded, v:created, v:notAttached } -> illegal state
          - { f:notAdded, v:notCreated, v:attached } -> impossible
          - { f:notAdded, v:notCreated, v:notAttached } -> add, create, attach
           */
  
          // { f:notAdded, v:created, v:attached } -> illegal state
          // { f:notAdded, v:created, v:notAttached } -> illegal state
          if (!fragment.isAdded() && view != null) {
              throw new IllegalStateException("Design assumption violated.");
          }
  
          // { f:added, v:notCreated, v:notAttached} -> schedule callback for when created
          if (fragment.isAdded() && view == null) {
              scheduleViewAttach(fragment, container);
              return;
          }
  
          // { f:added, v:created, v:attached } -> check if attached to the right container
          if (fragment.isAdded() && view.getParent() != null) {
              if (view.getParent() != container) {
                  addViewToContainer(view, container);
              }
              return;
          }
  
          // { f:added, v:created, v:notAttached} -> attach view to container
          if (fragment.isAdded()) {
              addViewToContainer(view, container);
              return;
          }
  
          // { f:notAdded, v:notCreated, v:notAttached } -> add, create, attach
          if (!shouldDelayFragmentTransactions()) {
              scheduleViewAttach(fragment, container);
              mFragmentManager.beginTransaction()
                      .add(fragment, "f" + holder.getItemId())
                      .setMaxLifecycle(fragment, STARTED)
                      .commitNow();
              mFragmentMaxLifecycleEnforcer.updateFragmentMaxLifecycle(false);
          } else {
              if (mFragmentManager.isDestroyed()) {
                  return; // nothing we can do
              }
              mLifecycle.addObserver(new LifecycleEventObserver() {
                  @Override
                  public void onStateChanged(@NonNull LifecycleOwner source,
                                             @NonNull Lifecycle.Event event) {
                      if (shouldDelayFragmentTransactions()) {
                          return;
                      }
                      source.getLifecycle().removeObserver(this);
                      if (ViewCompat.isAttachedToWindow(holder.getContainer())) {
                          placeFragmentInViewHolder(holder);
                      }
                  }
              });
          }
      }
  
      private void scheduleViewAttach(final Fragment fragment, @NonNull final FrameLayout container) {
          // After a config change, Fragments that were in FragmentManager will be recreated. Since
          // ViewHolder container ids are dynamically generated, we opted to manually handle
          // attaching Fragment views to containers. For consistency, we use the same mechanism for
          // all Fragment views.
          mFragmentManager.registerFragmentLifecycleCallbacks(
                  new FragmentManager.FragmentLifecycleCallbacks() {
                      // TODO(b/141956012): Suppressed during upgrade to AGP 3.6.
                      @SuppressWarnings("ReferenceEquality")
                      @Override
                      public void onFragmentViewCreated(@NonNull FragmentManager fm,
                                                        @NonNull Fragment f, @NonNull View v,
                                                        @Nullable Bundle savedInstanceState) {
                          if (f == fragment) {
                              fm.unregisterFragmentLifecycleCallbacks(this);
                              addViewToContainer(v, container);
                          }
                      }
                  }, false);
      }
  
      @SuppressWarnings("WeakerAccess") // to avoid creation of a synthetic accessor
      void addViewToContainer(@NonNull View v, @NonNull FrameLayout container) {
          if (container.getChildCount() > 1) {
              throw new IllegalStateException("Design assumption violated.");
          }
  
          if (v.getParent() == container) {
              return;
          }
  
          if (container.getChildCount() > 0) {
              container.removeAllViews();
          }
  
          if (v.getParent() != null) {
              ((ViewGroup) v.getParent()).removeView(v);
          }
  
          container.addView(v);
      }
  
      @Override
      public final void onViewRecycled(@NonNull CyFragmentViewHolderVp2 holder) {
  
          final int viewHolderId = holder.getContainer().getId();
          final Long boundItemId = itemForViewHolder(viewHolderId); // item currently bound to the VH
          if (boundItemId != null) {
              removeFragment(boundItemId);
              mItemIdToViewHolder.remove(boundItemId);
          }
      }
  
      @Override
      public final boolean onFailedToRecycleView(@NonNull CyFragmentViewHolderVp2 holder) {
          /*
           This happens when a ViewHolder is in a transient state (e.g. during an
           animation).
  
           Our ViewHolders are effectively just FrameLayout instances in which we put Fragment
           Views, so it's safe to force recycle them. This is because:
           - FrameLayout instances are not to be directly manipulated, so no animations are
           expected to be running directly on them.
           - Fragment Views are not reused between position (one Fragment = one page). Animation
           running in one of the Fragment Views won't affect another Fragment View.
           - If a user chooses to violate these assumptions, they are also in the position to
           correct the state in their code.
          */
          return true;
      }
  
      private void removeFragment(long itemId) {
          Fragment fragment = mFragments.get(itemId);
  
          if (fragment == null) {
              return;
          }
  
          if (fragment.getView() != null) {
              ViewParent viewParent = fragment.getView().getParent();
              if (viewParent != null) {
                  ((FrameLayout) viewParent).removeAllViews();
              }
          }
  
          if (!containsItem(itemId)) {
              mSavedStates.remove(itemId);
          }
  
          if (!fragment.isAdded()) {
              mFragments.remove(itemId);
              return;
          }
  
          if (shouldDelayFragmentTransactions()) {
              mHasStaleFragments = true;
              return;
          }
  
          if (fragment.isAdded() && containsItem(itemId)) {
              mSavedStates.put(itemId, mFragmentManager.saveFragmentInstanceState(fragment));
          }
          mFragmentManager.beginTransaction().remove(fragment).commitNow();
          mFragments.remove(itemId);
      }
  
      @SuppressWarnings("WeakerAccess") // to avoid creation of a synthetic accessor
      boolean shouldDelayFragmentTransactions() {
          return mFragmentManager.isStateSaved();
      }
  
      /**
       * Default implementation works for collections that don't add, move, remove items.
       * <p>
       * TODO(b/122670460): add lint rule
       * When overriding, also override {@link #containsItem(long)}.
       * <p>
       * If the item is not a part of the collection, return {@link RecyclerView#NO_ID}.
       *
       * @param position Adapter position
       * @return stable item id {@link RecyclerView.Adapter#hasStableIds()}
       */
      @Override
      public long getItemId(int position) {
          return position;
      }
  
      /**
       * Default implementation works for collections that don't add, move, remove items.
       * <p>
       * TODO(b/122670460): add lint rule
       * When overriding, also override {@link #getItemId(int)}
       */
      public boolean containsItem(long itemId) {
          return itemId >= 0 && itemId < getItemCount();
      }
  
      @Override
      public final void setHasStableIds(boolean hasStableIds) {
          throw new UnsupportedOperationException(
                  "Stable Ids are required for the adapter to function properly, and the adapter "
                          + "takes care of setting the flag.");
      }
  
      @Override
      public final @NonNull
      Parcelable saveState() {
          /** TODO(b/122670461): use custom {@link Parcelable} instead of Bundle to save space */
          Bundle savedState = new Bundle(mFragments.size() + mSavedStates.size());
  
          /** save references to active fragments */
          for (int ix = 0; ix < mFragments.size(); ix++) {
              long itemId = mFragments.keyAt(ix);
              Fragment fragment = mFragments.get(itemId);
              if (fragment != null && fragment.isAdded()) {
                  String key = createKey(KEY_PREFIX_FRAGMENT, itemId);
                  mFragmentManager.putFragment(savedState, key, fragment);
              }
          }
  
          /** Write {@link mSavedStates) into a {@link Parcelable} */
          for (int ix = 0; ix < mSavedStates.size(); ix++) {
              long itemId = mSavedStates.keyAt(ix);
              if (containsItem(itemId)) {
                  String key = createKey(KEY_PREFIX_STATE, itemId);
                  savedState.putParcelable(key, mSavedStates.get(itemId));
              }
          }
  
          return savedState;
      }
  
      @Override
      public final void restoreState(@NonNull Parcelable savedState) {
          if (!mSavedStates.isEmpty() || !mFragments.isEmpty()) {
              throw new IllegalStateException(
                      "Expected the adapter to be 'fresh' while restoring state.");
          }
  
          Bundle bundle = (Bundle) savedState;
          if (bundle.getClassLoader() == null) {
              /** TODO(b/133752041): pass the class loader from {@link ViewPager2.SavedState } */
              bundle.setClassLoader(getClass().getClassLoader());
          }
  
          for (String key : bundle.keySet()) {
              if (isValidKey(key, KEY_PREFIX_FRAGMENT)) {
                  long itemId = parseIdFromKey(key, KEY_PREFIX_FRAGMENT);
                  Fragment fragment = mFragmentManager.getFragment(bundle, key);
                  mFragments.put(itemId, fragment);
                  continue;
              }
  
              if (isValidKey(key, KEY_PREFIX_STATE)) {
                  long itemId = parseIdFromKey(key, KEY_PREFIX_STATE);
                  Fragment.SavedState state = bundle.getParcelable(key);
                  if (containsItem(itemId)) {
                      mSavedStates.put(itemId, state);
                  }
                  continue;
              }
  
              throw new IllegalArgumentException("Unexpected key in savedState: " + key);
          }
  
          if (!mFragments.isEmpty()) {
              mHasStaleFragments = true;
              mIsInGracePeriod = true;
              gcFragments();
              scheduleGracePeriodEnd();
          }
      }
  
      private void scheduleGracePeriodEnd() {
          final Handler handler = new Handler(Looper.getMainLooper());
          final Runnable runnable = new Runnable() {
              @Override
              public void run() {
                  mIsInGracePeriod = false;
                  gcFragments(); // good opportunity to GC
              }
          };
  
          mLifecycle.addObserver(new LifecycleEventObserver() {
              @Override
              public void onStateChanged(@NonNull LifecycleOwner source,
                                         @NonNull Lifecycle.Event event) {
                  if (event == Lifecycle.Event.ON_DESTROY) {
                      handler.removeCallbacks(runnable);
                      source.getLifecycle().removeObserver(this);
                  }
              }
          });
  
          handler.postDelayed(runnable, GRACE_WINDOW_TIME_MS);
      }
  
      // Helper function for dealing with save / restore state
      private static @NonNull String createKey(@NonNull String prefix, long id) {
          return prefix + id;
      }
  
      // Helper function for dealing with save / restore state
      private static boolean isValidKey(@NonNull String key, @NonNull String prefix) {
          return key.startsWith(prefix) && key.length() > prefix.length();
      }
  
      // Helper function for dealing with save / restore state
      private static long parseIdFromKey(@NonNull String key, @NonNull String prefix) {
          return Long.parseLong(key.substring(prefix.length()));
      }
  
      /**
       * Pauses (STARTED) all Fragments that are attached and not a primary item.
       * Keeps primary item Fragment RESUMED.
       */
      class FragmentMaxLifecycleEnforcer {
          private ViewPager2.OnPageChangeCallback mPageChangeCallback;
          private RecyclerView.AdapterDataObserver mDataObserver;
          private LifecycleEventObserver mLifecycleObserver;
          private ViewPager2 mViewPager;
  
          private long mPrimaryItemId = NO_ID;
  
          void register(@NonNull RecyclerView recyclerView) {
              mViewPager = inferViewPager(recyclerView);
  
              // signal 1 of 3: current item has changed
              mPageChangeCallback = new ViewPager2.OnPageChangeCallback() {
                  @Override
                  public void onPageScrollStateChanged(int state) {
                      updateFragmentMaxLifecycle(false);
                  }
  
                  @Override
                  public void onPageSelected(int position) {
                      updateFragmentMaxLifecycle(false);
                  }
              };
              mViewPager.registerOnPageChangeCallback(mPageChangeCallback);
  
              // signal 2 of 3: underlying data-set has been updated
              mDataObserver = new CyFragStatePageAdapterVp2.DataSetChangeObserver() {
                  @Override
                  public void onChanged() {
                      updateFragmentMaxLifecycle(true);
                  }
              };
              registerAdapterDataObserver(mDataObserver);
  
              // signal 3 of 3: we may have to catch-up after being in a lifecycle state that
              // prevented us to perform transactions
              mLifecycleObserver = new LifecycleEventObserver() {
                  @Override
                  public void onStateChanged(@NonNull LifecycleOwner source,
                                             @NonNull Lifecycle.Event event) {
                      updateFragmentMaxLifecycle(false);
                  }
              };
              mLifecycle.addObserver(mLifecycleObserver);
          }
  
          void unregister(@NonNull RecyclerView recyclerView) {
              ViewPager2 viewPager = inferViewPager(recyclerView);
              viewPager.unregisterOnPageChangeCallback(mPageChangeCallback);
              unregisterAdapterDataObserver(mDataObserver);
              mLifecycle.removeObserver(mLifecycleObserver);
              mViewPager = null;
          }
  
          void updateFragmentMaxLifecycle(boolean dataSetChanged) {
              if (shouldDelayFragmentTransactions()) {
                  return; /** recovery step via {@link #mLifecycleObserver} */
              }
  
              if (mViewPager.getScrollState() != ViewPager2.SCROLL_STATE_IDLE) {
                  return; // do not update while not idle to avoid jitter
              }
  
              if (mFragments.isEmpty() || getItemCount() == 0) {
                  return; // nothing to do
              }
  
              final int currentItem = mViewPager.getCurrentItem();
              if (currentItem >= getItemCount()) {
                  /** current item is yet to be updated; it is guaranteed to change, so we will be
                   * notified via {@link ViewPager2.OnPageChangeCallback#onPageSelected(int)}  */
                  return;
              }
  
              long currentItemId = getItemId(currentItem);
              if (currentItemId == mPrimaryItemId && !dataSetChanged) {
                  return; // nothing to do
              }
  
              Fragment currentItemFragment = mFragments.get(currentItemId);
              if (currentItemFragment == null || !currentItemFragment.isAdded()) {
                  return;
              }
  
              mPrimaryItemId = currentItemId;
              FragmentTransaction transaction = mFragmentManager.beginTransaction();
  
              Fragment toResume = null;
              for (int ix = 0; ix < mFragments.size(); ix++) {
                  long itemId = mFragments.keyAt(ix);
                  Fragment fragment = mFragments.valueAt(ix);
  
                  if (!fragment.isAdded()) {
                      continue;
                  }
  
                  if (itemId != mPrimaryItemId) {
                      transaction.setMaxLifecycle(fragment, STARTED);
                  } else {
                      toResume = fragment; // itemId map key, so only one can match the predicate
                  }
  
                  fragment.setMenuVisibility(itemId == mPrimaryItemId);
              }
              if (toResume != null) { // in case the Fragment wasn't added yet
                  transaction.setMaxLifecycle(toResume, RESUMED);
              }
  
              if (!transaction.isEmpty()) {
                  transaction.commitNow();
              }
          }
  
          @NonNull
          private ViewPager2 inferViewPager(@NonNull RecyclerView recyclerView) {
              ViewParent parent = recyclerView.getParent();
              if (parent instanceof ViewPager2) {
                  return (ViewPager2) parent;
              }
              throw new IllegalStateException("Expected ViewPager2 instance. Got: " + parent);
          }
      }
  
      /**
       * Simplified {@link RecyclerView.AdapterDataObserver} for clients interested in any data-set
       * changes regardless of their nature.
       */
      private abstract static class DataSetChangeObserver extends RecyclerView.AdapterDataObserver {
          @Override
          public abstract void onChanged();
  
          @Override
          public final void onItemRangeChanged(int positionStart, int itemCount) {
              onChanged();
          }
  
          @Override
          public final void onItemRangeChanged(int positionStart, int itemCount,
                                               @Nullable Object payload) {
              onChanged();
          }
  
          @Override
          public final void onItemRangeInserted(int positionStart, int itemCount) {
              onChanged();
          }
  
          @Override
          public final void onItemRangeRemoved(int positionStart, int itemCount) {
              onChanged();
          }
  
          @Override
          public final void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
              onChanged();
          }
      }
  }
  