| // CHECKSTYLE:OFF Generated code |
| /* This file is auto-generated from PlaybackFragment.java. DO NOT MODIFY. */ |
| |
| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
| * in compliance with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software distributed under the License |
| * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
| * or implied. See the License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| package android.support.v17.leanback.app; |
| |
| import android.animation.Animator; |
| import android.animation.AnimatorInflater; |
| import android.animation.TimeInterpolator; |
| import android.animation.ValueAnimator; |
| import android.animation.ValueAnimator.AnimatorUpdateListener; |
| import android.support.v4.app.Fragment; |
| import android.content.Context; |
| import android.graphics.Color; |
| import android.graphics.drawable.ColorDrawable; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.Message; |
| import android.support.v17.leanback.R; |
| import android.support.v17.leanback.animation.LogAccelerateInterpolator; |
| import android.support.v17.leanback.animation.LogDecelerateInterpolator; |
| import android.support.v17.leanback.media.PlaybackGlueHost; |
| import android.support.v17.leanback.widget.ArrayObjectAdapter; |
| import android.support.v17.leanback.widget.BaseOnItemViewClickedListener; |
| import android.support.v17.leanback.widget.BaseOnItemViewSelectedListener; |
| import android.support.v17.leanback.widget.ClassPresenterSelector; |
| import android.support.v17.leanback.widget.ItemBridgeAdapter; |
| import android.support.v17.leanback.widget.ObjectAdapter; |
| import android.support.v17.leanback.widget.PlaybackRowPresenter; |
| import android.support.v17.leanback.widget.Presenter; |
| import android.support.v17.leanback.widget.PresenterSelector; |
| import android.support.v17.leanback.widget.Row; |
| import android.support.v17.leanback.widget.RowPresenter; |
| import android.support.v17.leanback.widget.VerticalGridView; |
| import android.support.v7.widget.RecyclerView; |
| import android.util.Log; |
| import android.view.InputEvent; |
| import android.view.KeyEvent; |
| import android.view.LayoutInflater; |
| import android.view.MotionEvent; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.view.animation.AccelerateInterpolator; |
| |
| import java.util.ArrayList; |
| |
| /** |
| * A fragment for displaying playback controls and related content. |
| * |
| * <p> |
| * A PlaybackSupportFragment renders the elements of its {@link ObjectAdapter} as a set |
| * of rows in a vertical list. The Adapter's {@link PresenterSelector} must maintain subclasses |
| * of {@link RowPresenter}. |
| * </p> |
| * <p> |
| * An instance of {@link android.support.v17.leanback.widget.PlaybackControlsRow} is expected to be |
| * at position 0 in the adapter. |
| * </p> |
| */ |
| public class PlaybackSupportFragment extends Fragment { |
| /** |
| * No background. |
| */ |
| public static final int BG_NONE = 0; |
| |
| /** |
| * A dark translucent background. |
| */ |
| public static final int BG_DARK = 1; |
| private PlaybackGlueHost.HostCallback mHostCallback; |
| |
| /** |
| * Resets the focus on the button in the middle of control row. |
| * @hide |
| */ |
| public void resetFocus() { |
| ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder) getVerticalGridView() |
| .findViewHolderForAdapterPosition(0); |
| if (vh != null && vh.getPresenter() instanceof PlaybackRowPresenter) { |
| ((PlaybackRowPresenter) vh.getPresenter()).onReappear( |
| (RowPresenter.ViewHolder) vh.getViewHolder()); |
| } |
| } |
| |
| private class SetSelectionRunnable implements Runnable { |
| int mPosition; |
| boolean mSmooth = true; |
| |
| @Override |
| public void run() { |
| if (mRowsSupportFragment == null) { |
| return; |
| } |
| mRowsSupportFragment.setSelectedPosition(mPosition, mSmooth); |
| } |
| } |
| |
| /** |
| * A light translucent background. |
| */ |
| public static final int BG_LIGHT = 2; |
| private RowsSupportFragment mRowsSupportFragment; |
| private ObjectAdapter mAdapter; |
| private PlaybackRowPresenter mPresenter; |
| private Row mRow; |
| private BaseOnItemViewSelectedListener mExternalItemSelectedListener; |
| private BaseOnItemViewClickedListener mExternalItemClickedListener; |
| private BaseOnItemViewClickedListener mPlaybackItemClickedListener; |
| |
| private final BaseOnItemViewClickedListener mOnItemViewClickedListener = |
| new BaseOnItemViewClickedListener() { |
| @Override |
| public void onItemClicked(Presenter.ViewHolder itemViewHolder, |
| Object item, |
| RowPresenter.ViewHolder rowViewHolder, |
| Object row) { |
| if (mPlaybackItemClickedListener != null |
| && rowViewHolder instanceof PlaybackRowPresenter.ViewHolder) { |
| mPlaybackItemClickedListener.onItemClicked( |
| itemViewHolder, item, rowViewHolder, row); |
| } |
| if (mExternalItemClickedListener != null) { |
| mExternalItemClickedListener.onItemClicked( |
| itemViewHolder, item, rowViewHolder, row); |
| } |
| } |
| }; |
| |
| private final BaseOnItemViewSelectedListener mOnItemViewSelectedListener = |
| new BaseOnItemViewSelectedListener() { |
| @Override |
| public void onItemSelected(Presenter.ViewHolder itemViewHolder, |
| Object item, |
| RowPresenter.ViewHolder rowViewHolder, |
| Object row) { |
| if (mExternalItemSelectedListener != null) { |
| mExternalItemSelectedListener.onItemSelected( |
| itemViewHolder, item, rowViewHolder, row); |
| } |
| } |
| }; |
| |
| private final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable(); |
| |
| public ObjectAdapter getAdapter() { |
| return mAdapter; |
| } |
| |
| /** |
| * Listener allowing the application to receive notification of fade in and/or fade out |
| * completion events. |
| * @hide |
| */ |
| public static class OnFadeCompleteListener { |
| public void onFadeInComplete() { |
| } |
| |
| public void onFadeOutComplete() { |
| } |
| } |
| |
| private static final String TAG = "PlaybackSupportFragment"; |
| private static final boolean DEBUG = false; |
| private static final int ANIMATION_MULTIPLIER = 1; |
| |
| private static int START_FADE_OUT = 1; |
| |
| // Fading status |
| private static final int IDLE = 0; |
| private static final int IN = 1; |
| private static final int OUT = 2; |
| |
| private int mPaddingTop; |
| private int mPaddingBottom; |
| private View mRootView; |
| private int mBackgroundType = BG_DARK; |
| private int mBgDarkColor; |
| private int mBgLightColor; |
| private int mShowTimeMs; |
| private int mMajorFadeTranslateY, mMinorFadeTranslateY; |
| private int mAnimationTranslateY; |
| private OnFadeCompleteListener mFadeCompleteListener; |
| private View.OnKeyListener mInputEventHandler; |
| private boolean mFadingEnabled = true; |
| private int mFadingStatus = IDLE; |
| private int mBgAlpha; |
| private ValueAnimator mBgFadeInAnimator, mBgFadeOutAnimator; |
| private ValueAnimator mControlRowFadeInAnimator, mControlRowFadeOutAnimator; |
| private ValueAnimator mOtherRowFadeInAnimator, mOtherRowFadeOutAnimator; |
| |
| private final Animator.AnimatorListener mFadeListener = |
| new Animator.AnimatorListener() { |
| @Override |
| public void onAnimationStart(Animator animation) { |
| enableVerticalGridAnimations(false); |
| } |
| |
| @Override |
| public void onAnimationRepeat(Animator animation) { |
| } |
| |
| @Override |
| public void onAnimationCancel(Animator animation) { |
| } |
| |
| @Override |
| public void onAnimationEnd(Animator animation) { |
| if (DEBUG) Log.v(TAG, "onAnimationEnd " + mBgAlpha); |
| if (mBgAlpha > 0) { |
| enableVerticalGridAnimations(true); |
| startFadeTimer(); |
| if (mFadeCompleteListener != null) { |
| mFadeCompleteListener.onFadeInComplete(); |
| } |
| } else { |
| VerticalGridView verticalView = getVerticalGridView(); |
| // reset focus to the primary actions only if the selected row was the controls row |
| if (verticalView != null && verticalView.getSelectedPosition() == 0) { |
| ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder) |
| verticalView.findViewHolderForAdapterPosition(0); |
| if (vh != null && vh.getPresenter() instanceof PlaybackRowPresenter) { |
| ((PlaybackRowPresenter)vh.getPresenter()).onReappear( |
| (RowPresenter.ViewHolder) vh.getViewHolder()); |
| } |
| } |
| if (mFadeCompleteListener != null) { |
| mFadeCompleteListener.onFadeOutComplete(); |
| } |
| } |
| mFadingStatus = IDLE; |
| } |
| }; |
| |
| VerticalGridView getVerticalGridView() { |
| if (mRowsSupportFragment == null) { |
| return null; |
| } |
| return mRowsSupportFragment.getVerticalGridView(); |
| } |
| |
| private final Handler mHandler = new Handler() { |
| @Override |
| public void handleMessage(Message message) { |
| if (message.what == START_FADE_OUT && mFadingEnabled) { |
| fade(false); |
| } |
| } |
| }; |
| |
| private final VerticalGridView.OnTouchInterceptListener mOnTouchInterceptListener = |
| new VerticalGridView.OnTouchInterceptListener() { |
| @Override |
| public boolean onInterceptTouchEvent(MotionEvent event) { |
| return onInterceptInputEvent(event); |
| } |
| }; |
| |
| private final VerticalGridView.OnKeyInterceptListener mOnKeyInterceptListener = |
| new VerticalGridView.OnKeyInterceptListener() { |
| @Override |
| public boolean onInterceptKeyEvent(KeyEvent event) { |
| return onInterceptInputEvent(event); |
| } |
| }; |
| |
| private void setBgAlpha(int alpha) { |
| mBgAlpha = alpha; |
| if (mRootView != null) { |
| mRootView.getBackground().setAlpha(alpha); |
| } |
| } |
| |
| private void enableVerticalGridAnimations(boolean enable) { |
| if (getVerticalGridView() != null) { |
| getVerticalGridView().setAnimateChildLayout(enable); |
| } |
| } |
| |
| /** |
| * Enables or disables view fading. If enabled, |
| * the view will be faded in when the fragment starts, |
| * and will fade out after a time period. The timeout |
| * period is reset each time {@link #tickle} is called. |
| */ |
| public void setFadingEnabled(boolean enabled) { |
| if (DEBUG) Log.v(TAG, "setFadingEnabled " + enabled); |
| if (enabled != mFadingEnabled) { |
| mFadingEnabled = enabled; |
| if (mFadingEnabled) { |
| if (isResumed() && mFadingStatus == IDLE |
| && !mHandler.hasMessages(START_FADE_OUT)) { |
| startFadeTimer(); |
| } |
| } else { |
| // Ensure fully opaque |
| mHandler.removeMessages(START_FADE_OUT); |
| fade(true); |
| } |
| } |
| } |
| |
| /** |
| * Returns true if view fading is enabled. |
| */ |
| public boolean isFadingEnabled() { |
| return mFadingEnabled; |
| } |
| |
| /** |
| * Sets the listener to be called when fade in or out has completed. |
| * @hide |
| */ |
| public void setFadeCompleteListener(OnFadeCompleteListener listener) { |
| mFadeCompleteListener = listener; |
| } |
| |
| /** |
| * Returns the listener to be called when fade in or out has completed. |
| * @hide |
| */ |
| public OnFadeCompleteListener getFadeCompleteListener() { |
| return mFadeCompleteListener; |
| } |
| |
| /** |
| * Sets the input event handler. |
| */ |
| public final void setOnKeyInterceptListener(View.OnKeyListener handler) { |
| mInputEventHandler = handler; |
| } |
| |
| /** |
| * Tickles the playback controls. Fades in the view if it was faded out, |
| * otherwise resets the fade out timer. Tickling on input events is handled |
| * by the fragment. |
| */ |
| public void tickle() { |
| if (DEBUG) Log.v(TAG, "tickle enabled " + mFadingEnabled + " isResumed " + isResumed()); |
| if (!mFadingEnabled || !isResumed()) { |
| return; |
| } |
| if (mHandler.hasMessages(START_FADE_OUT)) { |
| // Restart the timer |
| startFadeTimer(); |
| } else { |
| fade(true); |
| } |
| } |
| |
| /** |
| * Fades out the playback overlay immediately. |
| */ |
| public void fadeOut() { |
| mHandler.removeMessages(START_FADE_OUT); |
| fade(false); |
| } |
| |
| /** |
| * Returns true/false indicating whether playback controls are visible or not. |
| */ |
| private boolean areControlsHidden() { |
| return mFadingStatus == IDLE && mBgAlpha == 0; |
| } |
| |
| private boolean onInterceptInputEvent(InputEvent event) { |
| final boolean controlsHidden = areControlsHidden(); |
| if (DEBUG) Log.v(TAG, "onInterceptInputEvent hidden " + controlsHidden + " " + event); |
| boolean consumeEvent = false; |
| int keyCode = KeyEvent.KEYCODE_UNKNOWN; |
| |
| if (event instanceof KeyEvent) { |
| keyCode = ((KeyEvent) event).getKeyCode(); |
| if (mInputEventHandler != null) { |
| consumeEvent = mInputEventHandler.onKey(getView(), keyCode, (KeyEvent) event); |
| } |
| } |
| |
| switch (keyCode) { |
| case KeyEvent.KEYCODE_DPAD_CENTER: |
| case KeyEvent.KEYCODE_DPAD_DOWN: |
| case KeyEvent.KEYCODE_DPAD_UP: |
| case KeyEvent.KEYCODE_DPAD_LEFT: |
| case KeyEvent.KEYCODE_DPAD_RIGHT: |
| // Event may be consumed; regardless, if controls are hidden then these keys will |
| // bring up the controls. |
| if (controlsHidden) { |
| consumeEvent = true; |
| } |
| tickle(); |
| break; |
| case KeyEvent.KEYCODE_BACK: |
| case KeyEvent.KEYCODE_ESCAPE: |
| // If fading enabled and controls are not hidden, back will be consumed to fade |
| // them out (even if the key was consumed by the handler). |
| if (mFadingEnabled && !controlsHidden) { |
| consumeEvent = true; |
| mHandler.removeMessages(START_FADE_OUT); |
| fade(false); |
| } else if (consumeEvent) { |
| tickle(); |
| } |
| break; |
| default: |
| if (consumeEvent) { |
| tickle(); |
| } |
| } |
| return consumeEvent; |
| } |
| |
| @Override |
| public void onResume() { |
| super.onResume(); |
| if (mFadingEnabled) { |
| setBgAlpha(0); |
| fade(true); |
| } |
| getVerticalGridView().setOnTouchInterceptListener(mOnTouchInterceptListener); |
| getVerticalGridView().setOnKeyInterceptListener(mOnKeyInterceptListener); |
| if (mHostCallback != null) { |
| mHostCallback.onHostResume(); |
| } |
| } |
| |
| private void startFadeTimer() { |
| if (mHandler != null) { |
| mHandler.removeMessages(START_FADE_OUT); |
| mHandler.sendEmptyMessageDelayed(START_FADE_OUT, mShowTimeMs); |
| } |
| } |
| |
| private static ValueAnimator loadAnimator(Context context, int resId) { |
| ValueAnimator animator = (ValueAnimator) AnimatorInflater.loadAnimator(context, resId); |
| animator.setDuration(animator.getDuration() * ANIMATION_MULTIPLIER); |
| return animator; |
| } |
| |
| private void loadBgAnimator() { |
| AnimatorUpdateListener listener = new AnimatorUpdateListener() { |
| @Override |
| public void onAnimationUpdate(ValueAnimator arg0) { |
| setBgAlpha((Integer) arg0.getAnimatedValue()); |
| } |
| }; |
| |
| Context context = getContext(); |
| mBgFadeInAnimator = loadAnimator(context, R.animator.lb_playback_bg_fade_in); |
| mBgFadeInAnimator.addUpdateListener(listener); |
| mBgFadeInAnimator.addListener(mFadeListener); |
| |
| mBgFadeOutAnimator = loadAnimator(context, R.animator.lb_playback_bg_fade_out); |
| mBgFadeOutAnimator.addUpdateListener(listener); |
| mBgFadeOutAnimator.addListener(mFadeListener); |
| } |
| |
| private TimeInterpolator mLogDecelerateInterpolator = new LogDecelerateInterpolator(100, 0); |
| private TimeInterpolator mLogAccelerateInterpolator = new LogAccelerateInterpolator(100, 0); |
| |
| private View getControlRowView() { |
| if (getVerticalGridView() == null) { |
| return null; |
| } |
| RecyclerView.ViewHolder vh = getVerticalGridView().findViewHolderForPosition(0); |
| if (vh == null) { |
| return null; |
| } |
| return vh.itemView; |
| } |
| |
| private void loadControlRowAnimator() { |
| final AnimatorListener listener = new AnimatorListener() { |
| @Override |
| void getViews(ArrayList<View> views) { |
| View view = getControlRowView(); |
| if (view != null) { |
| views.add(view); |
| } |
| } |
| }; |
| final AnimatorUpdateListener updateListener = new AnimatorUpdateListener() { |
| @Override |
| public void onAnimationUpdate(ValueAnimator arg0) { |
| View view = getControlRowView(); |
| if (view != null) { |
| final float fraction = (Float) arg0.getAnimatedValue(); |
| if (DEBUG) Log.v(TAG, "fraction " + fraction); |
| view.setAlpha(fraction); |
| view.setTranslationY((float) mAnimationTranslateY * (1f - fraction)); |
| } |
| } |
| }; |
| |
| Context context = getContext(); |
| mControlRowFadeInAnimator = loadAnimator(context, R.animator.lb_playback_controls_fade_in); |
| mControlRowFadeInAnimator.addUpdateListener(updateListener); |
| mControlRowFadeInAnimator.addListener(listener); |
| mControlRowFadeInAnimator.setInterpolator(mLogDecelerateInterpolator); |
| |
| mControlRowFadeOutAnimator = loadAnimator(context, |
| R.animator.lb_playback_controls_fade_out); |
| mControlRowFadeOutAnimator.addUpdateListener(updateListener); |
| mControlRowFadeOutAnimator.addListener(listener); |
| mControlRowFadeOutAnimator.setInterpolator(mLogAccelerateInterpolator); |
| } |
| |
| private void loadOtherRowAnimator() { |
| final AnimatorListener listener = new AnimatorListener() { |
| @Override |
| void getViews(ArrayList<View> views) { |
| if (getVerticalGridView() == null) { |
| return; |
| } |
| final int count = getVerticalGridView().getChildCount(); |
| for (int i = 0; i < count; i++) { |
| View view = getVerticalGridView().getChildAt(i); |
| if (view != null) { |
| views.add(view); |
| } |
| } |
| } |
| }; |
| final AnimatorUpdateListener updateListener = new AnimatorUpdateListener() { |
| @Override |
| public void onAnimationUpdate(ValueAnimator arg0) { |
| if (getVerticalGridView() == null) { |
| return; |
| } |
| final float fraction = (Float) arg0.getAnimatedValue(); |
| for (View view : listener.mViews) { |
| if (getVerticalGridView().getChildPosition(view) > 0) { |
| view.setAlpha(fraction); |
| view.setTranslationY((float) mAnimationTranslateY * (1f - fraction)); |
| } |
| } |
| } |
| }; |
| |
| Context context = getContext(); |
| mOtherRowFadeInAnimator = loadAnimator(context, R.animator.lb_playback_controls_fade_in); |
| mOtherRowFadeInAnimator.addListener(listener); |
| mOtherRowFadeInAnimator.addUpdateListener(updateListener); |
| mOtherRowFadeInAnimator.setInterpolator(mLogDecelerateInterpolator); |
| |
| mOtherRowFadeOutAnimator = loadAnimator(context, R.animator.lb_playback_controls_fade_out); |
| mOtherRowFadeOutAnimator.addListener(listener); |
| mOtherRowFadeOutAnimator.addUpdateListener(updateListener); |
| mOtherRowFadeOutAnimator.setInterpolator(new AccelerateInterpolator()); |
| } |
| |
| private void fade(boolean fadeIn) { |
| if (DEBUG) Log.v(TAG, "fade " + fadeIn); |
| if (getView() == null) { |
| return; |
| } |
| if ((fadeIn && mFadingStatus == IN) || (!fadeIn && mFadingStatus == OUT)) { |
| if (DEBUG) Log.v(TAG, "requested fade in progress"); |
| return; |
| } |
| if ((fadeIn && mBgAlpha == 255) || (!fadeIn && mBgAlpha == 0)) { |
| if (DEBUG) Log.v(TAG, "fade is no-op"); |
| return; |
| } |
| |
| mAnimationTranslateY = getVerticalGridView().getSelectedPosition() == 0 |
| ? mMajorFadeTranslateY : mMinorFadeTranslateY; |
| |
| if (mFadingStatus == IDLE) { |
| if (fadeIn) { |
| mBgFadeInAnimator.start(); |
| mControlRowFadeInAnimator.start(); |
| mOtherRowFadeInAnimator.start(); |
| } else { |
| mBgFadeOutAnimator.start(); |
| mControlRowFadeOutAnimator.start(); |
| mOtherRowFadeOutAnimator.start(); |
| } |
| } else { |
| if (fadeIn) { |
| mBgFadeOutAnimator.reverse(); |
| mControlRowFadeOutAnimator.reverse(); |
| mOtherRowFadeOutAnimator.reverse(); |
| } else { |
| mBgFadeInAnimator.reverse(); |
| mControlRowFadeInAnimator.reverse(); |
| mOtherRowFadeInAnimator.reverse(); |
| } |
| } |
| |
| // If fading in while control row is focused, set initial translationY so |
| // views slide in from below. |
| if (fadeIn && mFadingStatus == IDLE) { |
| final int count = getVerticalGridView().getChildCount(); |
| for (int i = 0; i < count; i++) { |
| getVerticalGridView().getChildAt(i).setTranslationY(mAnimationTranslateY); |
| } |
| } |
| |
| mFadingStatus = fadeIn ? IN : OUT; |
| } |
| |
| /** |
| * Sets the selected row position with smooth animation. |
| */ |
| public void setSelectedPosition(int position) { |
| setSelectedPosition(position, true); |
| } |
| |
| /** |
| * Sets the selected row position. |
| */ |
| public void setSelectedPosition(int position, boolean smooth) { |
| mSetSelectionRunnable.mPosition = position; |
| mSetSelectionRunnable.mSmooth = smooth; |
| if (getView() != null && getView().getHandler() != null) { |
| getView().getHandler().post(mSetSelectionRunnable); |
| } |
| } |
| |
| private void setupChildFragmentLayout() { |
| setVerticalGridViewLayout(mRowsSupportFragment.getVerticalGridView()); |
| } |
| |
| void setVerticalGridViewLayout(VerticalGridView listview) { |
| if (listview == null) { |
| return; |
| } |
| // Padding affects alignment when last row is focused |
| // (last is first when there's only one row). |
| setPadding(listview, mPaddingTop, mPaddingBottom); |
| |
| // Item alignment affects focused row that isn't the last. |
| listview.setItemAlignmentOffset(0); |
| listview.setItemAlignmentOffsetPercent(50); |
| |
| // Push rows to the bottom. |
| listview.setWindowAlignmentOffset(0); |
| listview.setWindowAlignmentOffsetPercent(50); |
| listview.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE); |
| } |
| |
| private static void setPadding(View view, int paddingTop, int paddingBottom) { |
| view.setPadding(view.getPaddingLeft(), paddingTop, |
| view.getPaddingRight(), paddingBottom); |
| } |
| |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| |
| mPaddingTop = |
| getResources().getDimensionPixelSize(R.dimen.lb_playback_controls_padding_top); |
| mPaddingBottom = |
| getResources().getDimensionPixelSize(R.dimen.lb_playback_controls_padding_bottom); |
| mBgDarkColor = |
| getResources().getColor(R.color.lb_playback_controls_background_dark); |
| mBgLightColor = |
| getResources().getColor(R.color.lb_playback_controls_background_light); |
| mShowTimeMs = |
| getResources().getInteger(R.integer.lb_playback_controls_show_time_ms); |
| mMajorFadeTranslateY = |
| getResources().getDimensionPixelSize(R.dimen.lb_playback_major_fade_translate_y); |
| mMinorFadeTranslateY = |
| getResources().getDimensionPixelSize(R.dimen.lb_playback_minor_fade_translate_y); |
| |
| loadBgAnimator(); |
| loadControlRowAnimator(); |
| loadOtherRowAnimator(); |
| } |
| |
| /** |
| * Sets the background type. |
| * |
| * @param type One of BG_LIGHT, BG_DARK, or BG_NONE. |
| */ |
| public void setBackgroundType(int type) { |
| switch (type) { |
| case BG_LIGHT: |
| case BG_DARK: |
| case BG_NONE: |
| if (type != mBackgroundType) { |
| mBackgroundType = type; |
| updateBackground(); |
| } |
| break; |
| default: |
| throw new IllegalArgumentException("Invalid background type"); |
| } |
| } |
| |
| /** |
| * Returns the background type. |
| */ |
| public int getBackgroundType() { |
| return mBackgroundType; |
| } |
| |
| private void updateBackground() { |
| if (mRootView != null) { |
| int color = mBgDarkColor; |
| switch (mBackgroundType) { |
| case BG_DARK: |
| break; |
| case BG_LIGHT: |
| color = mBgLightColor; |
| break; |
| case BG_NONE: |
| color = Color.TRANSPARENT; |
| break; |
| } |
| mRootView.setBackground(new ColorDrawable(color)); |
| } |
| } |
| |
| private final ItemBridgeAdapter.AdapterListener mAdapterListener = |
| new ItemBridgeAdapter.AdapterListener() { |
| @Override |
| public void onAttachedToWindow(ItemBridgeAdapter.ViewHolder vh) { |
| if (DEBUG) Log.v(TAG, "onAttachedToWindow " + vh.getViewHolder().view); |
| if ((mFadingStatus == IDLE && mBgAlpha == 0) || mFadingStatus == OUT) { |
| if (DEBUG) Log.v(TAG, "setting alpha to 0"); |
| vh.getViewHolder().view.setAlpha(0); |
| } |
| } |
| |
| @Override |
| public void onDetachedFromWindow(ItemBridgeAdapter.ViewHolder vh) { |
| if (DEBUG) Log.v(TAG, "onDetachedFromWindow " + vh.getViewHolder().view); |
| // Reset animation state |
| vh.getViewHolder().view.setAlpha(1f); |
| vh.getViewHolder().view.setTranslationY(0); |
| vh.getViewHolder().view.setAlpha(1f); |
| } |
| |
| @Override |
| public void onBind(ItemBridgeAdapter.ViewHolder vh) { |
| } |
| }; |
| |
| @Override |
| public View onCreateView(LayoutInflater inflater, ViewGroup container, |
| Bundle savedInstanceState) { |
| mRootView = inflater.inflate(R.layout.lb_playback_fragment, container, false); |
| mRowsSupportFragment = (RowsSupportFragment) getChildFragmentManager().findFragmentById( |
| R.id.playback_controls_dock); |
| if (mRowsSupportFragment == null) { |
| mRowsSupportFragment = new RowsSupportFragment(); |
| getChildFragmentManager().beginTransaction() |
| .replace(R.id.playback_controls_dock, mRowsSupportFragment) |
| .commit(); |
| } |
| if (mAdapter == null) { |
| setAdapter(new ArrayObjectAdapter(new ClassPresenterSelector())); |
| } else { |
| mRowsSupportFragment.setAdapter(mAdapter); |
| } |
| mRowsSupportFragment.setOnItemViewSelectedListener(mOnItemViewSelectedListener); |
| mRowsSupportFragment.setOnItemViewClickedListener(mOnItemViewClickedListener); |
| |
| mBgAlpha = 255; |
| updateBackground(); |
| mRowsSupportFragment.setExternalAdapterListener(mAdapterListener); |
| return mRootView; |
| } |
| |
| /** |
| * Sets the {@link PlaybackGlueHost.HostCallback}. Implementor of this interface will |
| * take appropriate actions to take action when the hosting fragment starts/stops processing. |
| */ |
| public void setHostCallback(PlaybackGlueHost.HostCallback hostCallback) { |
| this.mHostCallback = hostCallback; |
| } |
| |
| @Override |
| public void onStart() { |
| super.onStart(); |
| setupChildFragmentLayout(); |
| mRowsSupportFragment.setAdapter(mAdapter); |
| if (mHostCallback != null) { |
| mHostCallback.onHostStart(); |
| } |
| } |
| |
| @Override |
| public void onStop() { |
| if (mHostCallback != null) { |
| mHostCallback.onHostStop(); |
| } |
| super.onStop(); |
| } |
| |
| @Override |
| public void onPause() { |
| if (mHostCallback != null) { |
| mHostCallback.onHostPause(); |
| } |
| super.onPause(); |
| } |
| |
| /** |
| * This listener is called every time there is a selection in {@link RowsSupportFragment}. This can |
| * be used by users to take additional actions such as animations. |
| */ |
| public void setOnItemViewSelectedListener(final BaseOnItemViewSelectedListener listener) { |
| mExternalItemSelectedListener = listener; |
| } |
| |
| /** |
| * This listener is called every time there is a click in {@link RowsSupportFragment}. This can |
| * be used by users to take additional actions such as animations. |
| */ |
| public void setOnItemViewClickedListener(final BaseOnItemViewClickedListener listener) { |
| mExternalItemClickedListener = listener; |
| } |
| |
| /** |
| * Sets the {@link BaseOnItemViewClickedListener} that would be invoked for clicks |
| * only on {@link android.support.v17.leanback.widget.PlaybackRowPresenter.ViewHolder}. |
| */ |
| public void setOnPlaybackItemViewClickedListener(final BaseOnItemViewClickedListener listener) { |
| mPlaybackItemClickedListener = listener; |
| } |
| |
| @Override |
| public void onDestroyView() { |
| mRootView = null; |
| super.onDestroyView(); |
| } |
| |
| @Override |
| public void onDestroy() { |
| if (mHostCallback != null) { |
| mHostCallback.onHostDestroy(); |
| } |
| super.onDestroy(); |
| } |
| |
| /** |
| * Sets the playback row for the playback controls. |
| */ |
| public void setPlaybackRow(Row row) { |
| this.mRow = row; |
| setupRow(); |
| setupPresenter(); |
| } |
| |
| /** |
| * Sets the presenter for rendering the playback controls. |
| */ |
| public void setPlaybackRowPresenter(PlaybackRowPresenter presenter) { |
| this.mPresenter = presenter; |
| setupPresenter(); |
| } |
| |
| /** |
| * Updates the ui when the row data changes. |
| */ |
| public void notifyPlaybackRowChanged() { |
| if (mAdapter == null) { |
| return; |
| } |
| mAdapter.notifyItemRangeChanged(0, 1); |
| } |
| |
| /** |
| * Sets the list of rows for the fragment. |
| */ |
| public void setAdapter(ObjectAdapter adapter) { |
| mAdapter = adapter; |
| setupRow(); |
| setupPresenter(); |
| if (mRowsSupportFragment != null) { |
| mRowsSupportFragment.setAdapter(adapter); |
| } |
| } |
| |
| private void setupRow() { |
| if (mAdapter instanceof ArrayObjectAdapter && mRow != null) { |
| ArrayObjectAdapter adapter = ((ArrayObjectAdapter) mAdapter); |
| if (adapter.size() == 0) { |
| adapter.add(mRow); |
| } else { |
| adapter.replace(0, mRow); |
| } |
| } |
| } |
| |
| private void setupPresenter() { |
| if (mAdapter != null && mRow != null && mPresenter != null) { |
| PresenterSelector selector = mAdapter.getPresenterSelector(); |
| if (selector == null) { |
| selector = new ClassPresenterSelector(); |
| mAdapter.setPresenterSelector(selector); |
| } |
| |
| if (selector instanceof ClassPresenterSelector) { |
| ((ClassPresenterSelector) selector).addClassPresenter(mRow.getClass(), mPresenter); |
| } |
| } |
| } |
| |
| static abstract class AnimatorListener implements Animator.AnimatorListener { |
| ArrayList<View> mViews = new ArrayList<View>(); |
| ArrayList<Integer> mLayerType = new ArrayList<Integer>(); |
| |
| @Override |
| public void onAnimationCancel(Animator animation) { |
| } |
| |
| @Override |
| public void onAnimationRepeat(Animator animation) { |
| } |
| |
| @Override |
| public void onAnimationStart(Animator animation) { |
| getViews(mViews); |
| for (View view : mViews) { |
| mLayerType.add(view.getLayerType()); |
| view.setLayerType(View.LAYER_TYPE_HARDWARE, null); |
| } |
| } |
| |
| @Override |
| public void onAnimationEnd(Animator animation) { |
| for (int i = 0; i < mViews.size(); i++) { |
| mViews.get(i).setLayerType(mLayerType.get(i), null); |
| } |
| mLayerType.clear(); |
| mViews.clear(); |
| } |
| |
| abstract void getViews(ArrayList<View> views); |
| } |
| } |