1a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang/*
2a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Copyright (C) 2014 The Android Open Source Project
3a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang *
4a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Licensed under the Apache License, Version 2.0 (the "License");
5a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * you may not use this file except in compliance with the License.
6a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * You may obtain a copy of the License at
7a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang *
8a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang *      http://www.apache.org/licenses/LICENSE-2.0
9a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang *
10a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Unless required by applicable law or agreed to in writing, software
11a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * distributed under the License is distributed on an "AS IS" BASIS,
12a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * See the License for the specific language governing permissions and
14a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * limitations under the License.
15a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */
16a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
17a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangpackage com.android.fmradio.views;
18a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
19a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.animation.Animator;
20a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.animation.Animator.AnimatorListener;
21a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.animation.AnimatorListenerAdapter;
22a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.animation.ObjectAnimator;
23a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.content.Context;
24a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.content.res.Configuration;
25a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.content.res.Resources;
26a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.content.res.TypedArray;
27a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.database.Cursor;
28a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.graphics.Canvas;
29a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.graphics.Color;
30a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.graphics.Paint;
31a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.graphics.Typeface;
32a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.hardware.display.DisplayManagerGlobal;
33a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.os.Handler;
34a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.os.Looper;
35a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.util.AttributeSet;
36a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.util.DisplayMetrics;
37a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.view.Display;
38a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.view.DisplayInfo;
39a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.view.LayoutInflater;
40a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.view.Menu;
41a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.view.MenuItem;
42a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.view.MotionEvent;
43a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.view.VelocityTracker;
44a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.view.View;
45a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.view.ViewConfiguration;
46a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.view.ViewGroup;
47a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.view.ViewTreeObserver.OnPreDrawListener;
48a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.view.animation.Interpolator;
49a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.widget.AdapterView;
50a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.widget.AdapterView.OnItemClickListener;
51a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.widget.BaseAdapter;
52a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.widget.EdgeEffect;
53a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.widget.FrameLayout;
54a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.widget.GridView;
55a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.widget.ImageView;
56a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.widget.PopupMenu;
57a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.widget.PopupMenu.OnMenuItemClickListener;
58a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.widget.ScrollView;
59a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.widget.Scroller;
60a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport android.widget.TextView;
61a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
62a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport com.android.fmradio.FmStation;
63a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport com.android.fmradio.FmUtils;
64a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport com.android.fmradio.R;
65a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangimport com.android.fmradio.FmStation.Station;
66a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
67a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang/**
68a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * Modified from Contact MultiShrinkScroll Handle the touch event and change
69a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang * header size and scroll
70a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang */
71a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huangpublic class FmScroller extends FrameLayout {
72a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static final String TAG = "FmScroller";
73a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
74a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
75a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * 1000 pixels per millisecond. Ie, 1 pixel per second.
76a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
77a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static final int PIXELS_PER_SECOND = 1000;
78a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static final int ON_PLAY_ANIMATION_DELAY = 1000;
79a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static final int PORT_COLUMN_NUM = 3;
80a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static final int LAND_COLUMN_NUM = 5;
81a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static final int STATE_NO_FAVORITE = 0;
82a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static final int STATE_HAS_FAVORITE = 1;
83a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
84a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private float[] mLastEventPosition = {
85a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            0, 0
86a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    };
87a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private VelocityTracker mVelocityTracker;
88a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean mIsBeingDragged = false;
89a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean mReceivedDown = false;
90a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean mFirstOnResume = true;
91a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
92a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private String mSelection = "IS_FAVORITE=?";
93a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private String[] mSelectionArgs = {
94a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        "1"
95a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    };
96a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
97a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private EventListener mEventListener;
98a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private PopupMenu mPopupMenu;
99a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private Handler mMainHandler;
100a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private ScrollView mScrollView;
101a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private View mScrollViewChild;
102a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private GridView mGridView;
103a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private TextView mFavoriteText;
104a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private View mHeader;
105a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int mMaximumHeaderHeight;
106a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int mMinimumHeaderHeight;
107a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private Adjuster mAdjuster;
108a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int mCurrentStation;
109a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean mIsFmPlaying;
110a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
111a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private FavoriteAdapter mAdapter;
112a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private final Scroller mScroller;
113a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private final EdgeEffect mEdgeGlowBottom;
114a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private final int mTouchSlop;
115a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private final int mMaximumVelocity;
116a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private final int mMinimumVelocity;
117a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private final int mActionBarSize;
118a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
119a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private final AnimatorListener mHeaderExpandAnimationListener = new AnimatorListenerAdapter() {
120a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        @Override
121a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        public void onAnimationEnd(Animator animation) {
122a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            refreshStateHeight();
123a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
124a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    };
125a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
126a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
127a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Interpolator from android.support.v4.view.ViewPager. Snappier and more
128a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * elastic feeling than the default interpolator.
129a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
130a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static final Interpolator INTERPOLATOR = new Interpolator() {
131a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
132a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        /**
133a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         * {@inheritDoc}
134a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         */
135a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        @Override
136a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        public float getInterpolation(float t) {
137a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            t -= 1.0f;
138a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return t * t * t * t * t + 1.0f;
139a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
140a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    };
141a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
142a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
143a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Constructor
144a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
145a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param context The context
146a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
147a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public FmScroller(Context context) {
148a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        this(context, null);
149a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
150a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
151a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
152a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Constructor
153a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
154a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param context The context
155a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param attrs The attrs
156a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
157a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public FmScroller(Context context, AttributeSet attrs) {
158a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        this(context, attrs, 0);
159a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
160a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
161a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
162a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Constructor
163a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
164a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param context The context
165a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param attrs The attrs
166a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param defStyleAttr The default attr
167a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
168a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public FmScroller(Context context, AttributeSet attrs, int defStyleAttr) {
169a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        super(context, attrs, defStyleAttr);
170a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
171a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final ViewConfiguration configuration = ViewConfiguration.get(context);
172a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        setFocusable(false);
173a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
174a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // Drawing must be enabled in order to support EdgeEffect
175a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        setWillNotDraw(/* willNotDraw = */false);
176a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
177a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mEdgeGlowBottom = new EdgeEffect(context);
178a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mScroller = new Scroller(context, INTERPOLATOR);
179a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mTouchSlop = configuration.getScaledTouchSlop();
180a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
181a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
182a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
183a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final TypedArray attributeArray = context.obtainStyledAttributes(new int[] {
184a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            android.R.attr.actionBarSize
185a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        });
186a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mActionBarSize = attributeArray.getDimensionPixelSize(0, 0);
187a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        attributeArray.recycle();
188a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
189a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
190a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
191a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * This method must be called inside the Activity's OnCreate.
192a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
193a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void initialize() {
194a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mScrollView = (ScrollView) findViewById(R.id.content_scroller);
195a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mScrollViewChild = findViewById(R.id.favorite_container);
196a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mHeader = findViewById(R.id.main_header_parent);
197a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
198a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mMainHandler = new Handler(Looper.getMainLooper());
199a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
200a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mFavoriteText = (TextView) findViewById(R.id.favorite_text);
201a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mGridView = (GridView) findViewById(R.id.gridview);
202a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mAdapter = new FavoriteAdapter(getContext());
203a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
204a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mAdjuster = new Adjuster(getContext());
205a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
206a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mGridView.setAdapter(mAdapter);
207a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Cursor c = getData();
208a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mAdapter.swipResult(c);
209a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mGridView.setFocusable(false);
210a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mGridView.setFocusableInTouchMode(false);
211a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
212a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mGridView.setOnItemClickListener(new OnItemClickListener() {
213a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
214a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            @Override
215a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
216a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (mEventListener != null && mAdapter != null) {
217a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mEventListener.onPlay(mAdapter.getFrequency(position));
218a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
219a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
220a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mMainHandler.removeCallbacks(null);
221a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mMainHandler.postDelayed(new Runnable() {
222a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    @Override
223a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    public void run() {
224a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mMaximumHeaderHeight = getMaxHeight(STATE_HAS_FAVORITE);
225a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        expandHeader();
226a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
227a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }, ON_PLAY_ANIMATION_DELAY);
228a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
229a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
230a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        });
231a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
232a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // Called when first time create activity
233a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        doOnPreDraw(this, /* drawNextFrame = */false, new Runnable() {
234a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            @Override
235a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            public void run() {
236a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                refreshStateHeight();
237a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                setHeaderHeight(getMaximumScrollableHeaderHeight());
238a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                updateHeaderTextAndButton();
239a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                refreshFavoriteLayout();
240a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
241a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        });
242a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
243a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
244a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
245a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Runs a piece of code just before the next draw, after layout and measurement
246a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
247a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param view The view depend on
248a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param drawNextFrame Whether to draw next frame
249a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param runnable The executed runnable instance
250a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
251a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void doOnPreDraw(final View view, final boolean drawNextFrame,
252a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            final Runnable runnable) {
253a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final OnPreDrawListener listener = new OnPreDrawListener() {
254a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            @Override
255a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            public boolean onPreDraw() {
256a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                view.getViewTreeObserver().removeOnPreDrawListener(this);
257a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                runnable.run();
258a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                return drawNextFrame;
259a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
260a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        };
261a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        view.getViewTreeObserver().addOnPreDrawListener(listener);
262a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
263a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
264a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void refreshFavoriteLayout() {
265a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        setFavoriteTextHeight(mAdapter.getCount() == 0);
266a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        setGridViewHeight(computeGridViewHeight());
267a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
268a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
269a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void setFavoriteTextHeight(boolean show) {
270a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mAdapter.getCount() == 0) {
271a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mFavoriteText.setVisibility(View.GONE);
272a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        } else {
273a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mFavoriteText.setVisibility(View.VISIBLE);
274a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
275a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
276a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
277a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void setGridViewHeight(int height) {
278a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final ViewGroup.LayoutParams params = mGridView.getLayoutParams();
279a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        params.height = height;
280a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mGridView.setLayoutParams(params);
281a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
282a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
283a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int computeGridViewHeight() {
284a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int itemcount = mAdapter.getCount();
285a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (itemcount == 0) {
286a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return 0;
287a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
288a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int curOrientation = getResources().getConfiguration().orientation;
289a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final boolean isLandscape = curOrientation == Configuration.ORIENTATION_LANDSCAPE;
290a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int columnNum = isLandscape ? LAND_COLUMN_NUM : PORT_COLUMN_NUM;
291a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int itemHeight = (int) getResources().getDimension(R.dimen.fm_gridview_item_height);
292a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int itemPadding = (int) getResources().getDimension(R.dimen.fm_gridview_item_padding);
293a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int rownum = (int) Math.ceil(itemcount / (float) columnNum);
294a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int totalHeight = rownum * itemHeight + rownum * itemPadding;
295a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (rownum == 2) {
296a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            int minGridViewHeight = getHeight() - getMinHeight(STATE_HAS_FAVORITE) - 72;
297a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            totalHeight = Math.max(totalHeight, minGridViewHeight);
298a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
299a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
300a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return totalHeight;
301a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
302a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
303a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    @Override
304a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public boolean onInterceptTouchEvent(MotionEvent event) {
305a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // The only time we want to intercept touch events is when we are being
306a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // dragged.
307a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return shouldStartDrag(event);
308a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
309a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
310a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean shouldStartDrag(MotionEvent event) {
311a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mIsBeingDragged) {
312a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mIsBeingDragged = false;
313a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return false;
314a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
315a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
316a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        switch (event.getAction()) {
317a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // If we are in the middle of a fling and there is a down event,
318a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // we'll steal it and
319a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // start a drag.
320a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            case MotionEvent.ACTION_DOWN:
321a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                updateLastEventPosition(event);
322a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (!mScroller.isFinished()) {
323a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    startDrag();
324a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    return true;
325a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                } else {
326a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mReceivedDown = true;
327a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
328a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                break;
329a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
330a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // Otherwise, we will start a drag if there is enough motion in the
331a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // direction we are
332a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // capable of scrolling.
333a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            case MotionEvent.ACTION_MOVE:
334a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (motionShouldStartDrag(event)) {
335a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    updateLastEventPosition(event);
336a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    startDrag();
337a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    return true;
338a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
339a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                break;
340a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
341a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            default:
342a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                break;
343a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
344a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
345a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return false;
346a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
347a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
348a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    @Override
349a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public boolean onTouchEvent(MotionEvent event) {
350a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final int action = event.getAction();
351a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
352a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mVelocityTracker == null) {
353a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mVelocityTracker = VelocityTracker.obtain();
354a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
355a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mVelocityTracker.addMovement(event);
356a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (!mIsBeingDragged) {
357a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (shouldStartDrag(event)) {
358a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                return true;
359a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
360a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
361a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (action == MotionEvent.ACTION_UP && mReceivedDown) {
362a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mReceivedDown = false;
363a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                return performClick();
364a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
365a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return true;
366a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
367a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
368a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        switch (action) {
369a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            case MotionEvent.ACTION_MOVE:
370a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                final float delta = updatePositionAndComputeDelta(event);
371a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                scrollTo(0, getScroll() + (int) delta);
372a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mReceivedDown = false;
373a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
374a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (mIsBeingDragged) {
375a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    final int distanceFromMaxScrolling = getMaximumScrollUpwards() - getScroll();
376a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    if (delta > distanceFromMaxScrolling) {
377a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        // The ScrollView is being pulled upwards while there is
378a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        // no more
379a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        // content offscreen, and the view port is already fully
380a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        // expanded.
381a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mEdgeGlowBottom.onPull(delta / getHeight(), 1 - event.getX() / getWidth());
382a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
383a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
384a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    if (!mEdgeGlowBottom.isFinished()) {
385a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        postInvalidateOnAnimation();
386a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
387a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
388a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
389a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                break;
390a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
391a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            case MotionEvent.ACTION_UP:
392a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            case MotionEvent.ACTION_CANCEL:
393a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                stopDrag(action == MotionEvent.ACTION_CANCEL);
394a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mReceivedDown = false;
395a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                break;
396a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
397a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            default:
398a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                break;
399a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
400a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
401a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return true;
402a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
403a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
404a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
405a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Expand to maximum size or starting size. Disable clicks on the
406a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * photo until the animation is complete.
407a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
408a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void expandHeader() {
409a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (getHeaderHeight() != mMaximumHeaderHeight) {
410a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // Expand header
411a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            final ObjectAnimator animator = ObjectAnimator.ofInt(this, "headerHeight",
412a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mMaximumHeaderHeight);
413a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            animator.addListener(mHeaderExpandAnimationListener);
414a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            animator.setDuration(300);
415a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            animator.start();
416a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // Scroll nested scroll view to its top
417a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (mScrollView.getScrollY() != 0) {
418a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                ObjectAnimator.ofInt(mScrollView, "scrollY", 0).setDuration(300).start();
419a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
420a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
421a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
422a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
423a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void collapseHeader() {
424a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (getHeaderHeight() != mMinimumHeaderHeight) {
425a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            final ObjectAnimator animator = ObjectAnimator.ofInt(this, "headerHeight",
426a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mMinimumHeaderHeight);
427a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            animator.addListener(mHeaderExpandAnimationListener);
428a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            animator.start();
429a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
430a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
431a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
432a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void startDrag() {
433a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mIsBeingDragged = true;
434a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mScroller.abortAnimation();
435a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
436a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
437a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void stopDrag(boolean cancelled) {
438a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mIsBeingDragged = false;
439a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (!cancelled && getChildCount() > 0) {
440a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            final float velocity = getCurrentVelocity();
441a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (velocity > mMinimumVelocity || velocity < -mMinimumVelocity) {
442a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                fling(-velocity);
443a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
444a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
445a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
446a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mVelocityTracker != null) {
447a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mVelocityTracker.recycle();
448a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mVelocityTracker = null;
449a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
450a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
451a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mEdgeGlowBottom.onRelease();
452a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
453a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
454a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    @Override
455a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void scrollTo(int x, int y) {
456a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final int delta = y - getScroll();
457a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (delta > 0) {
458a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            scrollUp(delta);
459a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        } else {
460a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            scrollDown(delta);
461a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
462a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        updateHeaderTextAndButton();
463a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
464a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
465a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int getToolbarHeight() {
466a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return mHeader.getLayoutParams().height;
467a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
468a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
469a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
470a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Set the height of the toolbar and update its tint accordingly.
471a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
472a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    @FmReflection
473a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void setHeaderHeight(int height) {
474a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final ViewGroup.LayoutParams toolbarLayoutParams = mHeader.getLayoutParams();
475a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        toolbarLayoutParams.height = height;
476a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mHeader.setLayoutParams(toolbarLayoutParams);
477a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        updateHeaderTextAndButton();
478a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
479a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
480a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
481a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Get header height. Used in ObjectAnimator
482a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
483a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @return The header height
484a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
485a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    @FmReflection
486a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public int getHeaderHeight() {
487a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return mHeader.getLayoutParams().height;
488a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
489a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
490a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
491a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Set scroll. Used in ObjectAnimator
492a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
493a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    @FmReflection
494a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void setScroll(int scroll) {
495a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        scrollTo(0, scroll);
496a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
497a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
498a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
499a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Returns the total amount scrolled inside the nested ScrollView + the amount
500a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * of shrinking performed on the ToolBar. This is the value inspected by animators.
501a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
502a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    @FmReflection
503a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public int getScroll() {
504a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return getMaximumScrollableHeaderHeight() - getToolbarHeight() + mScrollView.getScrollY();
505a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
506a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
507a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int getMaximumScrollableHeaderHeight() {
508a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return mMaximumHeaderHeight;
509a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
510a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
511a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
512a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * A variant of {@link #getScroll} that pretends the header is never
513a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * larger than than mIntermediateHeaderHeight. This function is sometimes
514a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * needed when making scrolling decisions that will not change the header
515a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * size (ie, snapping to the bottom or top). When mIsOpenContactSquare is
516a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * true, this function considers mIntermediateHeaderHeight == mMaximumHeaderHeight,
517a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * since snapping decisions will be made relative the full header size when
518a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * mIsOpenContactSquare = true. This value should never be used in conjunction
519a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * with {@link #getScroll} values.
520a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
521a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int getScrollIgnoreOversizedHeaderForSnapping() {
522a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return Math.max(getMaximumScrollableHeaderHeight() - getToolbarHeight(), 0)
523a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                + mScrollView.getScrollY();
524a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
525a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
526a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
527a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Return amount of scrolling needed in order for all the visible
528a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * subviews to scroll off the bottom.
529a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
530a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int getScrollUntilOffBottom() {
531a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return getHeight() + getScrollIgnoreOversizedHeaderForSnapping();
532a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
533a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
534a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    @Override
535a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void computeScroll() {
536a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mScroller.computeScrollOffset()) {
537a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // Examine the fling results in order to activate EdgeEffect when we
538a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // fling to the end.
539a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            final int oldScroll = getScroll();
540a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            scrollTo(0, mScroller.getCurrY());
541a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            final int delta = mScroller.getCurrY() - oldScroll;
542a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            final int distanceFromMaxScrolling = getMaximumScrollUpwards() - getScroll();
543a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (delta > distanceFromMaxScrolling && distanceFromMaxScrolling > 0) {
544a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mEdgeGlowBottom.onAbsorb((int) mScroller.getCurrVelocity());
545a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
546a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
547a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (!awakenScrollBars()) {
548a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // Keep on drawing until the animation has finished.
549a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                postInvalidateOnAnimation();
550a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
551a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (mScroller.getCurrY() >= getMaximumScrollUpwards()) {
552a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mScroller.abortAnimation();
553a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
554a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
555a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
556a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
557a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    @Override
558a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void draw(Canvas canvas) {
559a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        super.draw(canvas);
560a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
561a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (!mEdgeGlowBottom.isFinished()) {
562a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            final int restoreCount = canvas.save();
563a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            final int width = getWidth() - getPaddingLeft() - getPaddingRight();
564a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            final int height = getHeight();
565a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
566a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // Draw the EdgeEffect on the bottom of the Window (Or a little bit
567a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // below the bottom
568a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // of the Window if we start to scroll upwards while EdgeEffect is
569a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // visible). This
570a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // does not need to consider the case where this MultiShrinkScroller
571a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // doesn't fill
572a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // the Window, since the nested ScrollView should be set to
573a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // fillViewport.
574a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            canvas.translate(-width + getPaddingLeft(), height + getMaximumScrollUpwards()
575a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    - getScroll());
576a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
577a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            canvas.rotate(180, width, 0);
578a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mEdgeGlowBottom.setSize(width, height);
579a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (mEdgeGlowBottom.draw(canvas)) {
580a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                postInvalidateOnAnimation();
581a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
582a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            canvas.restoreToCount(restoreCount);
583a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
584a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
585a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
586a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private float getCurrentVelocity() {
587a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mVelocityTracker == null) {
588a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return 0;
589a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
590a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mVelocityTracker.computeCurrentVelocity(PIXELS_PER_SECOND, mMaximumVelocity);
591a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return mVelocityTracker.getYVelocity();
592a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
593a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
594a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void fling(float velocity) {
595a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // For reasons I do not understand, scrolling is less janky when
596a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // maxY=Integer.MAX_VALUE
597a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // then when maxY is set to an actual value.
598a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mScroller.fling(0, getScroll(), 0, (int) velocity, 0, 0, -Integer.MAX_VALUE,
599a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                Integer.MAX_VALUE);
600a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        invalidate();
601a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
602a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
603a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int getMaximumScrollUpwards() {
604a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return // How much the Header view can compress
605a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        getMaximumScrollableHeaderHeight() - getFullyCompressedHeaderHeight()
606a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // How much the ScrollView can scroll. 0, if child is
607a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // smaller than ScrollView.
608a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                + Math.max(0, mScrollViewChild.getHeight() - getHeight()
609a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        + getFullyCompressedHeaderHeight());
610a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
611a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
612a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void scrollUp(int delta) {
613a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final ViewGroup.LayoutParams toolbarLayoutParams = mHeader.getLayoutParams();
614a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (toolbarLayoutParams.height > getFullyCompressedHeaderHeight()) {
615a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            final int originalValue = toolbarLayoutParams.height;
616a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            toolbarLayoutParams.height -= delta;
617a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            toolbarLayoutParams.height = Math.max(toolbarLayoutParams.height,
618a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    getFullyCompressedHeaderHeight());
619a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mHeader.setLayoutParams(toolbarLayoutParams);
620a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            delta -= originalValue - toolbarLayoutParams.height;
621a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
622a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mScrollView.scrollBy(0, delta);
623a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
624a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
625a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
626a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Returns the minimum size that we want to compress the header to,
627a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * given that we don't want to allow the the ScrollView to scroll
628a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * unless there is new content off of the edge of ScrollView.
629a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
630a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int getFullyCompressedHeaderHeight() {
631a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int height = Math.min(Math.max(mHeader.getLayoutParams().height
632a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                - getOverflowingChildViewSize(), mMinimumHeaderHeight),
633a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                getMaximumScrollableHeaderHeight());
634a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return height;
635a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
636a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
637a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
638a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Returns the amount of mScrollViewChild that doesn't fit inside its parent. Outside size
639a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
640a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int getOverflowingChildViewSize() {
641a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final int usedScrollViewSpace = mScrollViewChild.getHeight();
642a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return -getHeight() + usedScrollViewSpace + mHeader.getLayoutParams().height;
643a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
644a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
645a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void scrollDown(int delta) {
646a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mScrollView.getScrollY() > 0) {
647a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            final int originalValue = mScrollView.getScrollY();
648a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mScrollView.scrollBy(0, delta);
649a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
650a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
651a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
652a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void updateHeaderTextAndButton() {
653a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mAdjuster.handleScroll();
654a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
655a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
656a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void updateLastEventPosition(MotionEvent event) {
657a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mLastEventPosition[0] = event.getX();
658a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mLastEventPosition[1] = event.getY();
659a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
660a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
661a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean motionShouldStartDrag(MotionEvent event) {
662a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final float deltaX = event.getX() - mLastEventPosition[0];
663a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final float deltaY = event.getY() - mLastEventPosition[1];
664a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final boolean draggedX = (deltaX > mTouchSlop || deltaX < -mTouchSlop);
665a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final boolean draggedY = (deltaY > mTouchSlop || deltaY < -mTouchSlop);
666a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return draggedY && !draggedX;
667a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
668a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
669a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private float updatePositionAndComputeDelta(MotionEvent event) {
670a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final int vertical = 1;
671a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        final float position = mLastEventPosition[vertical];
672a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        updateLastEventPosition(event);
673a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return position - mLastEventPosition[vertical];
674a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
675a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
676a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
677a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Interpolator that enforces a specific starting velocity.
678a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * This is useful to avoid a discontinuity between dragging
679a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * speed and flinging speed. Similar to a
680a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * {@link android.view.animation.AccelerateInterpolator} in
681a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * the sense that getInterpolation() is a quadratic function.
682a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
683a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private static class AcceleratingFlingInterpolator implements Interpolator {
684a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
685a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private final float mStartingSpeedPixelsPerFrame;
686a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
687a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private final float mDurationMs;
688a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
689a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private final int mPixelsDelta;
690a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
691a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private final float mNumberFrames;
692a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
693a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        public AcceleratingFlingInterpolator(int durationMs, float startingSpeedPixelsPerSecond,
694a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                int pixelsDelta) {
695a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mStartingSpeedPixelsPerFrame = startingSpeedPixelsPerSecond / getRefreshRate();
696a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mDurationMs = durationMs;
697a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mPixelsDelta = pixelsDelta;
698a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mNumberFrames = mDurationMs / getFrameIntervalMs();
699a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
700a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
701a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        @Override
702a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        public float getInterpolation(float input) {
703a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            final float animationIntervalNumber = mNumberFrames * input;
704a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            final float linearDelta = (animationIntervalNumber * mStartingSpeedPixelsPerFrame)
705a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    / mPixelsDelta;
706a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // Add the results of a linear interpolator (with the initial speed)
707a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // with the
708a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // results of a AccelerateInterpolator.
709a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (mStartingSpeedPixelsPerFrame > 0) {
710a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                return Math.min(input * input + linearDelta, 1);
711a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            } else {
712a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // Initial fling was in the wrong direction, make sure that the
713a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // quadratic component
714a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // grows faster in order to make up for this.
715a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                return Math.min(input * (input - linearDelta) + linearDelta, 1);
716a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
717a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
718a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
719a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private float getRefreshRate() {
720a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            DisplayInfo di = DisplayManagerGlobal.getInstance().getDisplayInfo(
721a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    Display.DEFAULT_DISPLAY);
722be90b9dd03b98eacd3801018cc23555b9eeeef98P.Y. Laligand            return di.getMode().getRefreshRate();
723a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
724a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
725a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        public long getFrameIntervalMs() {
726a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return (long) (1000 / getRefreshRate());
727a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
728a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
729a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
730a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int getMaxHeight(int state) {
731a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int height = 0;
732a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        switch (state) {
733a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            case STATE_NO_FAVORITE:
734a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                height = getHeight();
735a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                break;
736a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            case STATE_HAS_FAVORITE:
737a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                height = (int) getResources().getDimension(R.dimen.fm_main_header_big);
738a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                break;
739a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            default:
740a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                break;
741a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
742a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return height;
743a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
744a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
745a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private int getMinHeight(int state) {
746a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        int height = 0;
747a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        switch (state) {
748a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            case STATE_NO_FAVORITE:
749a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                height = (int) getResources().getDimension(R.dimen.fm_main_header_big);
750a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                break;
751a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            case STATE_HAS_FAVORITE:
752a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                height = (int) getResources().getDimension(R.dimen.fm_main_header_small);
753a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                break;
754a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            default:
755a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                break;
756a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
757a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return height;
758a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
759a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
760a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void setMinHeight(int height) {
761a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mMinimumHeaderHeight = height;
762a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
763a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
764a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    class FavoriteAdapter extends BaseAdapter {
765a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private Cursor mCursor;
766a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
767a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private LayoutInflater mInflater;
768a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
769a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        public FavoriteAdapter(Context context) {
770a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mInflater = LayoutInflater.from(context);
771a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
772a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
773a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        public int getFrequency(int position) {
774a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (mCursor != null && mCursor.moveToFirst()) {
775a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mCursor.moveToPosition(position);
776a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                return mCursor.getInt(mCursor.getColumnIndex(FmStation.Station.FREQUENCY));
777a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
778a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return 0;
779a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
780a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
781a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        public void swipResult(Cursor cursor) {
782a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (null != mCursor) {
783a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mCursor.close();
784a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
785a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mCursor = cursor;
786a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            notifyDataSetChanged();
787a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
788a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
789a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        @Override
790a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        public int getCount() {
791a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (null != mCursor) {
792a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                return mCursor.getCount();
793a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
794a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return 0;
795a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
796a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
797a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        @Override
798a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        public Object getItem(int position) {
799a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return null;
800a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
801a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
802a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        @Override
803a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        public long getItemId(int position) {
804a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return 0;
805a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
806a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
807a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        @Override
808a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        public View getView(int position, View convertView, ViewGroup parent) {
809a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            ViewHolder viewHolder = null;
810a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (null == convertView) {
811a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                viewHolder = new ViewHolder();
812a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                convertView = mInflater.inflate(R.layout.favorite_gridview_item, null);
813a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                viewHolder.mStationFreq = (TextView) convertView.findViewById(R.id.station_freq);
814a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                viewHolder.mPlayIndicator = (FmVisualizerView) convertView
815a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .findViewById(R.id.fm_play_indicator);
816a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                viewHolder.mStationName = (TextView) convertView.findViewById(R.id.station_name);
817a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                viewHolder.mMoreButton = (ImageView) convertView.findViewById(R.id.station_more);
818a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                viewHolder.mPopupMenuAnchor = convertView.findViewById(R.id.popupmenu_anchor);
819a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                convertView.setTag(viewHolder);
820a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            } else {
821a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                viewHolder = (ViewHolder) convertView.getTag();
822a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
823a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
824a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (mCursor != null && mCursor.moveToPosition(position)) {
825a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                final int stationFreq = mCursor.getInt(mCursor
826a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getColumnIndex(FmStation.Station.FREQUENCY));
827a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                String name = mCursor.getString(mCursor
828a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getColumnIndex(FmStation.Station.STATION_NAME));
829a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                String rds = mCursor.getString(mCursor
830a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getColumnIndex(FmStation.Station.RADIO_TEXT));
831a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                final int isFavorite = mCursor.getInt(mCursor
832a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getColumnIndex(FmStation.Station.IS_FAVORITE));
833a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
834a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (null == name || "".equals(name)) {
835a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    name = mCursor.getString(mCursor
836a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            .getColumnIndex(FmStation.Station.PROGRAM_SERVICE));
837a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
838a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (null == name || "".equals(name)) {
839a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    name = "";
840a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
841a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
842a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                viewHolder.mStationFreq.setText(FmUtils.formatStation(stationFreq));
843a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                viewHolder.mStationName.setText(name);
844a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
845a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (mCurrentStation == stationFreq) {
846a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    viewHolder.mPlayIndicator.setVisibility(View.VISIBLE);
847a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    if (mIsFmPlaying) {
848a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        viewHolder.mPlayIndicator.startAnimation();
849a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    } else {
850a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        viewHolder.mPlayIndicator.stopAnimation();
851a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
852a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    viewHolder.mStationFreq.setTextColor(Color.parseColor("#607D8B"));
853a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    viewHolder.mStationFreq.setAlpha(1f);
854a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    viewHolder.mStationName.setMaxLines(1);
855a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                } else {
856a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    viewHolder.mPlayIndicator.setVisibility(View.GONE);
857a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    viewHolder.mPlayIndicator.stopAnimation();
858a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    viewHolder.mStationFreq.setTextColor(Color.parseColor("#000000"));
859a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    viewHolder.mStationFreq.setAlpha(0.87f);
860a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    viewHolder.mStationName.setMaxLines(2);
861a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
862a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
863a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                viewHolder.mMoreButton.setTag(viewHolder.mPopupMenuAnchor);
864a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                viewHolder.mMoreButton.setOnClickListener(new OnClickListener() {
865a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    @Override
866a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    public void onClick(View v) {
867a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        // Use anchor view to fix PopupMenu postion and cover more button
868a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        View anchor = v;
869a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        if (v.getTag() != null) {
870a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            anchor = (View) v.getTag();
871a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        }
872a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        showPopupMenu(anchor, stationFreq);
873a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
874a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                });
875a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
876a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
877a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return convertView;
878a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
879a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
880a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
881a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private Cursor getData() {
882a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Cursor cursor = getContext().getContentResolver().query(Station.CONTENT_URI,
883a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                FmStation.COLUMNS, mSelection, mSelectionArgs,
884a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                FmStation.Station.FREQUENCY);
885a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        return cursor;
886a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
887a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
888a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
889a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Called when FmRadioActivity.onResume(), refresh layout
890a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
891a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void onResume() {
892a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Cursor c = getData();
893a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mAdapter.swipResult(c);
894a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mFirstOnResume) {
895a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mFirstOnResume = false;
896a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        } else {
897a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            refreshStateHeight();
898a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            updateHeaderTextAndButton();
899a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            refreshFavoriteLayout();
900a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
901a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            int curOrientation = getResources().getConfiguration().orientation;
902a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            final boolean isLandscape = curOrientation == Configuration.ORIENTATION_LANDSCAPE;
903a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            int columnNum = isLandscape ? LAND_COLUMN_NUM : PORT_COLUMN_NUM;
904a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            boolean isOneRow = c.getCount() <= columnNum;
905a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
906a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            boolean hasFavoriteCurrent = c.getCount() > 0;
907a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (mHasFavoriteWhenOnPause != hasFavoriteCurrent || isOneRow) {
908a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                setHeaderHeight(getMaximumScrollableHeaderHeight());
909a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
910a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
911a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
912a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
913a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private boolean mHasFavoriteWhenOnPause = false;
914a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
915a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
916a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Called when FmRadioActivity.onPause()
917a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
918a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void onPause() {
919a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mAdapter != null && mAdapter.getCount() > 0) {
920a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mHasFavoriteWhenOnPause = true;
921a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        } else {
922a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mHasFavoriteWhenOnPause = false;
923a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
924a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
925a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
926a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
927a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Notify refresh adapter when data change
928a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
929a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void notifyAdatperChange() {
930a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Cursor c = getData();
931a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mAdapter.swipResult(c);
932a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
933a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
934a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void refreshStateHeight() {
935a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mAdapter != null && mAdapter.getCount() > 0) {
936a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mMaximumHeaderHeight = getMaxHeight(STATE_HAS_FAVORITE);
937a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mMinimumHeaderHeight = getMinHeight(STATE_HAS_FAVORITE);
938a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        } else {
939a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mMaximumHeaderHeight = getMaxHeight(STATE_NO_FAVORITE);
940a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mMinimumHeaderHeight = getMinHeight(STATE_NO_FAVORITE);
941a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
942a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
943a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
944a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
945a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Called when add a favorite
946a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
947a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void onAddFavorite() {
948a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Cursor c = getData();
949a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mAdapter.swipResult(c);
950a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        refreshFavoriteLayout();
951a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (c.getCount() == 1) {
952a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // Last time count is 0, so need set STATE_NO_FAVORITE then collapse header
953a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mMinimumHeaderHeight = getMinHeight(STATE_NO_FAVORITE);
954a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mMaximumHeaderHeight = getMaxHeight(STATE_NO_FAVORITE);
955a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            collapseHeader();
956a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
957a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
958a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
959a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
960a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Called when remove a favorite
961a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
962a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void onRemoveFavorite() {
963a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Cursor c = getData();
964a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mAdapter.swipResult(c);
965a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        refreshFavoriteLayout();
966a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (c != null && c.getCount() == 0) {
967a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // Stop the play animation
968a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mMainHandler.removeCallbacks(null);
969a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
970a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // Last time count is 1, so need set STATE_NO_FAVORITE then expand header
971a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mMinimumHeaderHeight = getMinHeight(STATE_NO_FAVORITE);
972a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mMaximumHeaderHeight = getMaxHeight(STATE_NO_FAVORITE);
973a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            expandHeader();
974a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
975a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
976a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
977a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void showPopupMenu(View anchor, final int frequency) {
978a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        dismissPopupMenu();
979a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mPopupMenu = new PopupMenu(getContext(), anchor);
980a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        Menu menu = mPopupMenu.getMenu();
981a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mPopupMenu.getMenuInflater().inflate(R.menu.gridview_item_more_menu, menu);
982a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mPopupMenu.setOnMenuItemClickListener(new OnMenuItemClickListener() {
983a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            @Override
984a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            public boolean onMenuItemClick(MenuItem item) {
985a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                switch (item.getItemId()) {
986a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    case R.id.remove_favorite:
987a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        if (mEventListener != null) {
988a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            mEventListener.onRemoveFavorite(frequency);
989a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        }
990a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        break;
991a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    case R.id.rename:
992a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        if (mEventListener != null) {
993a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            mEventListener.onRename(frequency);
994a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        }
995a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        break;
996a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    default:
997a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        break;
998a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
999a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                return false;
1000a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
1001a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        });
1002a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mPopupMenu.show();
1003a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1004a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1005a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private void dismissPopupMenu() {
1006a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mPopupMenu != null) {
1007a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mPopupMenu.dismiss();
1008a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mPopupMenu = null;
1009a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1010a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1011a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1012a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1013a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Called when FmRadioActivity.onDestory()
1014a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1015a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void closeAdapterCursor() {
1016a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mAdapter.swipResult(null);
1017a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1018a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1019a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1020a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Register a listener for GridView item event
1021a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
1022a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param listener The event listener
1023a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1024a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void registerListener(EventListener listener) {
1025a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mEventListener = listener;
1026a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1027a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1028a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1029a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Unregister a listener for GridView item event
1030a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
1031a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param listener The event listener
1032a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1033a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void unregisterListener(EventListener listener) {
1034a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mEventListener = null;
1035a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1036a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1037a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1038a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Listen for GridView item event: remove, rename, click play
1039a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1040a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public interface EventListener {
1041a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        /**
1042a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         * Callback when click remove favorite menu
1043a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         *
1044a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         * @param frequency The frequency want to remove
1045a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         */
1046a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        void onRemoveFavorite(int frequency);
1047a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1048a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        /**
1049a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         * Callback when click rename favorite menu
1050a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         *
1051a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         * @param frequency The frequency want to rename
1052a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         */
1053a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        void onRename(int frequency);
1054a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1055a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        /**
1056a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         * Callback when click gridview item to play
1057a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         *
1058a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         * @param frequency The frequency want to play
1059a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         */
1060a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        void onPlay(int frequency);
1061a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1062a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1063a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1064a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Refresh the play indicator in gridview when play station or play state change
1065a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     *
1066a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param currentStation current station
1067a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * @param isFmPlaying whether fm is playing
1068a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1069a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    public void refreshPlayIndicator(int currentStation, boolean isFmPlaying) {
1070a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mCurrentStation = currentStation;
1071a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        mIsFmPlaying = isFmPlaying;
1072a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        if (mAdapter != null) {
1073a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mAdapter.notifyDataSetChanged();
1074a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1075a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1076a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1077a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    /**
1078a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     * Adjust view padding and text size when scroll
1079a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang     */
1080a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private class Adjuster {
1081a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private final DisplayMetrics mDisplayMetrics;
1082a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1083a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private final int mFirstTargetHeight;
1084a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1085a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private final int mSecondTargetHeight;
1086a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1087a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private final int mActionBarHeight = mActionBarSize;
1088a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1089a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private final int mStatusBarHeight;
1090a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1091a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private final int mFullHeight;// display height without status bar
1092a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1093a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private final float mDensity;
1094a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1095a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private final Typeface mDefaultFrequencyTypeface;
1096a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1097a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        // Text view
1098a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private TextView mFrequencyText;
1099a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
110040cbbc57444d732004bba3c8464c979f83290279Benson Huang        private TextView mFmDescriptionText;
1101a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1102a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private TextView mStationNameText;
1103a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1104a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private TextView mStationRdsText;
1105a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1106a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        /*
1107a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         * The five control buttons view(previous, next, increase,
1108a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         * decrease, favorite) and stop button
1109a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang         */
1110a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private View mControlView;
1111a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1112a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private View mPlayButtonView;
1113a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1114a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private final Context mContext;
1115a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1116a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private final boolean mIsLandscape;
1117a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1118a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private FirstRangeAdjuster mFirstRangeAdjuster;
1119a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1120a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private SecondRangeAdjuster mSecondRangeAdjusterr;
1121a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1122a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        public Adjuster(Context context) {
1123a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mContext = context;
1124a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mDisplayMetrics = mContext.getResources().getDisplayMetrics();
1125a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mDensity = mDisplayMetrics.density;
1126a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            int curOrientation = getResources().getConfiguration().orientation;
1127a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mIsLandscape = curOrientation == Configuration.ORIENTATION_LANDSCAPE;
1128a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            Resources res = mContext.getResources();
1129a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mFirstTargetHeight = res.getDimensionPixelSize(R.dimen.fm_main_header_big);
1130a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mSecondTargetHeight = res.getDimensionPixelSize(R.dimen.fm_main_header_small);
1131a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mStatusBarHeight = res
1132a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    .getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
1133a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mFullHeight = mDisplayMetrics.heightPixels - mStatusBarHeight;
1134a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1135a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mFrequencyText = (TextView) findViewById(R.id.station_value);
113640cbbc57444d732004bba3c8464c979f83290279Benson Huang            mFmDescriptionText = (TextView) findViewById(R.id.text_fm);
1137a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mStationNameText = (TextView) findViewById(R.id.station_name);
1138a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mStationRdsText = (TextView) findViewById(R.id.station_rds);
1139a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mControlView = findViewById(R.id.rl_imgbtnpart);
1140a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mPlayButtonView = findViewById(R.id.play_button_container);
1141a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1142a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mFirstRangeAdjuster = new FirstRangeAdjuster();
1143a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mSecondRangeAdjusterr = new SecondRangeAdjuster();
1144a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mControlView.setMinimumWidth(mIsLandscape ? mDisplayMetrics.heightPixels
1145a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    : mDisplayMetrics.widthPixels);
1146a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mDefaultFrequencyTypeface = mFrequencyText.getTypeface();
1147a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1148a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1149a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        public void handleScroll() {
1150a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            int height = getHeaderHeight();
1151a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (mIsLandscape || height > mFirstTargetHeight) {
1152a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mFirstRangeAdjuster.handleScroll();
1153a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            } else if (height >= mSecondTargetHeight) {
1154a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mSecondRangeAdjusterr.handleScroll();
1155a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
1156a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1157a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1158a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private class FirstRangeAdjuster {
1159a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            protected int mTargetHeight;
1160a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1161a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // start text size and margin
116240cbbc57444d732004bba3c8464c979f83290279Benson Huang            protected float mFmDescriptionTextSizeStart;
116340cbbc57444d732004bba3c8464c979f83290279Benson Huang
1164a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            protected float mFrequencyStartTextSize;
1165a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1166a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            protected float mStationNameTextSizeStart;
1167a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
116840cbbc57444d732004bba3c8464c979f83290279Benson Huang            protected float mFmDescriptionMarginTopStart;
116940cbbc57444d732004bba3c8464c979f83290279Benson Huang
117040cbbc57444d732004bba3c8464c979f83290279Benson Huang            protected float mFmDescriptionStartPaddingLeft;
1171a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1172a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            protected float mFrequencyMarginTopStart;
1173a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1174a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            protected float mStationNameMarginTopStart;
1175a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1176a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            protected float mStationRdsMarginTopStart;
1177a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1178a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            protected float mControlViewMarginTopStart;
1179a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1180a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // target text size and margin
118140cbbc57444d732004bba3c8464c979f83290279Benson Huang            protected float mFmDescriptionTextSizeTarget;
118240cbbc57444d732004bba3c8464c979f83290279Benson Huang
1183a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            protected float mFrequencyTextSizeTarget;
1184a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1185a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            protected float mStationNameTextSizeTarget;
1186a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
118740cbbc57444d732004bba3c8464c979f83290279Benson Huang            protected float mFmDescriptionMarginTopTarget;
1188a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1189a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            protected float mFrequencyMarginTopTarget;
1190a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1191a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            protected float mStationNameMarginTopTarget;
1192a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1193a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            protected float mStationRdsMarginTopTarget;
1194a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1195a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            protected float mControlViewMarginTopTarget;
1196a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1197a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            protected float mPlayButtonMarginTopStart;
1198a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1199a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            protected float mPlayButtonMarginTopTarget;
1200a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1201a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            protected float mPlayButtonHeight;
1202a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1203a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // Padding adjust rate as linear
120440cbbc57444d732004bba3c8464c979f83290279Benson Huang            protected float mFmDescriptionPaddingRate;
1205a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1206a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            protected float mFrequencyPaddingRate;
1207a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1208a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            protected float mStationNamePaddingRate;
1209a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1210a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            protected float mStationRdsPaddingRate;
1211a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1212a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            protected float mControlViewPaddingRate;
1213a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1214a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // init it with display height
1215a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            protected float mPlayButtonPaddingRate;
1216a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1217a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // Text size adjust rate as linear
1218a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // adjust from first to target critical height
121940cbbc57444d732004bba3c8464c979f83290279Benson Huang            protected float mFmDescriptionTextSizeRate;
122040cbbc57444d732004bba3c8464c979f83290279Benson Huang
1221a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            protected float mFrequencyTextSizeRate;
1222a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1223a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            // adjust before first critical height
1224a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            protected float mStationNameTextSizeRate;
1225a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1226a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            public FirstRangeAdjuster() {
1227a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                Resources res = mContext.getResources();
1228a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mTargetHeight = mFirstTargetHeight;
1229a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // init start
123040cbbc57444d732004bba3c8464c979f83290279Benson Huang                mFmDescriptionTextSizeStart = res.getDimension(R.dimen.fm_description_text_size);
1231a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mFrequencyStartTextSize = res.getDimension(R.dimen.fm_frequency_text_size_start);
1232a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mStationNameTextSizeStart = res
1233a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_station_name_text_size_start);
1234a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // first view, margin refer to parent
123540cbbc57444d732004bba3c8464c979f83290279Benson Huang                mFmDescriptionMarginTopStart = res
1236a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_description_margin_top_start) + mActionBarHeight;
1237a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mFrequencyMarginTopStart = res.getDimension(R.dimen.fm_frequency_margin_top_start);
1238a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mStationNameMarginTopStart = res
1239a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_station_name_margin_top_start);
1240a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mStationRdsMarginTopStart = res
1241a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_station_rds_margin_top_start);
1242a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mControlViewMarginTopStart = res
1243a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_control_buttons_margin_top_start);
1244a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // init target
1245a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mFrequencyTextSizeTarget = res
1246a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_frequency_text_size_first_target);
124740cbbc57444d732004bba3c8464c979f83290279Benson Huang                mFmDescriptionTextSizeTarget = mFrequencyTextSizeTarget;
1248a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mStationNameTextSizeTarget = res
1249a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_station_name_text_size_first_target);
125040cbbc57444d732004bba3c8464c979f83290279Benson Huang                mFmDescriptionMarginTopTarget = res
1251a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_description_margin_top_first_target);
125240cbbc57444d732004bba3c8464c979f83290279Benson Huang                mFmDescriptionStartPaddingLeft = mFrequencyText.getPaddingLeft();
1253a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // first view, margin refer to parent if not in landscape
1254a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (!mIsLandscape) {
125540cbbc57444d732004bba3c8464c979f83290279Benson Huang                    mFmDescriptionMarginTopTarget += mActionBarHeight;
125640cbbc57444d732004bba3c8464c979f83290279Benson Huang                } else {
125740cbbc57444d732004bba3c8464c979f83290279Benson Huang                    mFrequencyMarginTopStart += mActionBarHeight + mFmDescriptionTextSizeStart;
1258a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
1259a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mFrequencyMarginTopTarget = res
1260a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_frequency_margin_top_first_target);
1261a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mStationNameMarginTopTarget = res
1262a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_station_name_margin_top_first_target);
1263a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mStationRdsMarginTopTarget = res
1264a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_station_rds_margin_top_first_target);
1265a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mControlViewMarginTopTarget = res
1266a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_control_buttons_margin_top_first_target);
1267a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // init text size and margin adjust rate
1268a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                int scrollHeight = mFullHeight - mTargetHeight;
126940cbbc57444d732004bba3c8464c979f83290279Benson Huang                mFmDescriptionTextSizeRate =
127040cbbc57444d732004bba3c8464c979f83290279Benson Huang                        (mFmDescriptionTextSizeStart - mFmDescriptionTextSizeTarget) / scrollHeight;
1271a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mFrequencyTextSizeRate = (mFrequencyStartTextSize - mFrequencyTextSizeTarget)
1272a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        / scrollHeight;
1273a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mStationNameTextSizeRate = (mStationNameTextSizeStart - mStationNameTextSizeTarget)
1274a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        / scrollHeight;
127540cbbc57444d732004bba3c8464c979f83290279Benson Huang                mFmDescriptionPaddingRate =
127640cbbc57444d732004bba3c8464c979f83290279Benson Huang                        (mFmDescriptionMarginTopStart - mFmDescriptionMarginTopTarget)
1277a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        / scrollHeight;
1278a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mFrequencyPaddingRate = (mFrequencyMarginTopStart - mFrequencyMarginTopTarget)
1279a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        / scrollHeight;
1280a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mStationNamePaddingRate = (mStationNameMarginTopStart - mStationNameMarginTopTarget)
1281a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        / scrollHeight;
1282a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mStationRdsPaddingRate = (mStationRdsMarginTopStart - mStationRdsMarginTopTarget)
1283a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        / scrollHeight;
1284a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mControlViewPaddingRate = (mControlViewMarginTopStart - mControlViewMarginTopTarget)
1285a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        / scrollHeight;
1286a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // init play button padding, it different to others, padding top refer to parent
1287a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mPlayButtonHeight = res.getDimension(R.dimen.play_button_height);
1288a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mPlayButtonMarginTopStart = mFullHeight - mPlayButtonHeight - 16 * mDensity;
1289a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mPlayButtonMarginTopTarget = mFirstTargetHeight - mPlayButtonHeight / 2;
1290a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mPlayButtonPaddingRate = (mPlayButtonMarginTopStart - mPlayButtonMarginTopTarget)
1291a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        / scrollHeight;
1292a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
1293a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1294a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            public void handleScroll() {
1295a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (mIsLandscape) {
1296a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    handleScrollLandscapeMode();
1297a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    return;
1298a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
1299a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                int currentHeight = getHeaderHeight();
1300a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                float newMargin = 0;
1301a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                float lastHeight = 0;
1302a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                float newTextSize;
1303a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // 1.FM description (margin)
130440cbbc57444d732004bba3c8464c979f83290279Benson Huang                newMargin = getNewSize(currentHeight, mTargetHeight, mFmDescriptionMarginTopTarget,
130540cbbc57444d732004bba3c8464c979f83290279Benson Huang                        mFmDescriptionPaddingRate);
130640cbbc57444d732004bba3c8464c979f83290279Benson Huang                lastHeight = setNewPadding(mFmDescriptionText, newMargin);
1307a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // 2. frequency text (text size and margin)
1308a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                newTextSize = getNewSize(currentHeight, mTargetHeight, mFrequencyTextSizeTarget,
1309a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mFrequencyTextSizeRate);
1310a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mFrequencyText.setTextSize(newTextSize / mDensity);
1311a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                newMargin = getNewSize(currentHeight, mTargetHeight, mFrequencyMarginTopTarget,
1312a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mFrequencyPaddingRate);
1313a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                lastHeight = setNewPadding(mFrequencyText, newMargin + lastHeight);
1314a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // 3. station name (margin and text size)
1315a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                newMargin = getNewSize(currentHeight, mTargetHeight, mStationNameMarginTopTarget,
1316a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mStationNamePaddingRate);
1317a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                lastHeight = setNewPadding(mStationNameText, newMargin + lastHeight);
1318a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                newTextSize = getNewSize(currentHeight, mTargetHeight, mStationNameTextSizeTarget,
1319a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mStationNameTextSizeRate);
1320a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mStationNameText.setTextSize(newTextSize / mDensity);
1321a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // 4. station rds (margin)
1322a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                newMargin = getNewSize(currentHeight, mTargetHeight, mStationRdsMarginTopTarget,
1323a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mStationRdsPaddingRate);
1324a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                lastHeight = setNewPadding(mStationRdsText, newMargin + lastHeight);
1325a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // 5. control buttons (margin)
1326a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                newMargin = getNewSize(currentHeight, mTargetHeight, mControlViewMarginTopTarget,
1327a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mControlViewPaddingRate);
1328a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                setNewPadding(mControlView, newMargin + lastHeight);
1329a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // 6. stop button (padding), it different to others, padding top refer to parent
1330a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                newMargin = getNewSize(currentHeight, mTargetHeight, mPlayButtonMarginTopTarget,
1331a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mPlayButtonPaddingRate);
1332a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                setNewPadding(mPlayButtonView, newMargin);
1333a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
1334a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1335a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            private void handleScrollLandscapeMode() {
1336a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                int currentHeight = getHeaderHeight();
1337a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                float newMargin = 0;
1338a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                float lastHeight = 0;
1339a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                float newTextSize;
134040cbbc57444d732004bba3c8464c979f83290279Benson Huang                // 1. FM description (color, alpha and margin)
134140cbbc57444d732004bba3c8464c979f83290279Benson Huang                newMargin = getNewSize(currentHeight, mTargetHeight, mFmDescriptionMarginTopTarget,
134240cbbc57444d732004bba3c8464c979f83290279Benson Huang                        mFmDescriptionPaddingRate);
134340cbbc57444d732004bba3c8464c979f83290279Benson Huang                setNewPadding(mFmDescriptionText, newMargin);
134440cbbc57444d732004bba3c8464c979f83290279Benson Huang
134540cbbc57444d732004bba3c8464c979f83290279Benson Huang                newTextSize = getNewSize(currentHeight, mTargetHeight, mFmDescriptionTextSizeTarget,
134640cbbc57444d732004bba3c8464c979f83290279Benson Huang                        mFmDescriptionTextSizeRate);
134740cbbc57444d732004bba3c8464c979f83290279Benson Huang                mFmDescriptionText.setTextSize(newTextSize / mDensity);
134840cbbc57444d732004bba3c8464c979f83290279Benson Huang                boolean reachTop = (mSecondTargetHeight == getHeaderHeight());
134940cbbc57444d732004bba3c8464c979f83290279Benson Huang                mFmDescriptionText.setTextColor(reachTop ? Color.WHITE
135040cbbc57444d732004bba3c8464c979f83290279Benson Huang                        : getResources().getColor(R.color.text_fm_color));
135140cbbc57444d732004bba3c8464c979f83290279Benson Huang                mFmDescriptionText.setAlpha(reachTop ? 0.87f : 1.0f);
135240cbbc57444d732004bba3c8464c979f83290279Benson Huang
135340cbbc57444d732004bba3c8464c979f83290279Benson Huang                // 2. frequency text (text size, padding and margin)
1354a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                newTextSize = getNewSize(currentHeight, mTargetHeight, mFrequencyTextSizeTarget,
1355a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mFrequencyTextSizeRate);
1356a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mFrequencyText.setTextSize(newTextSize / mDensity);
1357a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                newMargin = getNewSize(currentHeight, mTargetHeight, mFrequencyMarginTopTarget,
1358a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mFrequencyPaddingRate);
135940cbbc57444d732004bba3c8464c979f83290279Benson Huang                // Move frequency text like "103.7" from middle to action bar in landscape,
136040cbbc57444d732004bba3c8464c979f83290279Benson Huang                // or opposite direction. For example:
136140cbbc57444d732004bba3c8464c979f83290279Benson Huang                // *************************          *************************
136240cbbc57444d732004bba3c8464c979f83290279Benson Huang                // *                       *          * FM 103.7              *
136340cbbc57444d732004bba3c8464c979f83290279Benson Huang                // * FM                    *   <-->   *                       *
136440cbbc57444d732004bba3c8464c979f83290279Benson Huang                // * 103.7                 *          *                       *
136540cbbc57444d732004bba3c8464c979f83290279Benson Huang                // *************************          *************************
136640cbbc57444d732004bba3c8464c979f83290279Benson Huang                // "FM", "103.7" and other subviews are in a RelativeLayout (id actionbar_parent)
136740cbbc57444d732004bba3c8464c979f83290279Benson Huang                // in main_header.xml. The position is controlled by the padding of each subview.
136840cbbc57444d732004bba3c8464c979f83290279Benson Huang                // Because "FM" and "103.7" move up, we need to change the padding top and change
136940cbbc57444d732004bba3c8464c979f83290279Benson Huang                // the padding left of "103.7".
137040cbbc57444d732004bba3c8464c979f83290279Benson Huang                // The padding between "FM" and "103.7" is 0.2 (e.g. paddingRate) times
137140cbbc57444d732004bba3c8464c979f83290279Benson Huang                // the length of "FM" string length.
137240cbbc57444d732004bba3c8464c979f83290279Benson Huang                float paddingRate = 0.2f;
137340cbbc57444d732004bba3c8464c979f83290279Benson Huang                float addPadding = (((1 + paddingRate) * computeFmDescriptionWidth())
137440cbbc57444d732004bba3c8464c979f83290279Benson Huang                        * (mFullHeight - currentHeight)) / (mFullHeight - mTargetHeight);
137540cbbc57444d732004bba3c8464c979f83290279Benson Huang                mFrequencyText.setPadding((int) (addPadding + mFmDescriptionStartPaddingLeft),
137640cbbc57444d732004bba3c8464c979f83290279Benson Huang                        (int) (newMargin), mFrequencyText.getPaddingRight(),
137740cbbc57444d732004bba3c8464c979f83290279Benson Huang                        mFrequencyText.getPaddingBottom());
137840cbbc57444d732004bba3c8464c979f83290279Benson Huang                lastHeight = newMargin + lastHeight + mFrequencyText.getTextSize();
1379a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // If frequency text move to action bar, change it to bold
1380a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                setNewTypefaceForFrequencyText();
138140cbbc57444d732004bba3c8464c979f83290279Benson Huang
1382a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // 3. station name (text size and margin)
1383a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                newTextSize = getNewSize(currentHeight, mTargetHeight, mStationNameTextSizeTarget,
1384a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mStationNameTextSizeRate);
1385a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mStationNameText.setTextSize(newTextSize / mDensity);
1386a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                newMargin = getNewSize(currentHeight, mTargetHeight, mStationNameMarginTopTarget,
1387a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mStationNamePaddingRate);
1388a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // if move to target position, need not move over the edge of actionbar
1389a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (lastHeight <= mActionBarHeight) {
1390a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    lastHeight = mActionBarHeight;
1391a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
1392a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                lastHeight = setNewPadding(mStationNameText, newMargin + lastHeight);
1393a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                /*
1394a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                 * 4. station rds (margin), in landscape with favorite
1395a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                 * it need parallel to station name
1396a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                 */
1397a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                newMargin = getNewSize(currentHeight, mTargetHeight, mStationRdsMarginTopTarget,
1398a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mStationRdsPaddingRate);
1399a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                int targetHeight = mFullHeight - (mFullHeight - mTargetHeight) / 2;
1400a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (currentHeight <= targetHeight) {
1401a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    String stationName = "" + mStationNameText.getText();
1402a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    int stationNameTextWidth = mStationNameText.getPaddingLeft();
1403a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    if (!stationName.equals("")) {
1404a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        Paint paint = mStationNameText.getPaint();
1405a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        stationNameTextWidth += (int) paint.measureText(stationName) + 8;
1406a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    }
1407a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mStationRdsText.setPadding((int) stationNameTextWidth,
1408a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            (int) (newMargin + lastHeight), mStationRdsText.getPaddingRight(),
1409a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            mStationRdsText.getPaddingBottom());
1410a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                } else {
1411a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    mStationRdsText.setPadding((int) (16 * mDensity),
1412a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            (int) (newMargin + lastHeight), mStationRdsText.getPaddingRight(),
1413a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                            mStationRdsText.getPaddingBottom());
1414a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
1415a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // 5. control buttons (margin)
1416a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                newMargin = getNewSize(currentHeight, mTargetHeight, mControlViewMarginTopTarget,
1417a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mControlViewPaddingRate);
1418a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                setNewPadding(mControlView, newMargin + lastHeight);
1419a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // 6. stop button (padding), it different to others, padding top refer to parent
1420a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                newMargin = getNewSize(currentHeight, mTargetHeight, mPlayButtonMarginTopTarget,
1421a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mPlayButtonPaddingRate);
1422a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                setNewPadding(mPlayButtonView, newMargin);
1423a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
142440cbbc57444d732004bba3c8464c979f83290279Benson Huang
142540cbbc57444d732004bba3c8464c979f83290279Benson Huang            // Compute the text "FM" width
142640cbbc57444d732004bba3c8464c979f83290279Benson Huang            private float computeFmDescriptionWidth() {
142740cbbc57444d732004bba3c8464c979f83290279Benson Huang                Paint paint = mFmDescriptionText.getPaint();
142840cbbc57444d732004bba3c8464c979f83290279Benson Huang                return (float) paint.measureText(mFmDescriptionText.getText().toString());
142940cbbc57444d732004bba3c8464c979f83290279Benson Huang            }
1430a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1431a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1432a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private class SecondRangeAdjuster extends FirstRangeAdjuster {
1433a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            public SecondRangeAdjuster() {
1434a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                Resources res = mContext.getResources();
1435a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mTargetHeight = mSecondTargetHeight;
1436a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // init start
1437a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mFrequencyStartTextSize = res
1438a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_frequency_text_size_first_target);
1439a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mStationNameTextSizeStart = res
1440a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_station_name_text_size_first_target);
144140cbbc57444d732004bba3c8464c979f83290279Benson Huang                mFmDescriptionMarginTopStart = res
1442a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_description_margin_top_first_target)
1443a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        + mActionBarHeight;// first view, margin refer to parent
1444a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mFrequencyMarginTopStart = res
1445a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_frequency_margin_top_first_target);
1446a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mStationNameMarginTopStart = res
1447a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_station_name_margin_top_first_target);
1448a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mStationRdsMarginTopStart = res
1449a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_station_rds_margin_top_first_target);
1450a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mControlViewMarginTopStart = res
1451a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_control_buttons_margin_top_first_target);
1452a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // init target
1453a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mFrequencyTextSizeTarget = res
1454a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_frequency_text_size_second_target);
1455a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mStationNameTextSizeTarget = res
1456a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_station_name_text_size_second_target);
145740cbbc57444d732004bba3c8464c979f83290279Benson Huang                mFmDescriptionMarginTopTarget = res
1458a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_description_margin_top_second_target);
1459a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mFrequencyMarginTopTarget = res
1460a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_frequency_margin_top_second_target);
1461a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mStationNameMarginTopTarget = res
1462a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_station_name_margin_top_second_target);
1463a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mStationRdsMarginTopTarget = res
1464a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_station_rds_margin_top_second_target);
1465a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mControlViewMarginTopTarget = res
1466a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        .getDimension(R.dimen.fm_control_buttons_margin_top_second_target);
1467a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // init text size and margin adjust rate
1468a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                float scrollHeight = mFirstTargetHeight - mTargetHeight;
1469a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mFrequencyTextSizeRate =
1470a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        (mFrequencyStartTextSize - mFrequencyTextSizeTarget)
1471a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        / scrollHeight;
1472a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mStationNameTextSizeRate =
1473a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        (mStationNameTextSizeStart - mStationNameTextSizeTarget)
1474a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        / scrollHeight;
147540cbbc57444d732004bba3c8464c979f83290279Benson Huang                mFmDescriptionPaddingRate =
147640cbbc57444d732004bba3c8464c979f83290279Benson Huang                        (mFmDescriptionMarginTopStart - mFmDescriptionMarginTopTarget)
147740cbbc57444d732004bba3c8464c979f83290279Benson Huang
1478a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        / scrollHeight;
1479a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mFrequencyPaddingRate = (mFrequencyMarginTopStart - mFrequencyMarginTopTarget)
1480a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        / scrollHeight;
1481a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mStationNamePaddingRate = (mStationNameMarginTopStart - mStationNameMarginTopTarget)
1482a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        / scrollHeight;
1483a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mStationRdsPaddingRate = (mStationRdsMarginTopStart - mStationRdsMarginTopTarget)
1484a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        / scrollHeight;
1485a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mControlViewPaddingRate = (mControlViewMarginTopStart - mControlViewMarginTopTarget)
1486a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        / scrollHeight;
1487a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // init play button padding, it different to others, padding top refer to parent
1488a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mPlayButtonHeight = res.getDimension(R.dimen.play_button_height);
1489a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mPlayButtonMarginTopStart = mFullHeight - mPlayButtonHeight - 16 * mDensity;
1490a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mPlayButtonMarginTopTarget = mFirstTargetHeight - mPlayButtonHeight / 2;
1491a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mPlayButtonPaddingRate = (mPlayButtonMarginTopStart - mPlayButtonMarginTopTarget)
1492a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        / scrollHeight;
1493a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
1494a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1495a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            @Override
1496a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            public void handleScroll() {
1497a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                int currentHeight = getHeaderHeight();
1498a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                float newMargin = 0;
1499a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                float lastHeight = 0;
1500a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                float newTextSize;
1501a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // 1. FM description (alpha and margin)
1502a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                float alpha = 0f;
1503a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                int offset = (int) ((mFirstTargetHeight - currentHeight) / mDensity);// dip
1504a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (offset <= 0) {
1505a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    alpha = 1f;
1506a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                } else if (offset <= 16) {
1507a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    alpha = 1 - offset / 16f;
1508a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
150940cbbc57444d732004bba3c8464c979f83290279Benson Huang                mFmDescriptionText.setAlpha(alpha);
151040cbbc57444d732004bba3c8464c979f83290279Benson Huang                newMargin = getNewSize(currentHeight, mTargetHeight, mFmDescriptionMarginTopTarget,
151140cbbc57444d732004bba3c8464c979f83290279Benson Huang                        mFmDescriptionPaddingRate);
151240cbbc57444d732004bba3c8464c979f83290279Benson Huang                lastHeight = setNewPadding(mFmDescriptionText, newMargin);
1513a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // 2. frequency text (text size and margin)
1514a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                newTextSize = getNewSize(currentHeight, mTargetHeight, mFrequencyTextSizeTarget,
1515a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mFrequencyTextSizeRate);
1516a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mFrequencyText.setTextSize(newTextSize / mDensity);
1517a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                newMargin = getNewSize(currentHeight, mTargetHeight, mFrequencyMarginTopTarget,
1518a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mFrequencyPaddingRate);
1519a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                lastHeight = setNewPadding(mFrequencyText, newMargin + lastHeight);
1520a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // If frequency text move to action bar, change it to bold
1521a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                setNewTypefaceForFrequencyText();
1522a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // 3. station name (text size and margin)
1523a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                newTextSize = getNewSize(currentHeight, mTargetHeight, mStationNameTextSizeTarget,
1524a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mStationNameTextSizeRate);
1525a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                mStationNameText.setTextSize(newTextSize / mDensity);
1526a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                newMargin = getNewSize(currentHeight, mTargetHeight, mStationNameMarginTopTarget,
1527a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mStationNamePaddingRate);
1528a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // if move to target position, need not move over the edge of actionbar
1529a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                if (lastHeight <= mActionBarHeight) {
1530a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    lastHeight = mActionBarHeight;
1531a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                }
1532a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                lastHeight = setNewPadding(mStationNameText, newMargin + lastHeight);
1533a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // 4. station rds (margin)
1534a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                newMargin = getNewSize(currentHeight, mTargetHeight, mStationRdsMarginTopTarget,
1535a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mStationRdsPaddingRate);
1536a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                lastHeight = setNewPadding(mStationRdsText, newMargin + lastHeight);
1537a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // 5. control buttons (margin)
1538a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                newMargin = getNewSize(currentHeight, mTargetHeight, mControlViewMarginTopTarget,
1539a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                        mControlViewPaddingRate);
1540a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                setNewPadding(mControlView, newMargin + lastHeight);
1541a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                // 6. stop button (padding), it different to others, padding top refer to parent
1542a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                newMargin = currentHeight - mPlayButtonHeight / 2;
1543a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                setNewPadding(mPlayButtonView, newMargin);
1544a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
1545a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1546a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1547a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private void setNewTypefaceForFrequencyText() {
1548a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            boolean needBold = (mSecondTargetHeight == getHeaderHeight());
1549a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            mFrequencyText.setTypeface(needBold ? Typeface.SANS_SERIF : mDefaultFrequencyTypeface);
1550a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1551a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1552a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private float setNewPadding(TextView current, float newMargin) {
1553a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            current.setPadding(current.getPaddingLeft(), (int) (newMargin),
1554a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    current.getPaddingRight(), current.getPaddingBottom());
1555a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            float nextLayoutPadding = newMargin + current.getTextSize();
1556a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return nextLayoutPadding;
1557a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1558a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1559a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private void setNewPadding(View current, float newMargin) {
1560a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            float newPadding = newMargin;
1561a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            current.setPadding(current.getPaddingLeft(), (int) (newPadding),
1562a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                    current.getPaddingRight(), current.getPaddingBottom());
1563a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1564a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1565a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        private float getNewSize(int currentHeight, int targetHeight,
1566a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                float targetSize, float rate) {
1567a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            if (currentHeight == targetHeight) {
1568a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang                return targetSize;
1569a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            }
1570a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang            return targetSize + (currentHeight - targetHeight) * rate;
1571a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        }
1572a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1573a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang
1574a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    private final class ViewHolder {
1575a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        ImageView mMoreButton;
1576a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        FmVisualizerView mPlayIndicator;
1577a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        TextView mStationFreq;
1578a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        TextView mStationName;
1579a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang        View mPopupMenuAnchor;
1580a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang    }
1581a8b6afca0e187c008ba8fdeb670d5f2c13116bedBenson Huang}
1582