124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller/*
224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller * Copyright (C) 2009 The Android Open Source Project
324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller *
424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller * Licensed under the Apache License, Version 2.0 (the "License");
524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller * you may not use this file except in compliance with the License.
624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller * You may obtain a copy of the License at
724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller *
824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller *      http://www.apache.org/licenses/LICENSE-2.0
924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller *
1024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller * Unless required by applicable law or agreed to in writing, software
1124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller * distributed under the License is distributed on an "AS IS" BASIS,
1224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller * See the License for the specific language governing permissions and
1424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller * limitations under the License.
1524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller */
1624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
1724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Millerpackage com.android.internal.widget;
1824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
1924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Millerimport android.content.Context;
201d0a152fa8905b9dfb87dc87da8e5432e48eae96Jim Millerimport android.content.res.Configuration;
2124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Millerimport android.content.res.Resources;
2224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Millerimport android.content.res.TypedArray;
23521d400b230bee5e7b9748f26832c0d0275b8253Jim Millerimport android.graphics.Canvas;
2424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Millerimport android.graphics.Rect;
25753401aa471d2fb87ab937c2b02b182ebc215c3aJim Millerimport android.graphics.drawable.Drawable;
2624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Millerimport android.os.Vibrator;
2724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Millerimport android.util.AttributeSet;
2824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Millerimport android.util.Log;
2924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Millerimport android.view.Gravity;
3024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Millerimport android.view.MotionEvent;
3124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Millerimport android.view.View;
3224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Millerimport android.view.ViewGroup;
33521d400b230bee5e7b9748f26832c0d0275b8253Jim Millerimport android.view.animation.AlphaAnimation;
34521d400b230bee5e7b9748f26832c0d0275b8253Jim Millerimport android.view.animation.Animation;
35521d400b230bee5e7b9748f26832c0d0275b8253Jim Millerimport android.view.animation.LinearInterpolator;
36521d400b230bee5e7b9748f26832c0d0275b8253Jim Millerimport android.view.animation.TranslateAnimation;
37521d400b230bee5e7b9748f26832c0d0275b8253Jim Millerimport android.view.animation.Animation.AnimationListener;
3824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Millerimport android.widget.ImageView;
3924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Millerimport android.widget.TextView;
4024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Millerimport android.widget.ImageView.ScaleType;
4124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Millerimport com.android.internal.R;
4224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
4324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller/**
4424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller * A special widget containing two Sliders and a threshold for each.  Moving either slider beyond
45425ca595dcc37ddb7a9f96310e5b800f424811a6Jim Miller * the threshold will cause the registered OnTriggerListener.onTrigger() to be called with
464df2c5433bb530fae6191a0298a6d494ca03eaa7Jim Miller * whichHandle being {@link OnTriggerListener#LEFT_HANDLE} or {@link OnTriggerListener#RIGHT_HANDLE}
47425ca595dcc37ddb7a9f96310e5b800f424811a6Jim Miller * Equivalently, selecting a tab will result in a call to
484df2c5433bb530fae6191a0298a6d494ca03eaa7Jim Miller * {@link OnTriggerListener#onGrabbedStateChange(View, int)} with one of these two states. Releasing
494df2c5433bb530fae6191a0298a6d494ca03eaa7Jim Miller * the tab will result in whichHandle being {@link OnTriggerListener#NO_HANDLE}.
5024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller *
5124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller */
5224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Millerpublic class SlidingTab extends ViewGroup {
5324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private static final String LOG_TAG = "SlidingTab";
5424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private static final boolean DBG = false;
5524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private static final int HORIZONTAL = 0; // as defined in attrs.xml
5624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private static final int VERTICAL = 1;
5724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
5824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    // TODO: Make these configurable
594df2c5433bb530fae6191a0298a6d494ca03eaa7Jim Miller    private static final float THRESHOLD = 2.0f / 3.0f;
6024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private static final long VIBRATE_SHORT = 30;
6124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private static final long VIBRATE_LONG = 40;
62521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller    private static final int TRACKING_MARGIN = 50;
63521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller    private static final int ANIM_DURATION = 250; // Time for most animations (in ms)
64521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller    private static final int ANIM_TARGET_TIME = 500; // Time to show targets (in ms)
654f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller    private boolean mHoldLeftOnTransition = true;
664f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller    private boolean mHoldRightOnTransition = true;
6724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
6824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private OnTriggerListener mOnTriggerListener;
6924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private int mGrabbedState = OnTriggerListener.NO_HANDLE;
7024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private boolean mTriggered = false;
7124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private Vibrator mVibrator;
7224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private float mDensity; // used to scale dimensions for bitmaps.
7324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
7424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    /**
7524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * Either {@link #HORIZONTAL} or {@link #VERTICAL}.
7624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     */
7724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private int mOrientation;
7824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
7924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private Slider mLeftSlider;
8024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private Slider mRightSlider;
8124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private Slider mCurrentSlider;
8224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private boolean mTracking;
834df2c5433bb530fae6191a0298a6d494ca03eaa7Jim Miller    private float mThreshold;
8424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private Slider mOtherSlider;
8524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private boolean mAnimating;
86521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller    private Rect mTmpRect;
8724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
8824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    /**
894f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller     * Listener used to reset the view when the current animation completes.
904f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller     */
914f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller    private final AnimationListener mAnimationDoneListener = new AnimationListener() {
924f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller        public void onAnimationStart(Animation animation) {
934f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller
944f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller        }
954f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller
964f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller        public void onAnimationRepeat(Animation animation) {
974f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller
984f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller        }
994f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller
1004f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller        public void onAnimationEnd(Animation animation) {
1014f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller            onAnimationDone();
1024f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller        }
1034f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller    };
1044f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller
1054f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller    /**
10624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * Interface definition for a callback to be invoked when a tab is triggered
1074df2c5433bb530fae6191a0298a6d494ca03eaa7Jim Miller     * by moving it beyond a threshold.
10824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     */
10924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    public interface OnTriggerListener {
11024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        /**
11124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         * The interface was triggered because the user let go of the handle without reaching the
1124df2c5433bb530fae6191a0298a6d494ca03eaa7Jim Miller         * threshold.
11324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         */
11424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        public static final int NO_HANDLE = 0;
11524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
11624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        /**
11724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         * The interface was triggered because the user grabbed the left handle and moved it past
1184df2c5433bb530fae6191a0298a6d494ca03eaa7Jim Miller         * the threshold.
11924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         */
12024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        public static final int LEFT_HANDLE = 1;
12124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
12224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        /**
12324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         * The interface was triggered because the user grabbed the right handle and moved it past
1244df2c5433bb530fae6191a0298a6d494ca03eaa7Jim Miller         * the threshold.
12524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         */
12624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        public static final int RIGHT_HANDLE = 2;
12724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
12824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        /**
1294df2c5433bb530fae6191a0298a6d494ca03eaa7Jim Miller         * Called when the user moves a handle beyond the threshold.
13024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         *
13124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         * @param v The view that was triggered.
13224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         * @param whichHandle  Which "dial handle" the user grabbed,
13324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         *        either {@link #LEFT_HANDLE}, {@link #RIGHT_HANDLE}.
13424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         */
13524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        void onTrigger(View v, int whichHandle);
13624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
13724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        /**
13824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         * Called when the "grabbed state" changes (i.e. when the user either grabs or releases
13924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         * one of the handles.)
14024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         *
14124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         * @param v the view that was triggered
14224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         * @param grabbedState the new state: {@link #NO_HANDLE}, {@link #LEFT_HANDLE},
14324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         * or {@link #RIGHT_HANDLE}.
14424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         */
14524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        void onGrabbedStateChange(View v, int grabbedState);
14624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    }
14724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
14824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    /**
149425ca595dcc37ddb7a9f96310e5b800f424811a6Jim Miller     * Simple container class for all things pertinent to a slider.
15024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * A slider consists of 3 Views:
151425ca595dcc37ddb7a9f96310e5b800f424811a6Jim Miller     *
15224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * {@link #tab} is the tab shown on the screen in the default state.
15324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * {@link #text} is the view revealed as the user slides the tab out.
15424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * {@link #target} is the target the user must drag the slider past to trigger the slider.
15524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     *
15624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     */
15724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private static class Slider {
15824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        /**
15924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         * Tab alignment - determines which side the tab should be drawn on
16024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         */
16124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        public static final int ALIGN_LEFT = 0;
16224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        public static final int ALIGN_RIGHT = 1;
16324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        public static final int ALIGN_TOP = 2;
16424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        public static final int ALIGN_BOTTOM = 3;
165521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller        public static final int ALIGN_UNKNOWN = 4;
16624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
16724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        /**
16824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         * States for the view.
16924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         */
17024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        private static final int STATE_NORMAL = 0;
17124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        private static final int STATE_PRESSED = 1;
17224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        private static final int STATE_ACTIVE = 2;
17324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
17424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        private final ImageView tab;
17524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        private final TextView text;
17624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        private final ImageView target;
1774df2c5433bb530fae6191a0298a6d494ca03eaa7Jim Miller        private int currentState = STATE_NORMAL;
178521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller        private int alignment = ALIGN_UNKNOWN;
179521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller        private int alignment_value;
18024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
18124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        /**
18224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         * Constructor
183425ca595dcc37ddb7a9f96310e5b800f424811a6Jim Miller         *
18424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         * @param parent the container view of this one
18524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         * @param tabId drawable for the tab
18624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         * @param barId drawable for the bar
18724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         * @param targetId drawable for the target
18824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         */
18924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        Slider(ViewGroup parent, int tabId, int barId, int targetId) {
19024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            // Create tab
19124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            tab = new ImageView(parent.getContext());
19224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            tab.setBackgroundResource(tabId);
19324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            tab.setScaleType(ScaleType.CENTER);
19424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            tab.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
19524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    LayoutParams.WRAP_CONTENT));
19624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
19724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            // Create hint TextView
19824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            text = new TextView(parent.getContext());
19924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            text.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
200980a938c1c9a6a5791a8240e5a1e6638ab28dc77Romain Guy                    LayoutParams.MATCH_PARENT));
20124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            text.setBackgroundResource(barId);
20224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            text.setTextAppearance(parent.getContext(), R.style.TextAppearance_SlidingTabNormal);
20324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            // hint.setSingleLine();  // Hmm.. this causes the text to disappear off-screen
20424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
20524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            // Create target
20624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            target = new ImageView(parent.getContext());
20724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            target.setImageResource(targetId);
20824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            target.setScaleType(ScaleType.CENTER);
20924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            target.setLayoutParams(
21024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
21124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            target.setVisibility(View.INVISIBLE);
21224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
21324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            parent.addView(target); // this needs to be first - relies on painter's algorithm
21424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            parent.addView(tab);
21524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            parent.addView(text);
21624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        }
21724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
21824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        void setIcon(int iconId) {
21924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            tab.setImageResource(iconId);
22024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        }
221425ca595dcc37ddb7a9f96310e5b800f424811a6Jim Miller
22224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        void setTabBackgroundResource(int tabId) {
22324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            tab.setBackgroundResource(tabId);
22424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        }
225425ca595dcc37ddb7a9f96310e5b800f424811a6Jim Miller
22624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        void setBarBackgroundResource(int barId) {
22724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            text.setBackgroundResource(barId);
22824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        }
229425ca595dcc37ddb7a9f96310e5b800f424811a6Jim Miller
23024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        void setHintText(int resId) {
231425ca595dcc37ddb7a9f96310e5b800f424811a6Jim Miller            text.setText(resId);
23224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        }
23324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
23424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        void hide() {
235521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            boolean horiz = alignment == ALIGN_LEFT || alignment == ALIGN_RIGHT;
236521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            int dx = horiz ? (alignment == ALIGN_LEFT ? alignment_value - tab.getRight()
237521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                    : alignment_value - tab.getLeft()) : 0;
238521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            int dy = horiz ? 0 : (alignment == ALIGN_TOP ? alignment_value - tab.getBottom()
239521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                    : alignment_value - tab.getTop());
240521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller
241521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            Animation trans = new TranslateAnimation(0, dx, 0, dy);
242521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            trans.setDuration(ANIM_DURATION);
243521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            trans.setFillAfter(true);
244521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            tab.startAnimation(trans);
245521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            text.startAnimation(trans);
24624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            target.setVisibility(View.INVISIBLE);
24724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        }
24824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
249521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller        void show(boolean animate) {
250521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            text.setVisibility(View.VISIBLE);
251521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            tab.setVisibility(View.VISIBLE);
252521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            //target.setVisibility(View.INVISIBLE);
253521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            if (animate) {
254521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                boolean horiz = alignment == ALIGN_LEFT || alignment == ALIGN_RIGHT;
255521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                int dx = horiz ? (alignment == ALIGN_LEFT ? tab.getWidth() : -tab.getWidth()) : 0;
256521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                int dy = horiz ? 0: (alignment == ALIGN_TOP ? tab.getHeight() : -tab.getHeight());
257521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller
258521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                Animation trans = new TranslateAnimation(-dx, 0, -dy, 0);
259521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                trans.setDuration(ANIM_DURATION);
260521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                tab.startAnimation(trans);
261521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                text.startAnimation(trans);
262521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            }
263521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller        }
264521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller
26524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        void setState(int state) {
26624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            text.setPressed(state == STATE_PRESSED);
26724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            tab.setPressed(state == STATE_PRESSED);
26824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            if (state == STATE_ACTIVE) {
26924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                final int[] activeState = new int[] {com.android.internal.R.attr.state_active};
27024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                if (text.getBackground().isStateful()) {
27124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    text.getBackground().setState(activeState);
27224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                }
27324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                if (tab.getBackground().isStateful()) {
27424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    tab.getBackground().setState(activeState);
27524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                }
27624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                text.setTextAppearance(text.getContext(), R.style.TextAppearance_SlidingTabActive);
27724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            } else {
27824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                text.setTextAppearance(text.getContext(), R.style.TextAppearance_SlidingTabNormal);
27924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            }
2804df2c5433bb530fae6191a0298a6d494ca03eaa7Jim Miller            currentState = state;
28124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        }
28224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
28324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        void showTarget() {
284521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            AlphaAnimation alphaAnim = new AlphaAnimation(0.0f, 1.0f);
285521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            alphaAnim.setDuration(ANIM_TARGET_TIME);
286521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            target.startAnimation(alphaAnim);
28724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            target.setVisibility(View.VISIBLE);
28824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        }
28924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
290521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller        void reset(boolean animate) {
29124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            setState(STATE_NORMAL);
29224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            text.setVisibility(View.VISIBLE);
29324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            text.setTextAppearance(text.getContext(), R.style.TextAppearance_SlidingTabNormal);
29424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            tab.setVisibility(View.VISIBLE);
29524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            target.setVisibility(View.INVISIBLE);
296521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            final boolean horiz = alignment == ALIGN_LEFT || alignment == ALIGN_RIGHT;
297521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            int dx = horiz ? (alignment == ALIGN_LEFT ?  alignment_value - tab.getLeft()
298521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                    : alignment_value - tab.getRight()) : 0;
299521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            int dy = horiz ? 0 : (alignment == ALIGN_TOP ? alignment_value - tab.getTop()
300521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                    : alignment_value - tab.getBottom());
301521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            if (animate) {
302521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                TranslateAnimation trans = new TranslateAnimation(0, dx, 0, dy);
303521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                trans.setDuration(ANIM_DURATION);
304521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                trans.setFillAfter(false);
305521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                text.startAnimation(trans);
306521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                tab.startAnimation(trans);
307521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            } else {
308521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                if (horiz) {
309521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                    text.offsetLeftAndRight(dx);
310521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                    tab.offsetLeftAndRight(dx);
311521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                } else {
312521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                    text.offsetTopAndBottom(dy);
313521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                    tab.offsetTopAndBottom(dy);
314521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                }
3154f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller                text.clearAnimation();
3164f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller                tab.clearAnimation();
3174f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller                target.clearAnimation();
318521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            }
31924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        }
32024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
32124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        void setTarget(int targetId) {
32224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            target.setImageResource(targetId);
32324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        }
32424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
32524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        /**
32624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         * Layout the given widgets within the parent.
32724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         *
32824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         * @param l the parent's left border
32924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         * @param t the parent's top border
33024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         * @param r the parent's right border
33124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         * @param b the parent's bottom border
33224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         * @param alignment which side to align the widget to
33324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller         */
33424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        void layout(int l, int t, int r, int b, int alignment) {
335521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            this.alignment = alignment;
336753401aa471d2fb87ab937c2b02b182ebc215c3aJim Miller            final Drawable tabBackground = tab.getBackground();
337753401aa471d2fb87ab937c2b02b182ebc215c3aJim Miller            final int handleWidth = tabBackground.getIntrinsicWidth();
338753401aa471d2fb87ab937c2b02b182ebc215c3aJim Miller            final int handleHeight = tabBackground.getIntrinsicHeight();
339753401aa471d2fb87ab937c2b02b182ebc215c3aJim Miller            final Drawable targetDrawable = target.getDrawable();
340753401aa471d2fb87ab937c2b02b182ebc215c3aJim Miller            final int targetWidth = targetDrawable.getIntrinsicWidth();
341753401aa471d2fb87ab937c2b02b182ebc215c3aJim Miller            final int targetHeight = targetDrawable.getIntrinsicHeight();
34224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            final int parentWidth = r - l;
34324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            final int parentHeight = b - t;
34424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
3454df2c5433bb530fae6191a0298a6d494ca03eaa7Jim Miller            final int leftTarget = (int) (THRESHOLD * parentWidth) - targetWidth + handleWidth / 2;
3464df2c5433bb530fae6191a0298a6d494ca03eaa7Jim Miller            final int rightTarget = (int) ((1.0f - THRESHOLD) * parentWidth) - handleWidth / 2;
34724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            final int left = (parentWidth - handleWidth) / 2;
34824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            final int right = left + handleWidth;
34924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
35024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            if (alignment == ALIGN_LEFT || alignment == ALIGN_RIGHT) {
35124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                // horizontal
35224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                final int targetTop = (parentHeight - targetHeight) / 2;
35324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                final int targetBottom = targetTop + targetHeight;
35424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                final int top = (parentHeight - handleHeight) / 2;
35524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                final int bottom = (parentHeight + handleHeight) / 2;
35624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                if (alignment == ALIGN_LEFT) {
35724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    tab.layout(0, top, handleWidth, bottom);
35824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    text.layout(0 - parentWidth, top, 0, bottom);
35924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    text.setGravity(Gravity.RIGHT);
36024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    target.layout(leftTarget, targetTop, leftTarget + targetWidth, targetBottom);
361521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                    alignment_value = l;
36224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                } else {
36324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    tab.layout(parentWidth - handleWidth, top, parentWidth, bottom);
36424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    text.layout(parentWidth, top, parentWidth + parentWidth, bottom);
36524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    target.layout(rightTarget, targetTop, rightTarget + targetWidth, targetBottom);
36624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    text.setGravity(Gravity.TOP);
367521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                    alignment_value = r;
36824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                }
36924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            } else {
37024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                // vertical
37124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                final int targetLeft = (parentWidth - targetWidth) / 2;
37224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                final int targetRight = (parentWidth + targetWidth) / 2;
3734df2c5433bb530fae6191a0298a6d494ca03eaa7Jim Miller                final int top = (int) (THRESHOLD * parentHeight) + handleHeight / 2 - targetHeight;
3744df2c5433bb530fae6191a0298a6d494ca03eaa7Jim Miller                final int bottom = (int) ((1.0f - THRESHOLD) * parentHeight) - handleHeight / 2;
37524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                if (alignment == ALIGN_TOP) {
37624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    tab.layout(left, 0, right, handleHeight);
37724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    text.layout(left, 0 - parentHeight, right, 0);
37824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    target.layout(targetLeft, top, targetRight, top + targetHeight);
379521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                    alignment_value = t;
38024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                } else {
38124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    tab.layout(left, parentHeight - handleHeight, right, parentHeight);
38224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    text.layout(left, parentHeight, right, parentHeight + parentHeight);
38324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    target.layout(targetLeft, bottom, targetRight, bottom + targetHeight);
384521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                    alignment_value = b;
38524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                }
38624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            }
38724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        }
38824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
3894df2c5433bb530fae6191a0298a6d494ca03eaa7Jim Miller        public void updateDrawableStates() {
3904df2c5433bb530fae6191a0298a6d494ca03eaa7Jim Miller            setState(currentState);
3914df2c5433bb530fae6191a0298a6d494ca03eaa7Jim Miller        }
3924df2c5433bb530fae6191a0298a6d494ca03eaa7Jim Miller
3934811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller        /**
3944811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller         * Ensure all the dependent widgets are measured.
3954811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller         */
3964811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller        public void measure() {
3974811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller            tab.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
3984811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller                    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
3994811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller            text.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
4004811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller                    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
4014811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller        }
4024811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller
4034811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller        /**
4044811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller         * Get the measured tab width. Must be called after {@link Slider#measure()}.
4054811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller         * @return
4064811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller         */
40724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        public int getTabWidth() {
4084811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller            return tab.getMeasuredWidth();
40924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        }
41024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
4114811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller        /**
4124811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller         * Get the measured tab width. Must be called after {@link Slider#measure()}.
4134811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller         * @return
4144811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller         */
41524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        public int getTabHeight() {
4164811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller            return tab.getMeasuredHeight();
41724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        }
418521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller
4198b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller        /**
4208b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller         * Start animating the slider. Note we need two animations since an Animator
4218b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller         * keeps internal state of the invalidation region which is just the view being animated.
4222cd1e6eda90170114e0795b13f65f964296cf2f2Jim Miller         *
4238b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller         * @param anim1
4248b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller         * @param anim2
4258b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller         */
4268b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller        public void startAnimation(Animation anim1, Animation anim2) {
4278b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller            tab.startAnimation(anim1);
4288b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller            text.startAnimation(anim2);
4294f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller        }
4304f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller
4314f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller        public void hideTarget() {
4324f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller            target.clearAnimation();
4334f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller            target.setVisibility(View.INVISIBLE);
434521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller        }
43524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    }
43624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
43724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    public SlidingTab(Context context) {
43824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        this(context, null);
43924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    }
44024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
44124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    /**
44224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * Constructor used when this widget is created from a layout file.
44324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     */
44424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    public SlidingTab(Context context, AttributeSet attrs) {
44524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        super(context, attrs);
44624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
447521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller        // Allocate a temporary once that can be used everywhere.
448521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller        mTmpRect = new Rect();
449521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller
45024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SlidingTab);
45124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        mOrientation = a.getInt(R.styleable.SlidingTab_orientation, HORIZONTAL);
45224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        a.recycle();
45324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
45424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        Resources r = getResources();
45524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        mDensity = r.getDisplayMetrics().density;
45624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        if (DBG) log("- Density: " + mDensity);
45724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
458425ca595dcc37ddb7a9f96310e5b800f424811a6Jim Miller        mLeftSlider = new Slider(this,
459425ca595dcc37ddb7a9f96310e5b800f424811a6Jim Miller                R.drawable.jog_tab_left_generic,
46024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                R.drawable.jog_tab_bar_left_generic,
46124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                R.drawable.jog_tab_target_gray);
462425ca595dcc37ddb7a9f96310e5b800f424811a6Jim Miller        mRightSlider = new Slider(this,
463425ca595dcc37ddb7a9f96310e5b800f424811a6Jim Miller                R.drawable.jog_tab_right_generic,
46424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                R.drawable.jog_tab_bar_right_generic,
46524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                R.drawable.jog_tab_target_gray);
46624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
46724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        // setBackgroundColor(0x80808080);
46824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    }
46924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
47024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    @Override
47124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
47224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
47324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        int widthSpecSize =  MeasureSpec.getSize(widthMeasureSpec);
47424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
47524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
47624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        int heightSpecSize =  MeasureSpec.getSize(heightMeasureSpec);
47724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
47824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        if (widthSpecMode == MeasureSpec.UNSPECIFIED || heightSpecMode == MeasureSpec.UNSPECIFIED) {
479966a8c78598bdba1e8993428b5b1eef4d97ae501Jim Miller            Log.e("SlidingTab", "SlidingTab cannot have UNSPECIFIED MeasureSpec"
480966a8c78598bdba1e8993428b5b1eef4d97ae501Jim Miller                    +"(wspec=" + widthSpecMode + ", hspec=" + heightSpecMode + ")",
481966a8c78598bdba1e8993428b5b1eef4d97ae501Jim Miller                    new RuntimeException(LOG_TAG + "stack:"));
48224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        }
48324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
4844811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller        mLeftSlider.measure();
4854811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller        mRightSlider.measure();
4864811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller        final int leftTabWidth = mLeftSlider.getTabWidth();
4874811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller        final int rightTabWidth = mRightSlider.getTabWidth();
4884811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller        final int leftTabHeight = mLeftSlider.getTabHeight();
4894811d62d5f07a1ffbf085d5e424e2b54d0e7ebfdJim Miller        final int rightTabHeight = mRightSlider.getTabHeight();
49024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        final int width;
49124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        final int height;
49224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        if (isHorizontal()) {
49324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            width = Math.max(widthSpecSize, leftTabWidth + rightTabWidth);
49424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            height = Math.max(leftTabHeight, rightTabHeight);
49524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        } else {
49624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            width = Math.max(leftTabWidth, rightTabHeight);
49724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            height = Math.max(heightSpecSize, leftTabHeight + rightTabHeight);
49824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        }
49924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        setMeasuredDimension(width, height);
50024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    }
50124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
50224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    @Override
50324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    public boolean onInterceptTouchEvent(MotionEvent event) {
50424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        final int action = event.getAction();
50524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        final float x = event.getX();
50624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        final float y = event.getY();
50724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
50824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        if (mAnimating) {
50924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            return false;
51024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        }
51124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
51224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        View leftHandle = mLeftSlider.tab;
513521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller        leftHandle.getHitRect(mTmpRect);
514521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller        boolean leftHit = mTmpRect.contains((int) x, (int) y);
51524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
51624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        View rightHandle = mRightSlider.tab;
517521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller        rightHandle.getHitRect(mTmpRect);
518521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller        boolean rightHit = mTmpRect.contains((int)x, (int) y);
51924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
52024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        if (!mTracking && !(leftHit || rightHit)) {
52124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            return false;
52224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        }
52324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
52424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        switch (action) {
52524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            case MotionEvent.ACTION_DOWN: {
52624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                mTracking = true;
52724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                mTriggered = false;
52824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                vibrate(VIBRATE_SHORT);
52924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                if (leftHit) {
53024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    mCurrentSlider = mLeftSlider;
53124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    mOtherSlider = mRightSlider;
5324df2c5433bb530fae6191a0298a6d494ca03eaa7Jim Miller                    mThreshold = isHorizontal() ? THRESHOLD : 1.0f - THRESHOLD;
53324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    setGrabbedState(OnTriggerListener.LEFT_HANDLE);
53424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                } else {
53524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    mCurrentSlider = mRightSlider;
53624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    mOtherSlider = mLeftSlider;
5374df2c5433bb530fae6191a0298a6d494ca03eaa7Jim Miller                    mThreshold = isHorizontal() ? 1.0f - THRESHOLD : THRESHOLD;
53824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    setGrabbedState(OnTriggerListener.RIGHT_HANDLE);
53924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                }
54024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                mCurrentSlider.setState(Slider.STATE_PRESSED);
54124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                mCurrentSlider.showTarget();
54224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                mOtherSlider.hide();
54324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                break;
54424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            }
54524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        }
54624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
54724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        return true;
54824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    }
54924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
550d8a3a8957b9d71ab75584b0cc98324fd70cc527cJim Miller    /**
551d8a3a8957b9d71ab75584b0cc98324fd70cc527cJim Miller     * Reset the tabs to their original state and stop any existing animation.
552d8a3a8957b9d71ab75584b0cc98324fd70cc527cJim Miller     * Animate them back into place if animate is true.
553d8a3a8957b9d71ab75584b0cc98324fd70cc527cJim Miller     *
554d8a3a8957b9d71ab75584b0cc98324fd70cc527cJim Miller     * @param animate
555d8a3a8957b9d71ab75584b0cc98324fd70cc527cJim Miller     */
556d8a3a8957b9d71ab75584b0cc98324fd70cc527cJim Miller    public void reset(boolean animate) {
557d8a3a8957b9d71ab75584b0cc98324fd70cc527cJim Miller        mLeftSlider.reset(animate);
558d8a3a8957b9d71ab75584b0cc98324fd70cc527cJim Miller        mRightSlider.reset(animate);
559966a8c78598bdba1e8993428b5b1eef4d97ae501Jim Miller        if (!animate) {
560966a8c78598bdba1e8993428b5b1eef4d97ae501Jim Miller            mAnimating = false;
561966a8c78598bdba1e8993428b5b1eef4d97ae501Jim Miller        }
562d8a3a8957b9d71ab75584b0cc98324fd70cc527cJim Miller    }
563d8a3a8957b9d71ab75584b0cc98324fd70cc527cJim Miller
56424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    @Override
5654f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller    public void setVisibility(int visibility) {
5664f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller        // Clear animations so sliders don't continue to animate when we show the widget again.
5674f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller        if (visibility != getVisibility() && visibility == View.INVISIBLE) {
568d8a3a8957b9d71ab75584b0cc98324fd70cc527cJim Miller           reset(false);
5694f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller        }
5704f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller        super.setVisibility(visibility);
5714f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller    }
5724f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller
5734f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller    @Override
57424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    public boolean onTouchEvent(MotionEvent event) {
57524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        if (mTracking) {
57624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            final int action = event.getAction();
57724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            final float x = event.getX();
57824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            final float y = event.getY();
579521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller
58024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            switch (action) {
58124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                case MotionEvent.ACTION_MOVE:
582521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                    if (withinView(x, y, this) ) {
583521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                        moveHandle(x, y);
584521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                        float position = isHorizontal() ? x : y;
585521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                        float target = mThreshold * (isHorizontal() ? getWidth() : getHeight());
586521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                        boolean thresholdReached;
587521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                        if (isHorizontal()) {
588521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                            thresholdReached = mCurrentSlider == mLeftSlider ?
589521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                                    position > target : position < target;
590521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                        } else {
591521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                            thresholdReached = mCurrentSlider == mLeftSlider ?
592521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                                    position < target : position > target;
593521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                        }
594521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                        if (!mTriggered && thresholdReached) {
595521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                            mTriggered = true;
596521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                            mTracking = false;
597521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                            mCurrentSlider.setState(Slider.STATE_ACTIVE);
5984f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller                            boolean isLeft = mCurrentSlider == mLeftSlider;
5994f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller                            dispatchTriggerEvent(isLeft ?
600521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                                OnTriggerListener.LEFT_HANDLE : OnTriggerListener.RIGHT_HANDLE);
601521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller
6024f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller                            startAnimating(isLeft ? mHoldLeftOnTransition : mHoldRightOnTransition);
603521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                            setGrabbedState(OnTriggerListener.NO_HANDLE);
604521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                        }
60524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                        break;
60624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    }
60724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    // Intentionally fall through - we're outside tracking rectangle
60824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
60924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                case MotionEvent.ACTION_UP:
61024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                case MotionEvent.ACTION_CANCEL:
61124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    mTracking = false;
61224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    mTriggered = false;
613521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                    mOtherSlider.show(true);
614521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                    mCurrentSlider.reset(false);
6154f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller                    mCurrentSlider.hideTarget();
616521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                    mCurrentSlider = null;
617521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller                    mOtherSlider = null;
61824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    setGrabbedState(OnTriggerListener.NO_HANDLE);
61924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    break;
62024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            }
62124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        }
62224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
62324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        return mTracking || super.onTouchEvent(event);
62424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    }
62524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
6264f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller    void startAnimating(final boolean holdAfter) {
627521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller        mAnimating = true;
6288b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller        final Animation trans1;
6298b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller        final Animation trans2;
6304f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller        final Slider slider = mCurrentSlider;
6314f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller        final Slider other = mOtherSlider;
6324f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller        final int dx;
6334f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller        final int dy;
634521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller        if (isHorizontal()) {
635521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            int right = slider.tab.getRight();
636521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            int width = slider.tab.getWidth();
637521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            int left = slider.tab.getLeft();
638521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            int viewWidth = getWidth();
6394f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller            int holdOffset = holdAfter ? 0 : width; // how much of tab to show at the end of anim
6404f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller            dx =  slider == mRightSlider ? - (right + viewWidth - holdOffset)
6414f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller                    : (viewWidth - left) + viewWidth - holdOffset;
642521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            dy = 0;
643521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller        } else {
644521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            int top = slider.tab.getTop();
645521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            int bottom = slider.tab.getBottom();
646521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            int height = slider.tab.getHeight();
647521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            int viewHeight = getHeight();
6484f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller            int holdOffset = holdAfter ? 0 : height; // how much of tab to show at end of anim
649521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            dx = 0;
6504f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller            dy =  slider == mRightSlider ? (top + viewHeight - holdOffset)
6514f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller                    : - ((viewHeight - bottom) + viewHeight - holdOffset);
652521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller        }
6538b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller        trans1 = new TranslateAnimation(0, dx, 0, dy);
6548b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller        trans1.setDuration(ANIM_DURATION);
6558b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller        trans1.setInterpolator(new LinearInterpolator());
6568b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller        trans1.setFillAfter(true);
6578b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller        trans2 = new TranslateAnimation(0, dx, 0, dy);
6588b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller        trans2.setDuration(ANIM_DURATION);
6598b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller        trans2.setInterpolator(new LinearInterpolator());
6608b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller        trans2.setFillAfter(true);
6618b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller
6628b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller        trans1.setAnimationListener(new AnimationListener() {
663521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            public void onAnimationEnd(Animation animation) {
6644f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller                Animation anim;
6654f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller                if (holdAfter) {
6664f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller                    anim = new TranslateAnimation(dx, dx, dy, dy);
6674f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller                    anim.setDuration(1000); // plenty of time for transitions
6684f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller                    mAnimating = false;
6694f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller                } else {
6704f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller                    anim = new AlphaAnimation(0.5f, 1.0f);
6714f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller                    anim.setDuration(ANIM_DURATION);
6724f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller                    resetView();
6734f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller                }
6744f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller                anim.setAnimationListener(mAnimationDoneListener);
6752cd1e6eda90170114e0795b13f65f964296cf2f2Jim Miller
6768b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller                /* Animation can be the same for these since the animation just holds */
6778b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller                mLeftSlider.startAnimation(anim, anim);
6788b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller                mRightSlider.startAnimation(anim, anim);
679521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            }
680521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller
681521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            public void onAnimationRepeat(Animation animation) {
682521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller
683521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            }
684521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller
685521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            public void onAnimationStart(Animation animation) {
686521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller
687521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            }
688521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller
689521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller        });
690521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller
6914f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller        slider.hideTarget();
6928b63ab664ff5068d022c080ce04995a0bfdd7030Jim Miller        slider.startAnimation(trans1, trans2);
693521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller    }
694521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller
6954f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller    private void onAnimationDone() {
6964f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller        resetView();
6974f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller        mAnimating = false;
6984f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller    }
6994f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller
700521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller    private boolean withinView(final float x, final float y, final View view) {
701521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller        return isHorizontal() && y > - TRACKING_MARGIN && y < TRACKING_MARGIN + view.getHeight()
702521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller            || !isHorizontal() && x > -TRACKING_MARGIN && x < TRACKING_MARGIN + view.getWidth();
703521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller    }
704521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller
70524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private boolean isHorizontal() {
70624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        return mOrientation == HORIZONTAL;
70724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    }
70824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
70924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private void resetView() {
710521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller        mLeftSlider.reset(false);
711521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller        mRightSlider.reset(false);
712521d400b230bee5e7b9748f26832c0d0275b8253Jim Miller        // onLayout(true, getLeft(), getTop(), getLeft() + getWidth(), getTop() + getHeight());
71324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    }
71424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
71524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    @Override
71624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    protected void onLayout(boolean changed, int l, int t, int r, int b) {
71724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        if (!changed) return;
71824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
71924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        // Center the widgets in the view
72024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        mLeftSlider.layout(l, t, r, b, isHorizontal() ? Slider.ALIGN_LEFT : Slider.ALIGN_BOTTOM);
72124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        mRightSlider.layout(l, t, r, b, isHorizontal() ? Slider.ALIGN_RIGHT : Slider.ALIGN_TOP);
72224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    }
72324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
72424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private void moveHandle(float x, float y) {
72524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        final View handle = mCurrentSlider.tab;
72624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        final View content = mCurrentSlider.text;
72724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        if (isHorizontal()) {
72824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            int deltaX = (int) x - handle.getLeft() - (handle.getWidth() / 2);
72924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            handle.offsetLeftAndRight(deltaX);
73024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            content.offsetLeftAndRight(deltaX);
73124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        } else {
73224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            int deltaY = (int) y - handle.getTop() - (handle.getHeight() / 2);
73324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            handle.offsetTopAndBottom(deltaY);
73424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            content.offsetTopAndBottom(deltaY);
73524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        }
73624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        invalidate(); // TODO: be more conservative about what we're invalidating
73724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    }
73824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
73924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    /**
74024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * Sets the left handle icon to a given resource.
74124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     *
74224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * The resource should refer to a Drawable object, or use 0 to remove
74324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * the icon.
74424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     *
74524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * @param iconId the resource ID of the icon drawable
74624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * @param targetId the resource of the target drawable
74724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * @param barId the resource of the bar drawable (stateful)
748425ca595dcc37ddb7a9f96310e5b800f424811a6Jim Miller     * @param tabId the resource of the
74924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     */
75024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    public void setLeftTabResources(int iconId, int targetId, int barId, int tabId) {
75124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        mLeftSlider.setIcon(iconId);
752425ca595dcc37ddb7a9f96310e5b800f424811a6Jim Miller        mLeftSlider.setTarget(targetId);
75324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        mLeftSlider.setBarBackgroundResource(barId);
75424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        mLeftSlider.setTabBackgroundResource(tabId);
7554df2c5433bb530fae6191a0298a6d494ca03eaa7Jim Miller        mLeftSlider.updateDrawableStates();
75624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    }
75724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
75824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    /**
75924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * Sets the left handle hint text to a given resource string.
76024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     *
76124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * @param resId
76224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     */
76324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    public void setLeftHintText(int resId) {
764425ca595dcc37ddb7a9f96310e5b800f424811a6Jim Miller        if (isHorizontal()) {
765425ca595dcc37ddb7a9f96310e5b800f424811a6Jim Miller            mLeftSlider.setHintText(resId);
766425ca595dcc37ddb7a9f96310e5b800f424811a6Jim Miller        }
76724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    }
76824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
76924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    /**
77024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * Sets the right handle icon to a given resource.
77124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     *
77224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * The resource should refer to a Drawable object, or use 0 to remove
77324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * the icon.
77424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     *
77524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * @param iconId the resource ID of the icon drawable
77624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * @param targetId the resource of the target drawable
77724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * @param barId the resource of the bar drawable (stateful)
778425ca595dcc37ddb7a9f96310e5b800f424811a6Jim Miller     * @param tabId the resource of the
77924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     */
78024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    public void setRightTabResources(int iconId, int targetId, int barId, int tabId) {
78124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        mRightSlider.setIcon(iconId);
782425ca595dcc37ddb7a9f96310e5b800f424811a6Jim Miller        mRightSlider.setTarget(targetId);
78324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        mRightSlider.setBarBackgroundResource(barId);
78424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        mRightSlider.setTabBackgroundResource(tabId);
7854df2c5433bb530fae6191a0298a6d494ca03eaa7Jim Miller        mRightSlider.updateDrawableStates();
78624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    }
78724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
78824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    /**
78924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * Sets the left handle hint text to a given resource string.
79024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     *
79124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * @param resId
79224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     */
79324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    public void setRightHintText(int resId) {
794425ca595dcc37ddb7a9f96310e5b800f424811a6Jim Miller        if (isHorizontal()) {
795425ca595dcc37ddb7a9f96310e5b800f424811a6Jim Miller            mRightSlider.setHintText(resId);
796425ca595dcc37ddb7a9f96310e5b800f424811a6Jim Miller        }
79724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    }
79824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
7994f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller    public void setHoldAfterTrigger(boolean holdLeft, boolean holdRight) {
8004f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller        mHoldLeftOnTransition = holdLeft;
8014f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller        mHoldRightOnTransition = holdRight;
8024f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller    }
8034f01d4a4da7754dbe6a49cca8f94e81104acb87fJim Miller
80424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    /**
80524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * Triggers haptic feedback.
80624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     */
80724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private synchronized void vibrate(long duration) {
80824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        if (mVibrator == null) {
80924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            mVibrator = (android.os.Vibrator)
81024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                    getContext().getSystemService(Context.VIBRATOR_SERVICE);
81124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        }
81224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        mVibrator.vibrate(duration);
81324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    }
81424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
81524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    /**
81624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * Registers a callback to be invoked when the user triggers an event.
81724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     *
81824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * @param listener the OnDialTriggerListener to attach to this view
81924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     */
82024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    public void setOnTriggerListener(OnTriggerListener listener) {
82124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        mOnTriggerListener = listener;
82224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    }
82324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
82424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    /**
82524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * Dispatches a trigger event to listener. Ignored if a listener is not set.
82624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * @param whichHandle the handle that triggered the event.
82724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     */
82824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private void dispatchTriggerEvent(int whichHandle) {
82924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        vibrate(VIBRATE_LONG);
83024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        if (mOnTriggerListener != null) {
83124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            mOnTriggerListener.onTrigger(this, whichHandle);
83224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        }
83324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    }
83424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
83524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    /**
83624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * Sets the current grabbed state, and dispatches a grabbed state change
83724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     * event to our listener.
83824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller     */
83924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private void setGrabbedState(int newState) {
84024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        if (newState != mGrabbedState) {
84124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            mGrabbedState = newState;
84224ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            if (mOnTriggerListener != null) {
84324ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller                mOnTriggerListener.onGrabbedStateChange(this, mGrabbedState);
84424ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller            }
84524ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        }
84624ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    }
84724ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller
84824ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    private void log(String msg) {
84924ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller        Log.d(LOG_TAG, msg);
85024ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller    }
85124ccf3c59077d81b0dd3b314822ff7dab215c165Jim Miller}
852