content: /*
   * Copyright (C) 2011 Google Inc.
   * Licensed to 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 com.android.ex.photo;
  
  import android.content.Context;
  import androidx.core.view.MotionEventCompat;
  import androidx.viewpager.widget.ViewPager;
  import android.util.AttributeSet;
  import android.view.MotionEvent;
  import android.view.View;
  
  /**
   * View pager for photo view fragments. Define our own class so we can specify the
   * view pager in XML.
   */
  public class PhotoViewPager extends ViewPager {
      /**
       * A type of intercept that should be performed
       */
      public static enum InterceptType { NONE, LEFT, RIGHT, BOTH }
  
      /**
       * Provides an ability to intercept touch events.
       * <p>
       * {@link ViewPager} intercepts all touch events and we need to be able to override this
       * behavior. Instead, we could perform a similar function by declaring a custom
       * {@link android.view.ViewGroup} to contain the pager and intercept touch events at a higher
       * level.
       */
      public static interface OnInterceptTouchListener {
          /**
           * Called when a touch intercept is about to occur.
           *
           * @param origX the raw x coordinate of the initial touch
           * @param origY the raw y coordinate of the initial touch
           * @return Which type of touch, if any, should should be intercepted.
           */
          public InterceptType onTouchIntercept(float origX, float origY);
      }
  
      private static final int INVALID_POINTER = -1;
  
      private float mLastMotionX;
      private int mActivePointerId;
      /** The x coordinate where the touch originated */
      private float mActivatedX;
      /** The y coordinate where the touch originated */
      private float mActivatedY;
      private OnInterceptTouchListener mListener;
  
      public PhotoViewPager(Context context) {
          super(context);
          initialize();
      }
  
      public PhotoViewPager(Context context, AttributeSet attrs) {
          super(context, attrs);
          initialize();
      }
  
      private void initialize() {
          // Set the page transformer to perform the transition animation
          // for each page in the view.
          setPageTransformer(true, new PageTransformer() {
              @Override
              public void transformPage(View page, float position) {
  
                  // The >= 1 is needed so that the page
                  // (page A) that transitions behind the newly visible
                  // page (page B) that comes in from the left does not
                  // get the touch events because it is still on screen
                  // (page A is still technically on screen despite being
                  // invisible). This makes sure that when the transition
                  // has completely finished, we revert it to its default
                  // behavior and move it off of the screen.
                  if (position < 0 || position >= 1.f) {
                      page.setTranslationX(0);
                      page.setAlpha(1.f);
                      page.setScaleX(1);
                      page.setScaleY(1);
                  } else {
                      page.setTranslationX(-position * page.getWidth());
                      page.setAlpha(Math.max(0,1.f - position));
                      final float scale = Math.max(0, 1.f - position * 0.3f);
                      page.setScaleX(scale);
                      page.setScaleY(scale);
                  }
              }
          });
      }
  
      /**
       * {@inheritDoc}
       * <p>
       * We intercept touch event intercepts so we can prevent switching views when the
       * current view is internally scrollable.
       */
      @Override
      public boolean onInterceptTouchEvent(MotionEvent ev) {
          final InterceptType intercept = (mListener != null)
                  ? mListener.onTouchIntercept(mActivatedX, mActivatedY)
                  : InterceptType.NONE;
          final boolean ignoreScrollLeft =
                  (intercept == InterceptType.BOTH || intercept == InterceptType.LEFT);
          final boolean ignoreScrollRight =
                  (intercept == InterceptType.BOTH || intercept == InterceptType.RIGHT);
  
          // Only check ability to page if we can't scroll in one / both directions
          final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
  
          if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
              mActivePointerId = INVALID_POINTER;
          }
  
          switch (action) {
              case MotionEvent.ACTION_MOVE: {
                  if (ignoreScrollLeft || ignoreScrollRight) {
                      final int activePointerId = mActivePointerId;
                      if (activePointerId == INVALID_POINTER) {
                          // If we don't have a valid id, the touch down wasn't on content.
                          break;
                      }
  
                      final int pointerIndex =
                              MotionEventCompat.findPointerIndex(ev, activePointerId);
                      final float x = MotionEventCompat.getX(ev, pointerIndex);
  
                      if (ignoreScrollLeft && ignoreScrollRight) {
                          mLastMotionX = x;
                          return false;
                      } else if (ignoreScrollLeft && (x > mLastMotionX)) {
                          mLastMotionX = x;
                          return false;
                      } else if (ignoreScrollRight && (x < mLastMotionX)) {
                          mLastMotionX = x;
                          return false;
                      }
                  }
                  break;
              }
  
              case MotionEvent.ACTION_DOWN: {
                  mLastMotionX = ev.getX();
                  // Use the raw x/y as the children can be located anywhere and there isn't a
                  // single offset that would be meaningful
                  mActivatedX = ev.getRawX();
                  mActivatedY = ev.getRawY();
                  mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
                  break;
              }
  
              case MotionEventCompat.ACTION_POINTER_UP: {
                  final int pointerIndex = MotionEventCompat.getActionIndex(ev);
                  final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
                  if (pointerId == mActivePointerId) {
                      // Our active pointer going up; select a new active pointer
                      final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                      mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex);
                      mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
                  }
                  break;
              }
          }
  
          return super.onInterceptTouchEvent(ev);
      }
  
      /**
       * sets the intercept touch listener.
       */
      public void setOnInterceptTouchListener(OnInterceptTouchListener l) {
          mListener = l;
      }
  }
  