content: /*
   * Copyright (C) 2006 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.widget;
  
  
  import android.annotation.AnimRes;
  import android.annotation.UnsupportedAppUsage;
  import android.content.Context;
  import android.content.res.TypedArray;
  import android.util.AttributeSet;
  import android.view.View;
  import android.view.ViewGroup;
  import android.view.animation.Animation;
  import android.view.animation.AnimationUtils;
  import android.view.inspector.InspectableProperty;
  
  /**
   * Base class for a {@link FrameLayout} container that will perform animations
   * when switching between its views.
   *
   * @attr ref android.R.styleable#ViewAnimator_inAnimation
   * @attr ref android.R.styleable#ViewAnimator_outAnimation
   * @attr ref android.R.styleable#ViewAnimator_animateFirstView
   */
  public class ViewAnimator extends FrameLayout {
  
      @UnsupportedAppUsage
      int mWhichChild = 0;
      @UnsupportedAppUsage
      boolean mFirstTime = true;
  
      boolean mAnimateFirstTime = true;
  
      Animation mInAnimation;
      Animation mOutAnimation;
  
      public ViewAnimator(Context context) {
          super(context);
          initViewAnimator(context, null);
      }
  
      public ViewAnimator(Context context, AttributeSet attrs) {
          super(context, attrs);
  
          TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.ViewAnimator);
          int resource = a.getResourceId(com.android.internal.R.styleable.ViewAnimator_inAnimation, 0);
          if (resource > 0) {
              setInAnimation(context, resource);
          }
  
          resource = a.getResourceId(com.android.internal.R.styleable.ViewAnimator_outAnimation, 0);
          if (resource > 0) {
              setOutAnimation(context, resource);
          }
  
          boolean flag = a.getBoolean(com.android.internal.R.styleable.ViewAnimator_animateFirstView, true);
          setAnimateFirstView(flag);
  
          a.recycle();
  
          initViewAnimator(context, attrs);
      }
  
      /**
       * Initialize this {@link ViewAnimator}, possibly setting
       * {@link #setMeasureAllChildren(boolean)} based on {@link FrameLayout} flags.
       */
      private void initViewAnimator(Context context, AttributeSet attrs) {
          if (attrs == null) {
              // For compatibility, always measure children when undefined.
              mMeasureAllChildren = true;
              return;
          }
  
          // For compatibility, default to measure children, but allow XML
          // attribute to override.
          final TypedArray a = context.obtainStyledAttributes(attrs,
                  com.android.internal.R.styleable.FrameLayout);
          final boolean measureAllChildren = a.getBoolean(
                  com.android.internal.R.styleable.FrameLayout_measureAllChildren, true);
          setMeasureAllChildren(measureAllChildren);
          a.recycle();
      }
  
      /**
       * Sets which child view will be displayed.
       *
       * @param whichChild the index of the child view to display
       */
      @android.view.RemotableViewMethod
      public void setDisplayedChild(int whichChild) {
          mWhichChild = whichChild;
          if (whichChild >= getChildCount()) {
              mWhichChild = 0;
          } else if (whichChild < 0) {
              mWhichChild = getChildCount() - 1;
          }
          boolean hasFocus = getFocusedChild() != null;
          // This will clear old focus if we had it
          showOnly(mWhichChild);
          if (hasFocus) {
              // Try to retake focus if we had it
              requestFocus(FOCUS_FORWARD);
          }
      }
  
      /**
       * Returns the index of the currently displayed child view.
       */
      public int getDisplayedChild() {
          return mWhichChild;
      }
  
      /**
       * Manually shows the next child.
       */
      @android.view.RemotableViewMethod
      public void showNext() {
          setDisplayedChild(mWhichChild + 1);
      }
  
      /**
       * Manually shows the previous child.
       */
      @android.view.RemotableViewMethod
      public void showPrevious() {
          setDisplayedChild(mWhichChild - 1);
      }
  
      /**
       * Shows only the specified child. The other displays Views exit the screen,
       * optionally with the with the {@link #getOutAnimation() out animation} and
       * the specified child enters the screen, optionally with the
       * {@link #getInAnimation() in animation}.
       *
       * @param childIndex The index of the child to be shown.
       * @param animate Whether or not to use the in and out animations, defaults
       *            to true.
       */
      @UnsupportedAppUsage
      void showOnly(int childIndex, boolean animate) {
          final int count = getChildCount();
          for (int i = 0; i < count; i++) {
              final View child = getChildAt(i);
              if (i == childIndex) {
                  if (animate && mInAnimation != null) {
                      child.startAnimation(mInAnimation);
                  }
                  child.setVisibility(View.VISIBLE);
                  mFirstTime = false;
              } else {
                  if (animate && mOutAnimation != null && child.getVisibility() == View.VISIBLE) {
                      child.startAnimation(mOutAnimation);
                  } else if (child.getAnimation() == mInAnimation)
                      child.clearAnimation();
                  child.setVisibility(View.GONE);
              }
          }
      }
      /**
       * Shows only the specified child. The other displays Views exit the screen
       * with the {@link #getOutAnimation() out animation} and the specified child
       * enters the screen with the {@link #getInAnimation() in animation}.
       *
       * @param childIndex The index of the child to be shown.
       */
      void showOnly(int childIndex) {
          final boolean animate = (!mFirstTime || mAnimateFirstTime);
          showOnly(childIndex, animate);
      }
  
      @Override
      public void addView(View child, int index, ViewGroup.LayoutParams params) {
          super.addView(child, index, params);
          if (getChildCount() == 1) {
              child.setVisibility(View.VISIBLE);
          } else {
              child.setVisibility(View.GONE);
          }
          if (index >= 0 && mWhichChild >= index) {
              // Added item above current one, increment the index of the displayed child
              setDisplayedChild(mWhichChild + 1);
          }
      }
  
      @Override
      public void removeAllViews() {
          super.removeAllViews();
          mWhichChild = 0;
          mFirstTime = true;
      }
  
      @Override
      public void removeView(View view) {
          final int index = indexOfChild(view);
          if (index >= 0) {
              removeViewAt(index);
          }
      }
  
      @Override
      public void removeViewAt(int index) {
          super.removeViewAt(index);
          final int childCount = getChildCount();
          if (childCount == 0) {
              mWhichChild = 0;
              mFirstTime = true;
          } else if (mWhichChild >= childCount) {
              // Displayed is above child count, so float down to top of stack
              setDisplayedChild(childCount - 1);
          } else if (mWhichChild == index) {
              // Displayed was removed, so show the new child living in its place
              setDisplayedChild(mWhichChild);
          }
      }
  
      public void removeViewInLayout(View view) {
          removeView(view);
      }
  
      public void removeViews(int start, int count) {
          super.removeViews(start, count);
          if (getChildCount() == 0) {
              mWhichChild = 0;
              mFirstTime = true;
          } else if (mWhichChild >= start && mWhichChild < start + count) {
              // Try showing new displayed child, wrapping if needed
              setDisplayedChild(mWhichChild);
          }
      }
  
      public void removeViewsInLayout(int start, int count) {
          removeViews(start, count);
      }
  
      /**
       * Returns the View corresponding to the currently displayed child.
       *
       * @return The View currently displayed.
       *
       * @see #getDisplayedChild()
       */
      public View getCurrentView() {
          return getChildAt(mWhichChild);
      }
  
      /**
       * Returns the current animation used to animate a View that enters the screen.
       *
       * @return An Animation or null if none is set.
       *
       * @see #setInAnimation(android.view.animation.Animation)
       * @see #setInAnimation(android.content.Context, int)
       */
      @InspectableProperty
      public Animation getInAnimation() {
          return mInAnimation;
      }
  
      /**
       * Specifies the animation used to animate a View that enters the screen.
       *
       * @param inAnimation The animation started when a View enters the screen.
       *
       * @see #getInAnimation()
       * @see #setInAnimation(android.content.Context, int)
       */
      public void setInAnimation(Animation inAnimation) {
          mInAnimation = inAnimation;
      }
  
      /**
       * Returns the current animation used to animate a View that exits the screen.
       *
       * @return An Animation or null if none is set.
       *
       * @see #setOutAnimation(android.view.animation.Animation)
       * @see #setOutAnimation(android.content.Context, int)
       */
      @InspectableProperty
      public Animation getOutAnimation() {
          return mOutAnimation;
      }
  
      /**
       * Specifies the animation used to animate a View that exit the screen.
       *
       * @param outAnimation The animation started when a View exit the screen.
       *
       * @see #getOutAnimation()
       * @see #setOutAnimation(android.content.Context, int)
       */
      public void setOutAnimation(Animation outAnimation) {
          mOutAnimation = outAnimation;
      }
  
      /**
       * Specifies the animation used to animate a View that enters the screen.
       *
       * @param context The application's environment.
       * @param resourceID The resource id of the animation.
       *
       * @see #getInAnimation()
       * @see #setInAnimation(android.view.animation.Animation)
       */
      public void setInAnimation(Context context, @AnimRes int resourceID) {
          setInAnimation(AnimationUtils.loadAnimation(context, resourceID));
      }
  
      /**
       * Specifies the animation used to animate a View that exit the screen.
       *
       * @param context The application's environment.
       * @param resourceID The resource id of the animation.
       *
       * @see #getOutAnimation()
       * @see #setOutAnimation(android.view.animation.Animation)
       */
      public void setOutAnimation(Context context, @AnimRes int resourceID) {
          setOutAnimation(AnimationUtils.loadAnimation(context, resourceID));
      }
  
      /**
       * Returns whether the current View should be animated the first time the ViewAnimator
       * is displayed.
       *
       * @return true if the current View will be animated the first time it is displayed,
       * false otherwise.
       *
       * @see #setAnimateFirstView(boolean)
       */
      @InspectableProperty
      public boolean getAnimateFirstView() {
          return mAnimateFirstTime;
      }
  
      /**
       * Indicates whether the current View should be animated the first time
       * the ViewAnimator is displayed.
       *
       * @param animate True to animate the current View the first time it is displayed,
       *                false otherwise.
       */
      public void setAnimateFirstView(boolean animate) {
          mAnimateFirstTime = animate;
      }
  
      @Override
      public int getBaseline() {
          return (getCurrentView() != null) ? getCurrentView().getBaseline() : super.getBaseline();
      }
  
      @Override
      public CharSequence getAccessibilityClassName() {
          return ViewAnimator.class.getName();
      }
  }
  