content: /*
   * Copyright (C) 2011 Cyril Mottier (http://www.cyrilmottier.com)
   *
   * 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 greendroid.widget;
  
  import android.content.Context;
  import android.graphics.Canvas;
  import android.graphics.Rect;
  import android.graphics.drawable.Drawable;
  import android.os.Parcel;
  import android.os.Parcelable;
  import android.util.AttributeSet;
  import android.view.Gravity;
  import android.view.View;
  
  import com.androidquery.simplefeed.R;
  
  
  /**
   * <p>
   * Visual indicator of a paged content. The PageIndicator usually displays a
   * line of dots. Each dot represents a page. The PageIndicator supports two
   * types of dots.
   * </p>
   * <ul>
   * <li>{@link DotType#SINGLE}: all dots are drawn but only one is in the
   * selected state at a time. The selected one represents the currently visible
   * page.</li>
   * <li>{@link DotType#MULTIPLE}: the selected page is actually represented by
   * the amount of dots currently being drawn. This behavior is similar to the one
   * visible on the Android Launcher application.</li>
   * </ul>
   * <p>
   * You can have a look at GDCatalog to get a sample code of how to use a
   * PageIndicator in addition to a {@link PagedView}.
   * </p>
   * 
   * @author Cyril Mottier
   */
  public class PageIndicator extends View {
  
      /**
       * Constant that may be used to select none of the dots in the PageIndicator
       */
      public static final int NO_ACTIVE_DOT = -1;
  
      /**
       * Interface containing of dot types supported by the PageIndicator class.
       * 
       * @author Cyril Mottier
       */
      public interface DotType {
  
          /**
           * Represents the single dot type. Only one selected dot may be drawn at
           * a time.
           */
  
          int SINGLE = 0;
  
          /**
           * Represents the multiple dot type. Several selected dot may be drawn
           * at a time. The number of dots drawn represents the currently
           * remaining page count.
           */
          int MULTIPLE = 1;
      }
  
      private static final int MIN_DOT_COUNT = 1;
  
      private static Rect sInRect = new Rect();
      private static Rect sOutRect = new Rect();
  
      private int mGravity;
      private int mDotSpacing;
      private Drawable mDotDrawable;
      private int mDotCount;
      private int mDotType;
  
      private int mActiveDot;
  
      private int[] mExtraState;
  
      private boolean mInitializing;
  
      public PageIndicator(Context context) {
          this(context, null);
          init(context);
      }
  
      public PageIndicator(Context context, AttributeSet attrs) {
          //this(context, attrs, R.attr.gdPageIndicatorStyle);
      	super(context, attrs);
      	 init(context);
      }
  
      public PageIndicator(Context context, AttributeSet attrs, int defStyle) {
          super(context, attrs, defStyle);
          
          init(context);
      }
      
      private void init(Context context){
      	
      	initPageIndicator();
      	
      	mInitializing = true;
  
          
          //TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PageIndicator, defStyle, 0);
  
          
          setDotCount(1);
          setActiveDot(0);
          setDotDrawable(context.getResources().getDrawable(R.drawable.gd_page_indicator_dot));
          setDotSpacing(3);
          //setGravity(a.getInt(R.styleable.PageIndicator_gravity, mGravity));
          //setDotType(a.getInt(R.styleable.PageIndicator_dotType, mDotType));
          
          //a.recycle();
          
          mInitializing = false;
      }
  
      private void initPageIndicator() {
          mDotCount = MIN_DOT_COUNT;
          mGravity = Gravity.CENTER;
          mActiveDot = 0;
          mDotSpacing = 0;
          mDotType = DotType.SINGLE;
  
          mExtraState = onCreateDrawableState(1);
          mergeDrawableStates(mExtraState, SELECTED_STATE_SET);
      }
  
      /**
       * Get the maximum number of dots to be drawn.
       * 
       * @return The maximum number of dots.
       * @see #setDotCount(int)
       */
      public int getDotCount() {
          return mDotCount;
      }
  
      /**
       * Set the number of dots.
       * 
       * @param dotCount The number oF dots
       * @see #getDotCount()
       */
      public void setDotCount(int dotCount) {
          if (dotCount < MIN_DOT_COUNT) {
              dotCount = MIN_DOT_COUNT;
          }
  
          if (mDotCount != dotCount) {
              mDotCount = dotCount;
              requestLayout();
              invalidate();
          }
      }
  
      /**
       * Return the current active dot. Depending on the current dot type of this
       * PageIndicator the current active dot may be the number of displayed dots
       * or the index of the selected dot
       * 
       * @return The current active dot index or dots count
       * @see #setActiveDot(int)
       */
      public int getActiveDot() {
          return mActiveDot;
      }
  
      /**
       * Set the index of the active dot or the number of active dots. Depending
       * on the current dot type of this PageIndicator the current active dot may
       * be the number of displayed dots or the index of the selected dot
       * 
       * @param activeDot The number/index of (the) active dot(s)
       * @see #getActiveDot()
       */
      public void setActiveDot(int activeDot) {
          if (activeDot < 0) {
              activeDot = NO_ACTIVE_DOT;
          }
  
          switch (mDotType) {
              case DotType.SINGLE:
                  if (activeDot > mDotCount - 1) {
                      activeDot = NO_ACTIVE_DOT;
                  }
                  break;
  
              case DotType.MULTIPLE:
                  if (activeDot > mDotCount) {
                      activeDot = NO_ACTIVE_DOT;
                  }
          }
  
          mActiveDot = activeDot;
          invalidate();
      }
  
      /**
       * Return the Drawable currently used for each dot.
       * 
       * @return The Drawable used to draw each dot.
       */
      public Drawable getDotDrawable() {
          return mDotDrawable;
      }
  
      /**
       * Set the Drawable used for each dot. The given Drawable may be a
       * StateListDrawable in order to take advantage of the selection system. If
       * your StateListDrawable contains a android.R.attr.state_selected state,
       * the Drawable will be used to represent a selected dot.
       * <em><strong>Note :</strong> this methods does not support Drawable
       * that has no intrinsic dimensions.</em>
       * 
       * @param dotDrawable The Drawable used to represents a dot
       */
      public void setDotDrawable(Drawable dotDrawable) {
          if (dotDrawable != mDotDrawable) {
              if (mDotDrawable != null) {
                  mDotDrawable.setCallback(null);
              }
  
              mDotDrawable = dotDrawable;
  
              if (dotDrawable != null) {
  
                  if (dotDrawable.getIntrinsicHeight() == -1 || dotDrawable.getIntrinsicWidth() == -1) {
                      // Do not accept Drawable with no intrinsic dimensions.
                      return;
                  }
  
                  dotDrawable.setBounds(0, 0, dotDrawable.getIntrinsicWidth(), dotDrawable.getIntrinsicHeight());
                  dotDrawable.setCallback(this);
                  if (dotDrawable.isStateful()) {
                      dotDrawable.setState(getDrawableState());
                  }
              }
  
              requestLayout();
              invalidate();
          }
      }
  
      /**
       * The spacing between each dot
       * 
       * @return The spacing between each dot
       */
      public int getDotSpacing() {
          return mDotSpacing;
      }
  
      /**
       * Set the spacing between each dot
       * 
       * @param dotSpacing The spacing between each dot.
       */
      public void setDotSpacing(int dotSpacing) {
          if (dotSpacing != mDotSpacing) {
              mDotSpacing = dotSpacing;
              requestLayout();
              invalidate();
          }
      }
  
      /**
       * Return the gravity used to draw dots/
       * 
       * @return The current gravity
       */
      public int getGravity() {
          return mGravity;
      }
  
      /**
       * Specifies how to align the dots by the view's x- and/or y-axis when the
       * space taken by the dots is smaller than the view.
       * 
       * @param gravity The gravity
       */
      public void setGravity(int gravity) {
          if (mGravity != gravity) {
              mGravity = gravity;
              invalidate();
          }
      }
  
      /**
       * The current dot type
       * 
       * @return The dot type of this {@link PageIndicator}
       * @see DotType
       */
      public int getDotType() {
          return mDotType;
      }
  
      /**
       * Specifies the type of dot actually drawn by this {@link PageIndicator}
       * 
       * @param dotType The dot type to use
       * @see DotType
       */
      public void setDotType(int dotType) {
          if (dotType == DotType.SINGLE || dotType == DotType.MULTIPLE) {
              if (mDotType != dotType) {
                  mDotType = dotType;
                  invalidate();
              }
          }
      }
  
      @Override
      public void requestLayout() {
          if (!mInitializing) {
              super.requestLayout();
          }
      }
  
      @Override
      public void invalidate() {
          if (!mInitializing) {
              super.invalidate();
          }
      }
  
      @Override
      protected boolean verifyDrawable(Drawable who) {
          return super.verifyDrawable(who) || who == mDotDrawable;
      }
  
      @Override
      protected void drawableStateChanged() {
          super.drawableStateChanged();
          mExtraState = onCreateDrawableState(1);
          mergeDrawableStates(mExtraState, SELECTED_STATE_SET);
          invalidate();
      }
  
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  
          Drawable d = mDotDrawable;
  
          int width = 0;
          int height = 0;
          if (d != null) {
              width = mDotCount * (d.getIntrinsicWidth() + mDotSpacing) - mDotSpacing;
              height = d.getIntrinsicHeight();
          }
  
          width += getPaddingRight() + getPaddingLeft();
          height += getPaddingBottom() + getPaddingTop();
  
          setMeasuredDimension(resolveSize(width, widthMeasureSpec), resolveSize(height, heightMeasureSpec));
      }
  
      @Override
      protected void onDraw(Canvas canvas) {
  
          final Drawable d = mDotDrawable;
          if (d != null) {
  
              final int count = mDotType == DotType.SINGLE ? mDotCount : mActiveDot;
  
              if (count <= 0) {
                  return;
              }
  
              final int h = d.getIntrinsicHeight();
              final int w = Math.max(0, count * (d.getIntrinsicWidth() + mDotSpacing) - mDotSpacing);
  
              final int pRight = getPaddingRight();
              final int pLeft = getPaddingLeft();
              final int pTop = getPaddingTop();
              final int pBottom = getPaddingBottom();
  
              sInRect.set(pLeft, pTop, getWidth() - pRight, getHeight() - pBottom);
              Gravity.apply(mGravity, w, h, sInRect, sOutRect);
  
              canvas.save();
              canvas.translate(sOutRect.left, sOutRect.top);
              for (int i = 0; i < count; i++) {
                  if (d.isStateful()) {
                      int[] state = getDrawableState();
                      if (mDotType == DotType.MULTIPLE || i == mActiveDot) {
                          state = mExtraState;
                      }
                      // HACK Cyril: The following code prevent the setState call
                      // from invalidating the View again (which will result in
                      // calling onDraw over and over again).
                      d.setCallback(null);
                      d.setState(state);
                      d.setCallback(this);
                  }
                  d.draw(canvas);
                  canvas.translate(mDotSpacing + d.getIntrinsicWidth(), 0);
              }
              canvas.restore();
          }
      }
  
      static class SavedState extends BaseSavedState {
  
          int activeDot;
  
          SavedState(Parcelable superState) {
              super(superState);
          }
  
          private SavedState(Parcel in) {
              super(in);
              activeDot = in.readInt();
          }
  
          @Override
          public void writeToParcel(Parcel out, int flags) {
              super.writeToParcel(out, flags);
              out.writeInt(activeDot);
          }
  
          public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
              public SavedState createFromParcel(Parcel in) {
                  return new SavedState(in);
              }
  
              public SavedState[] newArray(int size) {
                  return new SavedState[size];
              }
          };
      }
  
      @Override
      public Parcelable onSaveInstanceState() {
          Parcelable superState = super.onSaveInstanceState();
          SavedState ss = new SavedState(superState);
  
          ss.activeDot = mActiveDot;
  
          return ss;
      }
  
      @Override
      public void onRestoreInstanceState(Parcelable state) {
          SavedState ss = (SavedState) state;
          super.onRestoreInstanceState(ss.getSuperState());
  
          mActiveDot = ss.activeDot;
      }
  }
  