content: /*
   * Copyright (C) 2019 The Android Open Source Project
   *
   * 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 android.view;
  
  import android.annotation.NonNull;
  import android.annotation.Nullable;
  import android.graphics.Rect;
  
  import com.android.internal.util.Preconditions;
  
  import java.lang.ref.WeakReference;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.Iterator;
  import java.util.List;
  
  /**
   * Used by {@link ViewRootImpl} to track system gesture exclusion rects reported by views.
   */
  class GestureExclusionTracker {
      private boolean mGestureExclusionViewsChanged = false;
      private boolean mRootGestureExclusionRectsChanged = false;
      private List<Rect> mRootGestureExclusionRects = Collections.emptyList();
      private List<GestureExclusionViewInfo> mGestureExclusionViewInfos = new ArrayList<>();
      private List<Rect> mGestureExclusionRects = Collections.emptyList();
  
      public void updateRectsForView(@NonNull View view) {
          boolean found = false;
          final Iterator<GestureExclusionViewInfo> i = mGestureExclusionViewInfos.iterator();
          while (i.hasNext()) {
              final GestureExclusionViewInfo info = i.next();
              final View v = info.getView();
              if (v == null || !v.isAttachedToWindow() || !v.isAggregatedVisible()) {
                  mGestureExclusionViewsChanged = true;
                  i.remove();
                  continue;
              }
              if (v == view) {
                  found = true;
                  info.mDirty = true;
                  break;
              }
          }
          if (!found && view.isAttachedToWindow()) {
              mGestureExclusionViewInfos.add(new GestureExclusionViewInfo(view));
              mGestureExclusionViewsChanged = true;
          }
      }
  
      @Nullable
      public List<Rect> computeChangedRects() {
          boolean changed = mRootGestureExclusionRectsChanged;
          final Iterator<GestureExclusionViewInfo> i = mGestureExclusionViewInfos.iterator();
          final List<Rect> rects = new ArrayList<>(mRootGestureExclusionRects);
          while (i.hasNext()) {
              final GestureExclusionViewInfo info = i.next();
              switch (info.update()) {
                  case GestureExclusionViewInfo.CHANGED:
                      changed = true;
                      // Deliberate fall-through
                  case GestureExclusionViewInfo.UNCHANGED:
                      rects.addAll(info.mExclusionRects);
                      break;
                  case GestureExclusionViewInfo.GONE:
                      mGestureExclusionViewsChanged = true;
                      i.remove();
                      break;
              }
          }
          if (changed || mGestureExclusionViewsChanged) {
              mGestureExclusionViewsChanged = false;
              mRootGestureExclusionRectsChanged = false;
              if (!mGestureExclusionRects.equals(rects)) {
                  mGestureExclusionRects = rects;
                  return rects;
              }
          }
          return null;
      }
  
      public void setRootSystemGestureExclusionRects(@NonNull List<Rect> rects) {
          Preconditions.checkNotNull(rects, "rects must not be null");
          mRootGestureExclusionRects = rects;
          mRootGestureExclusionRectsChanged = true;
      }
  
      @NonNull
      public List<Rect> getRootSystemGestureExclusionRects() {
          return mRootGestureExclusionRects;
      }
  
      private static class GestureExclusionViewInfo {
          public static final int CHANGED = 0;
          public static final int UNCHANGED = 1;
          public static final int GONE = 2;
  
          private final WeakReference<View> mView;
          boolean mDirty = true;
          List<Rect> mExclusionRects = Collections.emptyList();
  
          GestureExclusionViewInfo(View view) {
              mView = new WeakReference<>(view);
          }
  
          public View getView() {
              return mView.get();
          }
  
          public int update() {
              final View excludedView = getView();
              if (excludedView == null || !excludedView.isAttachedToWindow()
                      || !excludedView.isAggregatedVisible()) return GONE;
              final List<Rect> localRects = excludedView.getSystemGestureExclusionRects();
              final List<Rect> newRects = new ArrayList<>(localRects.size());
              for (Rect src : localRects) {
                  Rect mappedRect = new Rect(src);
                  ViewParent p = excludedView.getParent();
                  if (p != null && p.getChildVisibleRect(excludedView, mappedRect, null)) {
                      newRects.add(mappedRect);
                  }
              }
  
              if (mExclusionRects.equals(localRects)) return UNCHANGED;
              mExclusionRects = newRects;
              return CHANGED;
          }
      }
  }
  