blob: 8b0bdaadf966c97eb8737ece5d7d5acf22c0b4e1 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.fmradio.views;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnPreDrawListener;
import android.view.animation.Interpolator;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.EdgeEffect;
import android.widget.FrameLayout;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.PopupMenu.OnMenuItemClickListener;
import android.widget.ScrollView;
import android.widget.Scroller;
import android.widget.TextView;
import com.android.fmradio.FmStation;
import com.android.fmradio.FmUtils;
import com.android.fmradio.R;
import com.android.fmradio.FmStation.Station;
/**
* Modified from Contact MultiShrinkScroll Handle the touch event and change
* header size and scroll
*/
public class FmScroller extends FrameLayout {
private static final String TAG = "FmScroller";
/**
* 1000 pixels per millisecond. Ie, 1 pixel per second.
*/
private static final int PIXELS_PER_SECOND = 1000;
private static final int ON_PLAY_ANIMATION_DELAY = 1000;
private static final int PORT_COLUMN_NUM = 3;
private static final int LAND_COLUMN_NUM = 5;
private static final int STATE_NO_FAVORITE = 0;
private static final int STATE_HAS_FAVORITE = 1;
private float[] mLastEventPosition = {
0, 0
};
private VelocityTracker mVelocityTracker;
private boolean mIsBeingDragged = false;
private boolean mReceivedDown = false;
private boolean mFirstOnResume = true;
private String mSelection = "IS_FAVORITE=?";
private String[] mSelectionArgs = {
"1"
};
private EventListener mEventListener;
private PopupMenu mPopupMenu;
private Handler mMainHandler;
private ScrollView mScrollView;
private View mScrollViewChild;
private GridView mGridView;
private TextView mFavoriteText;
private View mHeader;
private int mMaximumHeaderHeight;
private int mMinimumHeaderHeight;
private Adjuster mAdjuster;
private int mCurrentStation;
private boolean mIsFmPlaying;
private FavoriteAdapter mAdapter;
private final Scroller mScroller;
private final EdgeEffect mEdgeGlowBottom;
private final int mTouchSlop;
private final int mMaximumVelocity;
private final int mMinimumVelocity;
private final int mActionBarSize;
private final AnimatorListener mHeaderExpandAnimationListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
refreshStateHeight();
}
};
/**
* Interpolator from android.support.v4.view.ViewPager. Snappier and more
* elastic feeling than the default interpolator.
*/
private static final Interpolator INTERPOLATOR = new Interpolator() {
/**
* {@inheritDoc}
*/
@Override
public float getInterpolation(float t) {
t -= 1.0f;
return t * t * t * t * t + 1.0f;
}
};
/**
* Constructor
*
* @param context The context
*/
public FmScroller(Context context) {
this(context, null);
}
/**
* Constructor
*
* @param context The context
* @param attrs The attrs
*/
public FmScroller(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
/**
* Constructor
*
* @param context The context
* @param attrs The attrs
* @param defStyleAttr The default attr
*/
public FmScroller(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
final ViewConfiguration configuration = ViewConfiguration.get(context);
setFocusable(false);
// Drawing must be enabled in order to support EdgeEffect
setWillNotDraw(/* willNotDraw = */false);
mEdgeGlowBottom = new EdgeEffect(context);
mScroller = new Scroller(context, INTERPOLATOR);
mTouchSlop = configuration.getScaledTouchSlop();
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
final TypedArray attributeArray = context.obtainStyledAttributes(new int[] {
android.R.attr.actionBarSize
});
mActionBarSize = attributeArray.getDimensionPixelSize(0, 0);
attributeArray.recycle();
}
/**
* This method must be called inside the Activity's OnCreate.
*/
public void initialize() {
mScrollView = (ScrollView) findViewById(R.id.content_scroller);
mScrollViewChild = findViewById(R.id.favorite_container);
mHeader = findViewById(R.id.main_header_parent);
mMainHandler = new Handler(Looper.getMainLooper());
mFavoriteText = (TextView) findViewById(R.id.favorite_text);
mGridView = (GridView) findViewById(R.id.gridview);
mAdapter = new FavoriteAdapter(getContext());
mAdjuster = new Adjuster(getContext());
mGridView.setAdapter(mAdapter);
Cursor c = getData();
mAdapter.swipResult(c);
mGridView.setFocusable(false);
mGridView.setFocusableInTouchMode(false);
mGridView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (mEventListener != null && mAdapter != null) {
mEventListener.onPlay(mAdapter.getFrequency(position));
}
mMainHandler.removeCallbacks(null);
mMainHandler.postDelayed(new Runnable() {
@Override
public void run() {
mMaximumHeaderHeight = getMaxHeight(STATE_HAS_FAVORITE);
expandHeader();
}
}, ON_PLAY_ANIMATION_DELAY);
}
});
// Called when first time create activity
doOnPreDraw(this, /* drawNextFrame = */false, new Runnable() {
@Override
public void run() {
refreshStateHeight();
setHeaderHeight(getMaximumScrollableHeaderHeight());
updateHeaderTextAndButton();
refreshFavoriteLayout();
}
});
}
/**
* Runs a piece of code just before the next draw, after layout and measurement
*
* @param view The view depend on
* @param drawNextFrame Whether to draw next frame
* @param runnable The executed runnable instance
*/
private void doOnPreDraw(final View view, final boolean drawNextFrame,
final Runnable runnable) {
final OnPreDrawListener listener = new OnPreDrawListener() {
@Override
public boolean onPreDraw() {
view.getViewTreeObserver().removeOnPreDrawListener(this);
runnable.run();
return drawNextFrame;
}
};
view.getViewTreeObserver().addOnPreDrawListener(listener);
}
private void refreshFavoriteLayout() {
setFavoriteTextHeight(mAdapter.getCount() == 0);
setGridViewHeight(computeGridViewHeight());
}
private void setFavoriteTextHeight(boolean show) {
if (mAdapter.getCount() == 0) {
mFavoriteText.setVisibility(View.GONE);
} else {
mFavoriteText.setVisibility(View.VISIBLE);
}
}
private void setGridViewHeight(int height) {
final ViewGroup.LayoutParams params = mGridView.getLayoutParams();
params.height = height;
mGridView.setLayoutParams(params);
}
private int computeGridViewHeight() {
int itemcount = mAdapter.getCount();
if (itemcount == 0) {
return 0;
}
int curOrientation = getResources().getConfiguration().orientation;
final boolean isLandscape = curOrientation == Configuration.ORIENTATION_LANDSCAPE;
int columnNum = isLandscape ? LAND_COLUMN_NUM : PORT_COLUMN_NUM;
int itemHeight = (int) getResources().getDimension(R.dimen.fm_gridview_item_height);
int itemPadding = (int) getResources().getDimension(R.dimen.fm_gridview_item_padding);
int rownum = (int) Math.ceil(itemcount / (float) columnNum);
int totalHeight = rownum * itemHeight + rownum * itemPadding;
if (rownum == 2) {
int minGridViewHeight = getHeight() - getMinHeight(STATE_HAS_FAVORITE) - 72;
totalHeight = Math.max(totalHeight, minGridViewHeight);
}
return totalHeight;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
// The only time we want to intercept touch events is when we are being
// dragged.
return shouldStartDrag(event);
}
private boolean shouldStartDrag(MotionEvent event) {
if (mIsBeingDragged) {
mIsBeingDragged = false;
return false;
}
switch (event.getAction()) {
// If we are in the middle of a fling and there is a down event,
// we'll steal it and
// start a drag.
case MotionEvent.ACTION_DOWN:
updateLastEventPosition(event);
if (!mScroller.isFinished()) {
startDrag();
return true;
} else {
mReceivedDown = true;
}
break;
// Otherwise, we will start a drag if there is enough motion in the
// direction we are
// capable of scrolling.
case MotionEvent.ACTION_MOVE:
if (motionShouldStartDrag(event)) {
updateLastEventPosition(event);
startDrag();
return true;
}
break;
default:
break;
}
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getAction();
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
if (!mIsBeingDragged) {
if (shouldStartDrag(event)) {
return true;
}
if (action == MotionEvent.ACTION_UP && mReceivedDown) {
mReceivedDown = false;
return performClick();
}
return true;
}
switch (action) {
case MotionEvent.ACTION_MOVE:
final float delta = updatePositionAndComputeDelta(event);
scrollTo(0, getScroll() + (int) delta);
mReceivedDown = false;
if (mIsBeingDragged) {
final int distanceFromMaxScrolling = getMaximumScrollUpwards() - getScroll();
if (delta > distanceFromMaxScrolling) {
// The ScrollView is being pulled upwards while there is
// no more
// content offscreen, and the view port is already fully
// expanded.
mEdgeGlowBottom.onPull(delta / getHeight(), 1 - event.getX() / getWidth());
}
if (!mEdgeGlowBottom.isFinished()) {
postInvalidateOnAnimation();
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
stopDrag(action == MotionEvent.ACTION_CANCEL);
mReceivedDown = false;
break;
default:
break;
}
return true;
}
/**
* Expand to maximum size or starting size. Disable clicks on the
* photo until the animation is complete.
*/
private void expandHeader() {
if (getHeaderHeight() != mMaximumHeaderHeight) {
// Expand header
final ObjectAnimator animator = ObjectAnimator.ofInt(this, "headerHeight",
mMaximumHeaderHeight);
animator.addListener(mHeaderExpandAnimationListener);
animator.setDuration(300);
animator.start();
// Scroll nested scroll view to its top
if (mScrollView.getScrollY() != 0) {
ObjectAnimator.ofInt(mScrollView, "scrollY", 0).setDuration(300).start();
}
}
}
private void collapseHeader() {
if (getHeaderHeight() != mMinimumHeaderHeight) {
final ObjectAnimator animator = ObjectAnimator.ofInt(this, "headerHeight",
mMinimumHeaderHeight);
animator.addListener(mHeaderExpandAnimationListener);
animator.start();
}
}
private void startDrag() {
mIsBeingDragged = true;
mScroller.abortAnimation();
}
private void stopDrag(boolean cancelled) {
mIsBeingDragged = false;
if (!cancelled && getChildCount() > 0) {
final float velocity = getCurrentVelocity();
if (velocity > mMinimumVelocity || velocity < -mMinimumVelocity) {
fling(-velocity);
}
}
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
mEdgeGlowBottom.onRelease();
}
@Override
public void scrollTo(int x, int y) {
final int delta = y - getScroll();
if (delta > 0) {
scrollUp(delta);
} else {
scrollDown(delta);
}
updateHeaderTextAndButton();
}
private int getToolbarHeight() {
return mHeader.getLayoutParams().height;
}
/**
* Set the height of the toolbar and update its tint accordingly.
*/
@FmReflection
public void setHeaderHeight(int height) {
final ViewGroup.LayoutParams toolbarLayoutParams = mHeader.getLayoutParams();
toolbarLayoutParams.height = height;
mHeader.setLayoutParams(toolbarLayoutParams);
updateHeaderTextAndButton();
}
/**
* Get header height. Used in ObjectAnimator
*
* @return The header height
*/
@FmReflection
public int getHeaderHeight() {
return mHeader.getLayoutParams().height;
}
/**
* Set scroll. Used in ObjectAnimator
*/
@FmReflection
public void setScroll(int scroll) {
scrollTo(0, scroll);
}
/**
* Returns the total amount scrolled inside the nested ScrollView + the amount
* of shrinking performed on the ToolBar. This is the value inspected by animators.
*/
@FmReflection
public int getScroll() {
return getMaximumScrollableHeaderHeight() - getToolbarHeight() + mScrollView.getScrollY();
}
private int getMaximumScrollableHeaderHeight() {
return mMaximumHeaderHeight;
}
/**
* A variant of {@link #getScroll} that pretends the header is never
* larger than than mIntermediateHeaderHeight. This function is sometimes
* needed when making scrolling decisions that will not change the header
* size (ie, snapping to the bottom or top). When mIsOpenContactSquare is
* true, this function considers mIntermediateHeaderHeight == mMaximumHeaderHeight,
* since snapping decisions will be made relative the full header size when
* mIsOpenContactSquare = true. This value should never be used in conjunction
* with {@link #getScroll} values.
*/
private int getScrollIgnoreOversizedHeaderForSnapping() {
return Math.max(getMaximumScrollableHeaderHeight() - getToolbarHeight(), 0)
+ mScrollView.getScrollY();
}
/**
* Return amount of scrolling needed in order for all the visible
* subviews to scroll off the bottom.
*/
private int getScrollUntilOffBottom() {
return getHeight() + getScrollIgnoreOversizedHeaderForSnapping();
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
// Examine the fling results in order to activate EdgeEffect when we
// fling to the end.
final int oldScroll = getScroll();
scrollTo(0, mScroller.getCurrY());
final int delta = mScroller.getCurrY() - oldScroll;
final int distanceFromMaxScrolling = getMaximumScrollUpwards() - getScroll();
if (delta > distanceFromMaxScrolling && distanceFromMaxScrolling > 0) {
mEdgeGlowBottom.onAbsorb((int) mScroller.getCurrVelocity());
}
if (!awakenScrollBars()) {
// Keep on drawing until the animation has finished.
postInvalidateOnAnimation();
}
if (mScroller.getCurrY() >= getMaximumScrollUpwards()) {
mScroller.abortAnimation();
}
}
}
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (!mEdgeGlowBottom.isFinished()) {
final int restoreCount = canvas.save();
final int width = getWidth() - getPaddingLeft() - getPaddingRight();
final int height = getHeight();
// Draw the EdgeEffect on the bottom of the Window (Or a little bit
// below the bottom
// of the Window if we start to scroll upwards while EdgeEffect is
// visible). This
// does not need to consider the case where this MultiShrinkScroller
// doesn't fill
// the Window, since the nested ScrollView should be set to
// fillViewport.
canvas.translate(-width + getPaddingLeft(), height + getMaximumScrollUpwards()
- getScroll());
canvas.rotate(180, width, 0);
mEdgeGlowBottom.setSize(width, height);
if (mEdgeGlowBottom.draw(canvas)) {
postInvalidateOnAnimation();
}
canvas.restoreToCount(restoreCount);
}
}
private float getCurrentVelocity() {
if (mVelocityTracker == null) {
return 0;
}
mVelocityTracker.computeCurrentVelocity(PIXELS_PER_SECOND, mMaximumVelocity);
return mVelocityTracker.getYVelocity();
}
private void fling(float velocity) {
// For reasons I do not understand, scrolling is less janky when
// maxY=Integer.MAX_VALUE
// then when maxY is set to an actual value.
mScroller.fling(0, getScroll(), 0, (int) velocity, 0, 0, -Integer.MAX_VALUE,
Integer.MAX_VALUE);
invalidate();
}
private int getMaximumScrollUpwards() {
return // How much the Header view can compress
getMaximumScrollableHeaderHeight() - getFullyCompressedHeaderHeight()
// How much the ScrollView can scroll. 0, if child is
// smaller than ScrollView.
+ Math.max(0, mScrollViewChild.getHeight() - getHeight()
+ getFullyCompressedHeaderHeight());
}
private void scrollUp(int delta) {
final ViewGroup.LayoutParams toolbarLayoutParams = mHeader.getLayoutParams();
if (toolbarLayoutParams.height > getFullyCompressedHeaderHeight()) {
final int originalValue = toolbarLayoutParams.height;
toolbarLayoutParams.height -= delta;
toolbarLayoutParams.height = Math.max(toolbarLayoutParams.height,
getFullyCompressedHeaderHeight());
mHeader.setLayoutParams(toolbarLayoutParams);
delta -= originalValue - toolbarLayoutParams.height;
}
mScrollView.scrollBy(0, delta);
}
/**
* Returns the minimum size that we want to compress the header to,
* given that we don't want to allow the the ScrollView to scroll
* unless there is new content off of the edge of ScrollView.
*/
private int getFullyCompressedHeaderHeight() {
int height = Math.min(Math.max(mHeader.getLayoutParams().height
- getOverflowingChildViewSize(), mMinimumHeaderHeight),
getMaximumScrollableHeaderHeight());
return height;
}
/**
* Returns the amount of mScrollViewChild that doesn't fit inside its parent. Outside size
*/
private int getOverflowingChildViewSize() {
final int usedScrollViewSpace = mScrollViewChild.getHeight();
return -getHeight() + usedScrollViewSpace + mHeader.getLayoutParams().height;
}
private void scrollDown(int delta) {
if (mScrollView.getScrollY() > 0) {
final int originalValue = mScrollView.getScrollY();
mScrollView.scrollBy(0, delta);
}
}
private void updateHeaderTextAndButton() {
mAdjuster.handleScroll();
}
private void updateLastEventPosition(MotionEvent event) {
mLastEventPosition[0] = event.getX();
mLastEventPosition[1] = event.getY();
}
private boolean motionShouldStartDrag(MotionEvent event) {
final float deltaX = event.getX() - mLastEventPosition[0];
final float deltaY = event.getY() - mLastEventPosition[1];
final boolean draggedX = (deltaX > mTouchSlop || deltaX < -mTouchSlop);
final boolean draggedY = (deltaY > mTouchSlop || deltaY < -mTouchSlop);
return draggedY && !draggedX;
}
private float updatePositionAndComputeDelta(MotionEvent event) {
final int vertical = 1;
final float position = mLastEventPosition[vertical];
updateLastEventPosition(event);
return position - mLastEventPosition[vertical];
}
/**
* Interpolator that enforces a specific starting velocity.
* This is useful to avoid a discontinuity between dragging
* speed and flinging speed. Similar to a
* {@link android.view.animation.AccelerateInterpolator} in
* the sense that getInterpolation() is a quadratic function.
*/
private static class AcceleratingFlingInterpolator implements Interpolator {
private final float mStartingSpeedPixelsPerFrame;
private final float mDurationMs;
private final int mPixelsDelta;
private final float mNumberFrames;
public AcceleratingFlingInterpolator(int durationMs, float startingSpeedPixelsPerSecond,
int pixelsDelta) {
mStartingSpeedPixelsPerFrame = startingSpeedPixelsPerSecond / getRefreshRate();
mDurationMs = durationMs;
mPixelsDelta = pixelsDelta;
mNumberFrames = mDurationMs / getFrameIntervalMs();
}
@Override
public float getInterpolation(float input) {
final float animationIntervalNumber = mNumberFrames * input;
final float linearDelta = (animationIntervalNumber * mStartingSpeedPixelsPerFrame)
/ mPixelsDelta;
// Add the results of a linear interpolator (with the initial speed)
// with the
// results of a AccelerateInterpolator.
if (mStartingSpeedPixelsPerFrame > 0) {
return Math.min(input * input + linearDelta, 1);
} else {
// Initial fling was in the wrong direction, make sure that the
// quadratic component
// grows faster in order to make up for this.
return Math.min(input * (input - linearDelta) + linearDelta, 1);
}
}
private float getRefreshRate() {
DisplayInfo di = DisplayManagerGlobal.getInstance().getDisplayInfo(
Display.DEFAULT_DISPLAY);
return di.refreshRate;
}
public long getFrameIntervalMs() {
return (long) (1000 / getRefreshRate());
}
}
private int getMaxHeight(int state) {
int height = 0;
switch (state) {
case STATE_NO_FAVORITE:
height = getHeight();
break;
case STATE_HAS_FAVORITE:
height = (int) getResources().getDimension(R.dimen.fm_main_header_big);
break;
default:
break;
}
return height;
}
private int getMinHeight(int state) {
int height = 0;
switch (state) {
case STATE_NO_FAVORITE:
height = (int) getResources().getDimension(R.dimen.fm_main_header_big);
break;
case STATE_HAS_FAVORITE:
height = (int) getResources().getDimension(R.dimen.fm_main_header_small);
break;
default:
break;
}
return height;
}
private void setMinHeight(int height) {
mMinimumHeaderHeight = height;
}
class FavoriteAdapter extends BaseAdapter {
private Cursor mCursor;
private LayoutInflater mInflater;
public FavoriteAdapter(Context context) {
mInflater = LayoutInflater.from(context);
}
public int getFrequency(int position) {
if (mCursor != null && mCursor.moveToFirst()) {
mCursor.moveToPosition(position);
return mCursor.getInt(mCursor.getColumnIndex(FmStation.Station.FREQUENCY));
}
return 0;
}
public void swipResult(Cursor cursor) {
if (null != mCursor) {
mCursor.close();
}
mCursor = cursor;
notifyDataSetChanged();
}
@Override
public int getCount() {
if (null != mCursor) {
return mCursor.getCount();
}
return 0;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if (null == convertView) {
viewHolder = new ViewHolder();
convertView = mInflater.inflate(R.layout.favorite_gridview_item, null);
viewHolder.mStationFreq = (TextView) convertView.findViewById(R.id.station_freq);
viewHolder.mPlayIndicator = (FmVisualizerView) convertView
.findViewById(R.id.fm_play_indicator);
viewHolder.mStationName = (TextView) convertView.findViewById(R.id.station_name);
viewHolder.mMoreButton = (ImageView) convertView.findViewById(R.id.station_more);
viewHolder.mPopupMenuAnchor = convertView.findViewById(R.id.popupmenu_anchor);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
if (mCursor != null && mCursor.moveToPosition(position)) {
final int stationFreq = mCursor.getInt(mCursor
.getColumnIndex(FmStation.Station.FREQUENCY));
String name = mCursor.getString(mCursor
.getColumnIndex(FmStation.Station.STATION_NAME));
String rds = mCursor.getString(mCursor
.getColumnIndex(FmStation.Station.RADIO_TEXT));
final int isFavorite = mCursor.getInt(mCursor
.getColumnIndex(FmStation.Station.IS_FAVORITE));
if (null == name || "".equals(name)) {
name = mCursor.getString(mCursor
.getColumnIndex(FmStation.Station.PROGRAM_SERVICE));
}
if (null == name || "".equals(name)) {
name = "";
}
viewHolder.mStationFreq.setText(FmUtils.formatStation(stationFreq));
viewHolder.mStationName.setText(name);
if (mCurrentStation == stationFreq) {
viewHolder.mPlayIndicator.setVisibility(View.VISIBLE);
if (mIsFmPlaying) {
viewHolder.mPlayIndicator.startAnimation();
} else {
viewHolder.mPlayIndicator.stopAnimation();
}
viewHolder.mStationFreq.setTextColor(Color.parseColor("#607D8B"));
viewHolder.mStationFreq.setAlpha(1f);
viewHolder.mStationName.setMaxLines(1);
} else {
viewHolder.mPlayIndicator.setVisibility(View.GONE);
viewHolder.mPlayIndicator.stopAnimation();
viewHolder.mStationFreq.setTextColor(Color.parseColor("#000000"));
viewHolder.mStationFreq.setAlpha(0.87f);
viewHolder.mStationName.setMaxLines(2);
}
viewHolder.mMoreButton.setTag(viewHolder.mPopupMenuAnchor);
viewHolder.mMoreButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// Use anchor view to fix PopupMenu postion and cover more button
View anchor = v;
if (v.getTag() != null) {
anchor = (View) v.getTag();
}
showPopupMenu(anchor, stationFreq);
}
});
}
return convertView;
}
}
private Cursor getData() {
Cursor cursor = getContext().getContentResolver().query(Station.CONTENT_URI,
FmStation.COLUMNS, mSelection, mSelectionArgs,
FmStation.Station.FREQUENCY);
return cursor;
}
/**
* Called when FmRadioActivity.onResume(), refresh layout
*/
public void onResume() {
Cursor c = getData();
mAdapter.swipResult(c);
if (mFirstOnResume) {
mFirstOnResume = false;
} else {
refreshStateHeight();
updateHeaderTextAndButton();
refreshFavoriteLayout();
int curOrientation = getResources().getConfiguration().orientation;
final boolean isLandscape = curOrientation == Configuration.ORIENTATION_LANDSCAPE;
int columnNum = isLandscape ? LAND_COLUMN_NUM : PORT_COLUMN_NUM;
boolean isOneRow = c.getCount() <= columnNum;
boolean hasFavoriteCurrent = c.getCount() > 0;
if (mHasFavoriteWhenOnPause != hasFavoriteCurrent || isOneRow) {
setHeaderHeight(getMaximumScrollableHeaderHeight());
}
}
}
private boolean mHasFavoriteWhenOnPause = false;
/**
* Called when FmRadioActivity.onPause()
*/
public void onPause() {
if (mAdapter != null && mAdapter.getCount() > 0) {
mHasFavoriteWhenOnPause = true;
} else {
mHasFavoriteWhenOnPause = false;
}
}
/**
* Notify refresh adapter when data change
*/
public void notifyAdatperChange() {
Cursor c = getData();
mAdapter.swipResult(c);
}
private void refreshStateHeight() {
if (mAdapter != null && mAdapter.getCount() > 0) {
mMaximumHeaderHeight = getMaxHeight(STATE_HAS_FAVORITE);
mMinimumHeaderHeight = getMinHeight(STATE_HAS_FAVORITE);
} else {
mMaximumHeaderHeight = getMaxHeight(STATE_NO_FAVORITE);
mMinimumHeaderHeight = getMinHeight(STATE_NO_FAVORITE);
}
}
/**
* Called when add a favorite
*/
public void onAddFavorite() {
Cursor c = getData();
mAdapter.swipResult(c);
refreshFavoriteLayout();
if (c.getCount() == 1) {
// Last time count is 0, so need set STATE_NO_FAVORITE then collapse header
mMinimumHeaderHeight = getMinHeight(STATE_NO_FAVORITE);
mMaximumHeaderHeight = getMaxHeight(STATE_NO_FAVORITE);
collapseHeader();
}
}
/**
* Called when remove a favorite
*/
public void onRemoveFavorite() {
Cursor c = getData();
mAdapter.swipResult(c);
refreshFavoriteLayout();
if (c != null && c.getCount() == 0) {
// Stop the play animation
mMainHandler.removeCallbacks(null);
// Last time count is 1, so need set STATE_NO_FAVORITE then expand header
mMinimumHeaderHeight = getMinHeight(STATE_NO_FAVORITE);
mMaximumHeaderHeight = getMaxHeight(STATE_NO_FAVORITE);
expandHeader();
}
}
private void showPopupMenu(View anchor, final int frequency) {
dismissPopupMenu();
mPopupMenu = new PopupMenu(getContext(), anchor);
Menu menu = mPopupMenu.getMenu();
mPopupMenu.getMenuInflater().inflate(R.menu.gridview_item_more_menu, menu);
mPopupMenu.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.remove_favorite:
if (mEventListener != null) {
mEventListener.onRemoveFavorite(frequency);
}
break;
case R.id.rename:
if (mEventListener != null) {
mEventListener.onRename(frequency);
}
break;
default:
break;
}
return false;
}
});
mPopupMenu.show();
}
private void dismissPopupMenu() {
if (mPopupMenu != null) {
mPopupMenu.dismiss();
mPopupMenu = null;
}
}
/**
* Called when FmRadioActivity.onDestory()
*/
public void closeAdapterCursor() {
mAdapter.swipResult(null);
}
/**
* Register a listener for GridView item event
*
* @param listener The event listener
*/
public void registerListener(EventListener listener) {
mEventListener = listener;
}
/**
* Unregister a listener for GridView item event
*
* @param listener The event listener
*/
public void unregisterListener(EventListener listener) {
mEventListener = null;
}
/**
* Listen for GridView item event: remove, rename, click play
*/
public interface EventListener {
/**
* Callback when click remove favorite menu
*
* @param frequency The frequency want to remove
*/
void onRemoveFavorite(int frequency);
/**
* Callback when click rename favorite menu
*
* @param frequency The frequency want to rename
*/
void onRename(int frequency);
/**
* Callback when click gridview item to play
*
* @param frequency The frequency want to play
*/
void onPlay(int frequency);
}
/**
* Refresh the play indicator in gridview when play station or play state change
*
* @param currentStation current station
* @param isFmPlaying whether fm is playing
*/
public void refreshPlayIndicator(int currentStation, boolean isFmPlaying) {
mCurrentStation = currentStation;
mIsFmPlaying = isFmPlaying;
if (mAdapter != null) {
mAdapter.notifyDataSetChanged();
}
}
/**
* Adjust view padding and text size when scroll
*/
private class Adjuster {
private final DisplayMetrics mDisplayMetrics;
private final int mFirstTargetHeight;
private final int mSecondTargetHeight;
private final int mActionBarHeight = mActionBarSize;
private final int mStatusBarHeight;
private final int mFullHeight;// display height without status bar
private final float mDensity;
private final Typeface mDefaultFrequencyTypeface;
// Text view
private TextView mFrequencyText;
private TextView mFmDescrptionText;
private TextView mStationNameText;
private TextView mStationRdsText;
/*
* The five control buttons view(previous, next, increase,
* decrease, favorite) and stop button
*/
private View mControlView;
private View mPlayButtonView;
private final Context mContext;
private final boolean mIsLandscape;
private FirstRangeAdjuster mFirstRangeAdjuster;
private SecondRangeAdjuster mSecondRangeAdjusterr;
public Adjuster(Context context) {
mContext = context;
mDisplayMetrics = mContext.getResources().getDisplayMetrics();
mDensity = mDisplayMetrics.density;
int curOrientation = getResources().getConfiguration().orientation;
mIsLandscape = curOrientation == Configuration.ORIENTATION_LANDSCAPE;
Resources res = mContext.getResources();
mFirstTargetHeight = res.getDimensionPixelSize(R.dimen.fm_main_header_big);
mSecondTargetHeight = res.getDimensionPixelSize(R.dimen.fm_main_header_small);
mStatusBarHeight = res
.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
mFullHeight = mDisplayMetrics.heightPixels - mStatusBarHeight;
mFrequencyText = (TextView) findViewById(R.id.station_value);
mFmDescrptionText = (TextView) findViewById(R.id.text_fm);
mStationNameText = (TextView) findViewById(R.id.station_name);
mStationRdsText = (TextView) findViewById(R.id.station_rds);
mControlView = findViewById(R.id.rl_imgbtnpart);
mPlayButtonView = findViewById(R.id.play_button_container);
mFirstRangeAdjuster = new FirstRangeAdjuster();
mSecondRangeAdjusterr = new SecondRangeAdjuster();
mControlView.setMinimumWidth(mIsLandscape ? mDisplayMetrics.heightPixels
: mDisplayMetrics.widthPixels);
mDefaultFrequencyTypeface = mFrequencyText.getTypeface();
}
public void handleScroll() {
int height = getHeaderHeight();
if (mIsLandscape || height > mFirstTargetHeight) {
mFirstRangeAdjuster.handleScroll();
} else if (height >= mSecondTargetHeight) {
mSecondRangeAdjusterr.handleScroll();
}
}
private class FirstRangeAdjuster {
protected int mTargetHeight;
// start text size and margin
protected float mFrequencyStartTextSize;
protected float mStationNameTextSizeStart;
protected float mFmDescrptionMarginTopStart;
protected float mFrequencyMarginTopStart;
protected float mStationNameMarginTopStart;
protected float mStationRdsMarginTopStart;
protected float mControlViewMarginTopStart;
// target text size and margin
protected float mFrequencyTextSizeTarget;
protected float mStationNameTextSizeTarget;
protected float mFmDescrptionMarginTopTarget;
protected float mFrequencyMarginTopTarget;
protected float mStationNameMarginTopTarget;
protected float mStationRdsMarginTopTarget;
protected float mControlViewMarginTopTarget;
protected float mPlayButtonMarginTopStart;
protected float mPlayButtonMarginTopTarget;
protected float mPlayButtonHeight;
// Padding adjust rate as linear
protected float mFmDescrptionPaddingRate;
protected float mFrequencyPaddingRate;
protected float mStationNamePaddingRate;
protected float mStationRdsPaddingRate;
protected float mControlViewPaddingRate;
// init it with display height
protected float mPlayButtonPaddingRate;
// Text size adjust rate as linear
// adjust from first to target critical height
protected float mFrequencyTextSizeRate;
// adjust before first critical height
protected float mStationNameTextSizeRate;
public FirstRangeAdjuster() {
Resources res = mContext.getResources();
mTargetHeight = mFirstTargetHeight;
// init start
mFrequencyStartTextSize = res.getDimension(R.dimen.fm_frequency_text_size_start);
mStationNameTextSizeStart = res
.getDimension(R.dimen.fm_station_name_text_size_start);
// first view, margin refer to parent
mFmDescrptionMarginTopStart = res
.getDimension(R.dimen.fm_description_margin_top_start) + mActionBarHeight;
mFrequencyMarginTopStart = res.getDimension(R.dimen.fm_frequency_margin_top_start);
mStationNameMarginTopStart = res
.getDimension(R.dimen.fm_station_name_margin_top_start);
mStationRdsMarginTopStart = res
.getDimension(R.dimen.fm_station_rds_margin_top_start);
mControlViewMarginTopStart = res
.getDimension(R.dimen.fm_control_buttons_margin_top_start);
// init target
mFrequencyTextSizeTarget = res
.getDimension(R.dimen.fm_frequency_text_size_first_target);
mStationNameTextSizeTarget = res
.getDimension(R.dimen.fm_station_name_text_size_first_target);
mFmDescrptionMarginTopTarget = res
.getDimension(R.dimen.fm_description_margin_top_first_target);
// first view, margin refer to parent if not in landscape
if (!mIsLandscape) {
mFmDescrptionMarginTopTarget += mActionBarHeight;
}
mFrequencyMarginTopTarget = res
.getDimension(R.dimen.fm_frequency_margin_top_first_target);
mStationNameMarginTopTarget = res
.getDimension(R.dimen.fm_station_name_margin_top_first_target);
mStationRdsMarginTopTarget = res
.getDimension(R.dimen.fm_station_rds_margin_top_first_target);
mControlViewMarginTopTarget = res
.getDimension(R.dimen.fm_control_buttons_margin_top_first_target);
// init text size and margin adjust rate
int scrollHeight = mFullHeight - mTargetHeight;
mFrequencyTextSizeRate = (mFrequencyStartTextSize - mFrequencyTextSizeTarget)
/ scrollHeight;
mStationNameTextSizeRate = (mStationNameTextSizeStart - mStationNameTextSizeTarget)
/ scrollHeight;
mFmDescrptionPaddingRate =
(mFmDescrptionMarginTopStart - mFmDescrptionMarginTopTarget)
/ scrollHeight;
mFrequencyPaddingRate = (mFrequencyMarginTopStart - mFrequencyMarginTopTarget)
/ scrollHeight;
mStationNamePaddingRate = (mStationNameMarginTopStart - mStationNameMarginTopTarget)
/ scrollHeight;
mStationRdsPaddingRate = (mStationRdsMarginTopStart - mStationRdsMarginTopTarget)
/ scrollHeight;
mControlViewPaddingRate = (mControlViewMarginTopStart - mControlViewMarginTopTarget)
/ scrollHeight;
// init play button padding, it different to others, padding top refer to parent
mPlayButtonHeight = res.getDimension(R.dimen.play_button_height);
mPlayButtonMarginTopStart = mFullHeight - mPlayButtonHeight - 16 * mDensity;
mPlayButtonMarginTopTarget = mFirstTargetHeight - mPlayButtonHeight / 2;
mPlayButtonPaddingRate = (mPlayButtonMarginTopStart - mPlayButtonMarginTopTarget)
/ scrollHeight;
}
public void handleScroll() {
if (mIsLandscape) {
handleScrollLandscapeMode();
return;
}
int currentHeight = getHeaderHeight();
float newMargin = 0;
float lastHeight = 0;
float newTextSize;
// 1.FM description (margin)
newMargin = getNewSize(currentHeight, mTargetHeight, mFmDescrptionMarginTopTarget,
mFmDescrptionPaddingRate);
lastHeight = setNewPadding(mFmDescrptionText, newMargin);
// 2. frequency text (text size and margin)
newTextSize = getNewSize(currentHeight, mTargetHeight, mFrequencyTextSizeTarget,
mFrequencyTextSizeRate);
mFrequencyText.setTextSize(newTextSize / mDensity);
newMargin = getNewSize(currentHeight, mTargetHeight, mFrequencyMarginTopTarget,
mFrequencyPaddingRate);
lastHeight = setNewPadding(mFrequencyText, newMargin + lastHeight);
// 3. station name (margin and text size)
newMargin = getNewSize(currentHeight, mTargetHeight, mStationNameMarginTopTarget,
mStationNamePaddingRate);
lastHeight = setNewPadding(mStationNameText, newMargin + lastHeight);
newTextSize = getNewSize(currentHeight, mTargetHeight, mStationNameTextSizeTarget,
mStationNameTextSizeRate);
mStationNameText.setTextSize(newTextSize / mDensity);
// 4. station rds (margin)
newMargin = getNewSize(currentHeight, mTargetHeight, mStationRdsMarginTopTarget,
mStationRdsPaddingRate);
lastHeight = setNewPadding(mStationRdsText, newMargin + lastHeight);
// 5. control buttons (margin)
newMargin = getNewSize(currentHeight, mTargetHeight, mControlViewMarginTopTarget,
mControlViewPaddingRate);
setNewPadding(mControlView, newMargin + lastHeight);
// 6. stop button (padding), it different to others, padding top refer to parent
newMargin = getNewSize(currentHeight, mTargetHeight, mPlayButtonMarginTopTarget,
mPlayButtonPaddingRate);
setNewPadding(mPlayButtonView, newMargin);
}
private void handleScrollLandscapeMode() {
int currentHeight = getHeaderHeight();
float newMargin = 0;
float lastHeight = 0;
float newTextSize;
// 1. FM description (alpha and margin)
float alpha = 0f;
int offset = (int) ((mFullHeight - currentHeight) / mDensity);// dip
if (offset <= 0) {
alpha = 1f;
} else if (offset <= 16) {
alpha = 1 - offset / 16f;
}
mFmDescrptionText.setAlpha(alpha);
newMargin = getNewSize(currentHeight, mTargetHeight, mFmDescrptionMarginTopTarget,
mFmDescrptionPaddingRate);
lastHeight = setNewPadding(mFmDescrptionText, newMargin);
// 2. frequency text (text size and margin)
newTextSize = getNewSize(currentHeight, mTargetHeight, mFrequencyTextSizeTarget,
mFrequencyTextSizeRate);
mFrequencyText.setTextSize(newTextSize / mDensity);
newMargin = getNewSize(currentHeight, mTargetHeight, mFrequencyMarginTopTarget,
mFrequencyPaddingRate);
lastHeight = setNewPadding(mFrequencyText, newMargin + lastHeight);
// If frequency text move to action bar, change it to bold
setNewTypefaceForFrequencyText();
// 3. station name (text size and margin)
newTextSize = getNewSize(currentHeight, mTargetHeight, mStationNameTextSizeTarget,
mStationNameTextSizeRate);
mStationNameText.setTextSize(newTextSize / mDensity);
newMargin = getNewSize(currentHeight, mTargetHeight, mStationNameMarginTopTarget,
mStationNamePaddingRate);
// if move to target position, need not move over the edge of actionbar
if (lastHeight <= mActionBarHeight) {
lastHeight = mActionBarHeight;
}
lastHeight = setNewPadding(mStationNameText, newMargin + lastHeight);
/*
* 4. station rds (margin), in landscape with favorite
* it need parallel to station name
*/
newMargin = getNewSize(currentHeight, mTargetHeight, mStationRdsMarginTopTarget,
mStationRdsPaddingRate);
int targetHeight = mFullHeight - (mFullHeight - mTargetHeight) / 2;
if (currentHeight <= targetHeight) {
String stationName = "" + mStationNameText.getText();
int stationNameTextWidth = mStationNameText.getPaddingLeft();
if (!stationName.equals("")) {
Paint paint = mStationNameText.getPaint();
stationNameTextWidth += (int) paint.measureText(stationName) + 8;
}
mStationRdsText.setPadding((int) stationNameTextWidth,
(int) (newMargin + lastHeight), mStationRdsText.getPaddingRight(),
mStationRdsText.getPaddingBottom());
} else {
mStationRdsText.setPadding((int) (16 * mDensity),
(int) (newMargin + lastHeight), mStationRdsText.getPaddingRight(),
mStationRdsText.getPaddingBottom());
}
// 5. control buttons (margin)
newMargin = getNewSize(currentHeight, mTargetHeight, mControlViewMarginTopTarget,
mControlViewPaddingRate);
setNewPadding(mControlView, newMargin + lastHeight);
// 6. stop button (padding), it different to others, padding top refer to parent
newMargin = getNewSize(currentHeight, mTargetHeight, mPlayButtonMarginTopTarget,
mPlayButtonPaddingRate);
setNewPadding(mPlayButtonView, newMargin);
}
}
private class SecondRangeAdjuster extends FirstRangeAdjuster {
public SecondRangeAdjuster() {
Resources res = mContext.getResources();
mTargetHeight = mSecondTargetHeight;
// init start
mFrequencyStartTextSize = res
.getDimension(R.dimen.fm_frequency_text_size_first_target);
mStationNameTextSizeStart = res
.getDimension(R.dimen.fm_station_name_text_size_first_target);
mFmDescrptionMarginTopStart = res
.getDimension(R.dimen.fm_description_margin_top_first_target)
+ mActionBarHeight;// first view, margin refer to parent
mFrequencyMarginTopStart = res
.getDimension(R.dimen.fm_frequency_margin_top_first_target);
mStationNameMarginTopStart = res
.getDimension(R.dimen.fm_station_name_margin_top_first_target);
mStationRdsMarginTopStart = res
.getDimension(R.dimen.fm_station_rds_margin_top_first_target);
mControlViewMarginTopStart = res
.getDimension(R.dimen.fm_control_buttons_margin_top_first_target);
// init target
mFrequencyTextSizeTarget = res
.getDimension(R.dimen.fm_frequency_text_size_second_target);
mStationNameTextSizeTarget = res
.getDimension(R.dimen.fm_station_name_text_size_second_target);
mFmDescrptionMarginTopTarget = res
.getDimension(R.dimen.fm_description_margin_top_second_target);
mFrequencyMarginTopTarget = res
.getDimension(R.dimen.fm_frequency_margin_top_second_target);
mStationNameMarginTopTarget = res
.getDimension(R.dimen.fm_station_name_margin_top_second_target);
mStationRdsMarginTopTarget = res
.getDimension(R.dimen.fm_station_rds_margin_top_second_target);
mControlViewMarginTopTarget = res
.getDimension(R.dimen.fm_control_buttons_margin_top_second_target);
// init text size and margin adjust rate
float scrollHeight = mFirstTargetHeight - mTargetHeight;
mFrequencyTextSizeRate =
(mFrequencyStartTextSize - mFrequencyTextSizeTarget)
/ scrollHeight;
mStationNameTextSizeRate =
(mStationNameTextSizeStart - mStationNameTextSizeTarget)
/ scrollHeight;
mFmDescrptionPaddingRate =
(mFmDescrptionMarginTopStart - mFmDescrptionMarginTopTarget)
/ scrollHeight;
mFrequencyPaddingRate = (mFrequencyMarginTopStart - mFrequencyMarginTopTarget)
/ scrollHeight;
mStationNamePaddingRate = (mStationNameMarginTopStart - mStationNameMarginTopTarget)
/ scrollHeight;
mStationRdsPaddingRate = (mStationRdsMarginTopStart - mStationRdsMarginTopTarget)
/ scrollHeight;
mControlViewPaddingRate = (mControlViewMarginTopStart - mControlViewMarginTopTarget)
/ scrollHeight;
// init play button padding, it different to others, padding top refer to parent
mPlayButtonHeight = res.getDimension(R.dimen.play_button_height);
mPlayButtonMarginTopStart = mFullHeight - mPlayButtonHeight - 16 * mDensity;
mPlayButtonMarginTopTarget = mFirstTargetHeight - mPlayButtonHeight / 2;
mPlayButtonPaddingRate = (mPlayButtonMarginTopStart - mPlayButtonMarginTopTarget)
/ scrollHeight;
}
@Override
public void handleScroll() {
int currentHeight = getHeaderHeight();
float newMargin = 0;
float lastHeight = 0;
float newTextSize;
// 1. FM description (alpha and margin)
float alpha = 0f;
int offset = (int) ((mFirstTargetHeight - currentHeight) / mDensity);// dip
if (offset <= 0) {
alpha = 1f;
} else if (offset <= 16) {
alpha = 1 - offset / 16f;
}
mFmDescrptionText.setAlpha(alpha);
newMargin = getNewSize(currentHeight, mTargetHeight, mFmDescrptionMarginTopTarget,
mFmDescrptionPaddingRate);
lastHeight = setNewPadding(mFmDescrptionText, newMargin);
// 2. frequency text (text size and margin)
newTextSize = getNewSize(currentHeight, mTargetHeight, mFrequencyTextSizeTarget,
mFrequencyTextSizeRate);
mFrequencyText.setTextSize(newTextSize / mDensity);
newMargin = getNewSize(currentHeight, mTargetHeight, mFrequencyMarginTopTarget,
mFrequencyPaddingRate);
lastHeight = setNewPadding(mFrequencyText, newMargin + lastHeight);
// If frequency text move to action bar, change it to bold
setNewTypefaceForFrequencyText();
// 3. station name (text size and margin)
newTextSize = getNewSize(currentHeight, mTargetHeight, mStationNameTextSizeTarget,
mStationNameTextSizeRate);
mStationNameText.setTextSize(newTextSize / mDensity);
newMargin = getNewSize(currentHeight, mTargetHeight, mStationNameMarginTopTarget,
mStationNamePaddingRate);
// if move to target position, need not move over the edge of actionbar
if (lastHeight <= mActionBarHeight) {
lastHeight = mActionBarHeight;
}
lastHeight = setNewPadding(mStationNameText, newMargin + lastHeight);
// 4. station rds (margin)
newMargin = getNewSize(currentHeight, mTargetHeight, mStationRdsMarginTopTarget,
mStationRdsPaddingRate);
lastHeight = setNewPadding(mStationRdsText, newMargin + lastHeight);
// 5. control buttons (margin)
newMargin = getNewSize(currentHeight, mTargetHeight, mControlViewMarginTopTarget,
mControlViewPaddingRate);
setNewPadding(mControlView, newMargin + lastHeight);
// 6. stop button (padding), it different to others, padding top refer to parent
newMargin = currentHeight - mPlayButtonHeight / 2;
setNewPadding(mPlayButtonView, newMargin);
}
}
private void setNewTypefaceForFrequencyText() {
boolean needBold = (mSecondTargetHeight == getHeaderHeight());
mFrequencyText.setTypeface(needBold ? Typeface.SANS_SERIF : mDefaultFrequencyTypeface);
}
private float setNewPadding(TextView current, float newMargin) {
current.setPadding(current.getPaddingLeft(), (int) (newMargin),
current.getPaddingRight(), current.getPaddingBottom());
float nextLayoutPadding = newMargin + current.getTextSize();
return nextLayoutPadding;
}
private void setNewPadding(View current, float newMargin) {
float newPadding = newMargin;
current.setPadding(current.getPaddingLeft(), (int) (newPadding),
current.getPaddingRight(), current.getPaddingBottom());
}
private float getNewSize(int currentHeight, int targetHeight,
float targetSize, float rate) {
if (currentHeight == targetHeight) {
return targetSize;
}
return targetSize + (currentHeight - targetHeight) * rate;
}
}
private final class ViewHolder {
ImageView mMoreButton;
FmVisualizerView mPlayIndicator;
TextView mStationFreq;
TextView mStationName;
View mPopupMenuAnchor;
}
}