API review: move PlaybackGlue classes into media package
am: 95ac470f2b

Change-Id: I2142802e1ebeec63750f13919a5da67a19578c3b
diff --git a/api/25.1.0.txt b/api/25.1.0.txt
index 38141fe..113e6c8 100644
--- a/api/25.1.0.txt
+++ b/api/25.1.0.txt
@@ -858,6 +858,173 @@
 
 }
 
+package android.support.media {
+
+  public class ExifInterface {
+    ctor public ExifInterface(java.lang.String) throws java.io.IOException;
+    ctor public ExifInterface(java.io.InputStream) throws java.io.IOException;
+    method public double getAltitude(double);
+    method public java.lang.String getAttribute(java.lang.String);
+    method public double getAttributeDouble(java.lang.String, double);
+    method public int getAttributeInt(java.lang.String, int);
+    method public boolean getLatLong(float[]);
+    method public byte[] getThumbnail();
+    method public android.graphics.Bitmap getThumbnailBitmap();
+    method public byte[] getThumbnailBytes();
+    method public long[] getThumbnailRange();
+    method public boolean hasThumbnail();
+    method public boolean isThumbnailCompressed();
+    method public void saveAttributes() throws java.io.IOException;
+    method public void setAttribute(java.lang.String, java.lang.String);
+    field public static final int ORIENTATION_FLIP_HORIZONTAL = 2; // 0x2
+    field public static final int ORIENTATION_FLIP_VERTICAL = 4; // 0x4
+    field public static final int ORIENTATION_NORMAL = 1; // 0x1
+    field public static final int ORIENTATION_ROTATE_180 = 3; // 0x3
+    field public static final int ORIENTATION_ROTATE_270 = 8; // 0x8
+    field public static final int ORIENTATION_ROTATE_90 = 6; // 0x6
+    field public static final int ORIENTATION_TRANSPOSE = 5; // 0x5
+    field public static final int ORIENTATION_TRANSVERSE = 7; // 0x7
+    field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
+    field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue";
+    field public static final java.lang.String TAG_ARTIST = "Artist";
+    field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample";
+    field public static final java.lang.String TAG_BRIGHTNESS_VALUE = "BrightnessValue";
+    field public static final java.lang.String TAG_CFA_PATTERN = "CFAPattern";
+    field public static final java.lang.String TAG_COLOR_SPACE = "ColorSpace";
+    field public static final java.lang.String TAG_COMPONENTS_CONFIGURATION = "ComponentsConfiguration";
+    field public static final java.lang.String TAG_COMPRESSED_BITS_PER_PIXEL = "CompressedBitsPerPixel";
+    field public static final java.lang.String TAG_COMPRESSION = "Compression";
+    field public static final java.lang.String TAG_CONTRAST = "Contrast";
+    field public static final java.lang.String TAG_COPYRIGHT = "Copyright";
+    field public static final java.lang.String TAG_CUSTOM_RENDERED = "CustomRendered";
+    field public static final java.lang.String TAG_DATETIME = "DateTime";
+    field public static final java.lang.String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
+    field public static final java.lang.String TAG_DATETIME_ORIGINAL = "DateTimeOriginal";
+    field public static final java.lang.String TAG_DEFAULT_CROP_SIZE = "DefaultCropSize";
+    field public static final java.lang.String TAG_DEVICE_SETTING_DESCRIPTION = "DeviceSettingDescription";
+    field public static final java.lang.String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio";
+    field public static final java.lang.String TAG_DNG_VERSION = "DNGVersion";
+    field public static final java.lang.String TAG_EXIF_VERSION = "ExifVersion";
+    field public static final java.lang.String TAG_EXPOSURE_BIAS_VALUE = "ExposureBiasValue";
+    field public static final java.lang.String TAG_EXPOSURE_INDEX = "ExposureIndex";
+    field public static final java.lang.String TAG_EXPOSURE_MODE = "ExposureMode";
+    field public static final java.lang.String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
+    field public static final java.lang.String TAG_EXPOSURE_TIME = "ExposureTime";
+    field public static final java.lang.String TAG_FILE_SOURCE = "FileSource";
+    field public static final java.lang.String TAG_FLASH = "Flash";
+    field public static final java.lang.String TAG_FLASHPIX_VERSION = "FlashpixVersion";
+    field public static final java.lang.String TAG_FLASH_ENERGY = "FlashEnergy";
+    field public static final java.lang.String TAG_FOCAL_LENGTH = "FocalLength";
+    field public static final java.lang.String TAG_FOCAL_LENGTH_IN_35MM_FILM = "FocalLengthIn35mmFilm";
+    field public static final java.lang.String TAG_FOCAL_PLANE_RESOLUTION_UNIT = "FocalPlaneResolutionUnit";
+    field public static final java.lang.String TAG_FOCAL_PLANE_X_RESOLUTION = "FocalPlaneXResolution";
+    field public static final java.lang.String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution";
+    field public static final java.lang.String TAG_F_NUMBER = "FNumber";
+    field public static final java.lang.String TAG_GAIN_CONTROL = "GainControl";
+    field public static final java.lang.String TAG_GPS_ALTITUDE = "GPSAltitude";
+    field public static final java.lang.String TAG_GPS_ALTITUDE_REF = "GPSAltitudeRef";
+    field public static final java.lang.String TAG_GPS_AREA_INFORMATION = "GPSAreaInformation";
+    field public static final java.lang.String TAG_GPS_DATESTAMP = "GPSDateStamp";
+    field public static final java.lang.String TAG_GPS_DEST_BEARING = "GPSDestBearing";
+    field public static final java.lang.String TAG_GPS_DEST_BEARING_REF = "GPSDestBearingRef";
+    field public static final java.lang.String TAG_GPS_DEST_DISTANCE = "GPSDestDistance";
+    field public static final java.lang.String TAG_GPS_DEST_DISTANCE_REF = "GPSDestDistanceRef";
+    field public static final java.lang.String TAG_GPS_DEST_LATITUDE = "GPSDestLatitude";
+    field public static final java.lang.String TAG_GPS_DEST_LATITUDE_REF = "GPSDestLatitudeRef";
+    field public static final java.lang.String TAG_GPS_DEST_LONGITUDE = "GPSDestLongitude";
+    field public static final java.lang.String TAG_GPS_DEST_LONGITUDE_REF = "GPSDestLongitudeRef";
+    field public static final java.lang.String TAG_GPS_DIFFERENTIAL = "GPSDifferential";
+    field public static final java.lang.String TAG_GPS_DOP = "GPSDOP";
+    field public static final java.lang.String TAG_GPS_IMG_DIRECTION = "GPSImgDirection";
+    field public static final java.lang.String TAG_GPS_IMG_DIRECTION_REF = "GPSImgDirectionRef";
+    field public static final java.lang.String TAG_GPS_LATITUDE = "GPSLatitude";
+    field public static final java.lang.String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
+    field public static final java.lang.String TAG_GPS_LONGITUDE = "GPSLongitude";
+    field public static final java.lang.String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
+    field public static final java.lang.String TAG_GPS_MAP_DATUM = "GPSMapDatum";
+    field public static final java.lang.String TAG_GPS_MEASURE_MODE = "GPSMeasureMode";
+    field public static final java.lang.String TAG_GPS_PROCESSING_METHOD = "GPSProcessingMethod";
+    field public static final java.lang.String TAG_GPS_SATELLITES = "GPSSatellites";
+    field public static final java.lang.String TAG_GPS_SPEED = "GPSSpeed";
+    field public static final java.lang.String TAG_GPS_SPEED_REF = "GPSSpeedRef";
+    field public static final java.lang.String TAG_GPS_STATUS = "GPSStatus";
+    field public static final java.lang.String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
+    field public static final java.lang.String TAG_GPS_TRACK = "GPSTrack";
+    field public static final java.lang.String TAG_GPS_TRACK_REF = "GPSTrackRef";
+    field public static final java.lang.String TAG_GPS_VERSION_ID = "GPSVersionID";
+    field public static final java.lang.String TAG_IMAGE_DESCRIPTION = "ImageDescription";
+    field public static final java.lang.String TAG_IMAGE_LENGTH = "ImageLength";
+    field public static final java.lang.String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
+    field public static final java.lang.String TAG_IMAGE_WIDTH = "ImageWidth";
+    field public static final java.lang.String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
+    field public static final java.lang.String TAG_ISO_SPEED_RATINGS = "ISOSpeedRatings";
+    field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
+    field public static final java.lang.String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
+    field public static final java.lang.String TAG_LIGHT_SOURCE = "LightSource";
+    field public static final java.lang.String TAG_MAKE = "Make";
+    field public static final java.lang.String TAG_MAKER_NOTE = "MakerNote";
+    field public static final java.lang.String TAG_MAX_APERTURE_VALUE = "MaxApertureValue";
+    field public static final java.lang.String TAG_METERING_MODE = "MeteringMode";
+    field public static final java.lang.String TAG_MODEL = "Model";
+    field public static final java.lang.String TAG_NEW_SUBFILE_TYPE = "NewSubfileType";
+    field public static final java.lang.String TAG_OECF = "OECF";
+    field public static final java.lang.String TAG_ORF_ASPECT_FRAME = "AspectFrame";
+    field public static final java.lang.String TAG_ORF_PREVIEW_IMAGE_LENGTH = "PreviewImageLength";
+    field public static final java.lang.String TAG_ORF_PREVIEW_IMAGE_START = "PreviewImageStart";
+    field public static final java.lang.String TAG_ORF_THUMBNAIL_IMAGE = "ThumbnailImage";
+    field public static final java.lang.String TAG_ORIENTATION = "Orientation";
+    field public static final java.lang.String TAG_PHOTOMETRIC_INTERPRETATION = "PhotometricInterpretation";
+    field public static final java.lang.String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
+    field public static final java.lang.String TAG_PIXEL_Y_DIMENSION = "PixelYDimension";
+    field public static final java.lang.String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration";
+    field public static final java.lang.String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities";
+    field public static final java.lang.String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite";
+    field public static final java.lang.String TAG_RELATED_SOUND_FILE = "RelatedSoundFile";
+    field public static final java.lang.String TAG_RESOLUTION_UNIT = "ResolutionUnit";
+    field public static final java.lang.String TAG_ROWS_PER_STRIP = "RowsPerStrip";
+    field public static final java.lang.String TAG_RW2_ISO = "ISO";
+    field public static final java.lang.String TAG_RW2_JPG_FROM_RAW = "JpgFromRaw";
+    field public static final java.lang.String TAG_RW2_SENSOR_BOTTOM_BORDER = "SensorBottomBorder";
+    field public static final java.lang.String TAG_RW2_SENSOR_LEFT_BORDER = "SensorLeftBorder";
+    field public static final java.lang.String TAG_RW2_SENSOR_RIGHT_BORDER = "SensorRightBorder";
+    field public static final java.lang.String TAG_RW2_SENSOR_TOP_BORDER = "SensorTopBorder";
+    field public static final java.lang.String TAG_SAMPLES_PER_PIXEL = "SamplesPerPixel";
+    field public static final java.lang.String TAG_SATURATION = "Saturation";
+    field public static final java.lang.String TAG_SCENE_CAPTURE_TYPE = "SceneCaptureType";
+    field public static final java.lang.String TAG_SCENE_TYPE = "SceneType";
+    field public static final java.lang.String TAG_SENSING_METHOD = "SensingMethod";
+    field public static final java.lang.String TAG_SHARPNESS = "Sharpness";
+    field public static final java.lang.String TAG_SHUTTER_SPEED_VALUE = "ShutterSpeedValue";
+    field public static final java.lang.String TAG_SOFTWARE = "Software";
+    field public static final java.lang.String TAG_SPATIAL_FREQUENCY_RESPONSE = "SpatialFrequencyResponse";
+    field public static final java.lang.String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity";
+    field public static final java.lang.String TAG_STRIP_BYTE_COUNTS = "StripByteCounts";
+    field public static final java.lang.String TAG_STRIP_OFFSETS = "StripOffsets";
+    field public static final java.lang.String TAG_SUBFILE_TYPE = "SubfileType";
+    field public static final java.lang.String TAG_SUBJECT_AREA = "SubjectArea";
+    field public static final java.lang.String TAG_SUBJECT_DISTANCE = "SubjectDistance";
+    field public static final java.lang.String TAG_SUBJECT_DISTANCE_RANGE = "SubjectDistanceRange";
+    field public static final java.lang.String TAG_SUBJECT_LOCATION = "SubjectLocation";
+    field public static final java.lang.String TAG_SUBSEC_TIME = "SubSecTime";
+    field public static final java.lang.String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
+    field public static final java.lang.String TAG_SUBSEC_TIME_ORIGINAL = "SubSecTimeOriginal";
+    field public static final java.lang.String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength";
+    field public static final java.lang.String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
+    field public static final java.lang.String TAG_TRANSFER_FUNCTION = "TransferFunction";
+    field public static final java.lang.String TAG_USER_COMMENT = "UserComment";
+    field public static final java.lang.String TAG_WHITE_BALANCE = "WhiteBalance";
+    field public static final java.lang.String TAG_WHITE_POINT = "WhitePoint";
+    field public static final java.lang.String TAG_X_RESOLUTION = "XResolution";
+    field public static final java.lang.String TAG_Y_CB_CR_COEFFICIENTS = "YCbCrCoefficients";
+    field public static final java.lang.String TAG_Y_CB_CR_POSITIONING = "YCbCrPositioning";
+    field public static final java.lang.String TAG_Y_CB_CR_SUB_SAMPLING = "YCbCrSubSampling";
+    field public static final java.lang.String TAG_Y_RESOLUTION = "YResolution";
+    field public static final int WHITEBALANCE_AUTO = 0; // 0x0
+    field public static final int WHITEBALANCE_MANUAL = 1; // 0x1
+  }
+
+}
+
 package android.support.percent {
 
   public class PercentFrameLayout extends android.widget.FrameLayout {
@@ -1761,10 +1928,9 @@
     method public abstract void onHeaderSelected(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
   }
 
-  public abstract class MediaControllerGlue extends android.support.v17.leanback.app.PlaybackControlGlue {
-    ctor public deprecated MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]);
-    ctor public deprecated MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]);
-    ctor public MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost, int[], int[]);
+  public abstract deprecated class MediaControllerGlue extends android.support.v17.leanback.app.PlaybackControlGlue {
+    ctor public MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]);
+    ctor public MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]);
     method public void attachToMediaController(android.support.v4.media.session.MediaControllerCompat);
     method public void detach();
     method public int getCurrentPosition();
@@ -1815,59 +1981,25 @@
     method public final void setLogoResourceId(int);
   }
 
-  public abstract class PlaybackControlGlue extends android.support.v17.leanback.app.PlaybackGlue implements android.support.v17.leanback.widget.OnActionClickedListener android.view.View.OnKeyListener {
+  public abstract deprecated class PlaybackControlGlue extends android.support.v17.leanback.media.PlaybackControlGlue {
     ctor public PlaybackControlGlue(android.content.Context, int[]);
     ctor public PlaybackControlGlue(android.content.Context, int[], int[]);
-    ctor public deprecated PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]);
-    ctor public deprecated PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]);
-    ctor public PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost, int[], int[]);
+    ctor public PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]);
+    ctor public PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]);
     method public android.support.v17.leanback.widget.PlaybackControlsRowPresenter createControlsRowAndPresenter();
     method protected android.support.v17.leanback.widget.SparseArrayObjectAdapter createPrimaryActionsAdapter(android.support.v17.leanback.widget.PresenterSelector);
-    method public void enableProgressUpdating(boolean);
-    method public android.support.v17.leanback.widget.PlaybackControlsRow getControlsRow();
-    method public abstract int getCurrentPosition();
-    method public abstract int getCurrentSpeedId();
-    method public int[] getFastForwardSpeeds();
-    method public deprecated android.support.v17.leanback.app.PlaybackOverlayFragment getFragment();
-    method public abstract android.graphics.drawable.Drawable getMediaArt();
-    method public abstract int getMediaDuration();
-    method public abstract java.lang.CharSequence getMediaSubtitle();
-    method public abstract java.lang.CharSequence getMediaTitle();
+    method public android.support.v17.leanback.app.PlaybackOverlayFragment getFragment();
     method public deprecated android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
-    method public int[] getRewindSpeeds();
-    method public abstract long getSupportedActions();
-    method public int getUpdatePeriod();
-    method public abstract boolean hasValidMedia();
-    method public boolean isFadingEnabled();
-    method public abstract boolean isMediaPlaying();
-    method public void onActionClicked(android.support.v17.leanback.widget.Action);
-    method public boolean onKey(android.view.View, int, android.view.KeyEvent);
-    method protected void onMetadataChanged();
-    method protected deprecated void onRowChanged(android.support.v17.leanback.widget.PlaybackControlsRow);
-    method protected void onStateChanged();
+    method public final void next();
+    method protected void onRowChanged(android.support.v17.leanback.widget.PlaybackControlsRow);
+    method public final void pause();
     method protected deprecated void pausePlayback();
-    method public void setControlsRow(android.support.v17.leanback.widget.PlaybackControlsRow);
-    method public void setFadingEnabled(boolean);
+    method public final void play(int);
+    method public final void previous();
     method public deprecated void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
     method protected deprecated void skipToNext();
     method protected deprecated void skipToPrevious();
     method protected deprecated void startPlayback(int);
-    method public void updateProgress();
-    field public static final int ACTION_CUSTOM_LEFT_FIRST = 1; // 0x1
-    field public static final int ACTION_CUSTOM_RIGHT_FIRST = 4096; // 0x1000
-    field public static final int ACTION_FAST_FORWARD = 128; // 0x80
-    field public static final int ACTION_PLAY_PAUSE = 64; // 0x40
-    field public static final int ACTION_REWIND = 32; // 0x20
-    field public static final int ACTION_SKIP_TO_NEXT = 256; // 0x100
-    field public static final int ACTION_SKIP_TO_PREVIOUS = 16; // 0x10
-    field public static final int PLAYBACK_SPEED_FAST_L0 = 10; // 0xa
-    field public static final int PLAYBACK_SPEED_FAST_L1 = 11; // 0xb
-    field public static final int PLAYBACK_SPEED_FAST_L2 = 12; // 0xc
-    field public static final int PLAYBACK_SPEED_FAST_L3 = 13; // 0xd
-    field public static final int PLAYBACK_SPEED_FAST_L4 = 14; // 0xe
-    field public static final int PLAYBACK_SPEED_INVALID = -1; // 0xffffffff
-    field public static final int PLAYBACK_SPEED_NORMAL = 1; // 0x1
-    field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0
   }
 
   public static abstract deprecated interface PlaybackControlGlue.InputEventHandler {
@@ -1910,7 +2042,7 @@
     method public void setBackgroundType(int);
     method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackFragment.OnFadeCompleteListener);
     method public void setFadingEnabled(boolean);
-    method public void setHostLifecycleCallback(android.support.v17.leanback.app.PlaybackGlue.HostLifecycleCallback);
+    method public void setHostCallback(android.support.v17.leanback.media.PlaybackGlueHost.HostCallback);
     method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
     method public final void setOnKeyInterceptListener(android.view.View.OnKeyListener);
     method public void setOnPlaybackItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
@@ -1930,46 +2062,10 @@
     method public void onFadeOutComplete();
   }
 
-  public class PlaybackFragmentGlueHost extends android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost {
+  public class PlaybackFragmentGlueHost extends android.support.v17.leanback.media.PlaybackGlueHost {
     ctor public PlaybackFragmentGlueHost(android.support.v17.leanback.app.PlaybackFragment);
   }
 
-  public abstract class PlaybackGlue {
-    ctor public PlaybackGlue(android.content.Context);
-    method public android.content.Context getContext();
-    method public android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost getHost();
-    method public boolean isReadyForPlayback();
-    method public void next();
-    method public void onDetachedFromHost();
-    method public void pause();
-    method public void play();
-    method public void previous();
-    method public void setHost(android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost);
-    method public void setPlayerCallback(android.support.v17.leanback.app.PlaybackGlue.PlayerCallback);
-  }
-
-  public static abstract class PlaybackGlue.HostLifecycleCallback {
-    ctor public PlaybackGlue.HostLifecycleCallback();
-    method public abstract void onHostStart();
-    method public abstract void onHostStop();
-  }
-
-  public static class PlaybackGlue.PlaybackGlueHost {
-    ctor public PlaybackGlue.PlaybackGlueHost();
-    method public void notifyPlaybackRowChanged();
-    method public void setFadingEnabled(boolean);
-    method public void setHostLifeCycleCallback(android.support.v17.leanback.app.PlaybackGlue.HostLifecycleCallback);
-    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
-    method public void setOnKeyInterceptListener(android.view.View.OnKeyListener);
-    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
-    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
-  }
-
-  public static abstract class PlaybackGlue.PlayerCallback {
-    ctor public PlaybackGlue.PlayerCallback();
-    method public abstract void onReadyForPlayback();
-  }
-
   public deprecated class PlaybackOverlayFragment extends android.support.v17.leanback.app.DetailsFragment {
     ctor public PlaybackOverlayFragment();
     method public void fadeOut();
@@ -2040,7 +2136,7 @@
     method public void setBackgroundType(int);
     method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackSupportFragment.OnFadeCompleteListener);
     method public void setFadingEnabled(boolean);
-    method public void setHostLifecycleCallback(android.support.v17.leanback.app.PlaybackGlue.HostLifecycleCallback);
+    method public void setHostCallback(android.support.v17.leanback.media.PlaybackGlueHost.HostCallback);
     method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
     method public final void setOnKeyInterceptListener(android.view.View.OnKeyListener);
     method public void setOnPlaybackItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
@@ -2060,7 +2156,7 @@
     method public void onFadeOutComplete();
   }
 
-  public class PlaybackSupportFragmentGlueHost extends android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost {
+  public class PlaybackSupportFragmentGlueHost extends android.support.v17.leanback.media.PlaybackGlueHost {
     ctor public PlaybackSupportFragmentGlueHost(android.support.v17.leanback.app.PlaybackSupportFragment);
   }
 
@@ -2186,10 +2282,6 @@
     method public abstract boolean onQueryTextSubmit(java.lang.String);
   }
 
-  public abstract interface SurfaceHolderGlueHost {
-    method public abstract void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
-  }
-
   public class VerticalGridFragment extends android.support.v17.leanback.app.BrandedFragment {
     ctor public VerticalGridFragment();
     method protected java.lang.Object createEntranceTransition();
@@ -2261,6 +2353,123 @@
 
 }
 
+package android.support.v17.leanback.media {
+
+  public abstract class MediaControllerGlue extends android.support.v17.leanback.media.PlaybackControlGlue {
+    ctor public MediaControllerGlue(android.content.Context, int[], int[]);
+    method public void attachToMediaController(android.support.v4.media.session.MediaControllerCompat);
+    method public void detach();
+    method public int getCurrentPosition();
+    method public int getCurrentSpeedId();
+    method public android.graphics.drawable.Drawable getMediaArt();
+    method public final android.support.v4.media.session.MediaControllerCompat getMediaController();
+    method public int getMediaDuration();
+    method public java.lang.CharSequence getMediaSubtitle();
+    method public java.lang.CharSequence getMediaTitle();
+    method public long getSupportedActions();
+    method public boolean hasValidMedia();
+    method public boolean isMediaPlaying();
+  }
+
+  public abstract class PlaybackControlGlue extends android.support.v17.leanback.media.PlaybackGlue implements android.support.v17.leanback.widget.OnActionClickedListener android.view.View.OnKeyListener {
+    ctor public PlaybackControlGlue(android.content.Context, int[]);
+    ctor public PlaybackControlGlue(android.content.Context, int[], int[]);
+    method public void enableProgressUpdating(boolean);
+    method public android.support.v17.leanback.widget.PlaybackControlsRow getControlsRow();
+    method public android.support.v17.leanback.widget.PlaybackControlsRowPresenter getControlsRowPresenter();
+    method public abstract int getCurrentPosition();
+    method public abstract int getCurrentSpeedId();
+    method public int[] getFastForwardSpeeds();
+    method public abstract android.graphics.drawable.Drawable getMediaArt();
+    method public abstract int getMediaDuration();
+    method public abstract java.lang.CharSequence getMediaSubtitle();
+    method public abstract java.lang.CharSequence getMediaTitle();
+    method public int[] getRewindSpeeds();
+    method public abstract long getSupportedActions();
+    method public int getUpdatePeriod();
+    method public abstract boolean hasValidMedia();
+    method public boolean isFadingEnabled();
+    method public abstract boolean isMediaPlaying();
+    method public void onActionClicked(android.support.v17.leanback.widget.Action);
+    method protected void onCreateControlsRowAndPresenter();
+    method protected void onCreatePrimaryActions(android.support.v17.leanback.widget.SparseArrayObjectAdapter);
+    method protected void onCreateSecondaryActions(android.support.v17.leanback.widget.ArrayObjectAdapter);
+    method public boolean onKey(android.view.View, int, android.view.KeyEvent);
+    method protected void onMetadataChanged();
+    method protected void onStateChanged();
+    method public void play(int);
+    method public final void play();
+    method public void setControlsRow(android.support.v17.leanback.widget.PlaybackControlsRow);
+    method public void setControlsRowPresenter(android.support.v17.leanback.widget.PlaybackControlsRowPresenter);
+    method public void setFadingEnabled(boolean);
+    method public void updateProgress();
+    field public static final int ACTION_CUSTOM_LEFT_FIRST = 1; // 0x1
+    field public static final int ACTION_CUSTOM_RIGHT_FIRST = 4096; // 0x1000
+    field public static final int ACTION_FAST_FORWARD = 128; // 0x80
+    field public static final int ACTION_PLAY_PAUSE = 64; // 0x40
+    field public static final int ACTION_REWIND = 32; // 0x20
+    field public static final int ACTION_SKIP_TO_NEXT = 256; // 0x100
+    field public static final int ACTION_SKIP_TO_PREVIOUS = 16; // 0x10
+    field public static final int PLAYBACK_SPEED_FAST_L0 = 10; // 0xa
+    field public static final int PLAYBACK_SPEED_FAST_L1 = 11; // 0xb
+    field public static final int PLAYBACK_SPEED_FAST_L2 = 12; // 0xc
+    field public static final int PLAYBACK_SPEED_FAST_L3 = 13; // 0xd
+    field public static final int PLAYBACK_SPEED_FAST_L4 = 14; // 0xe
+    field public static final int PLAYBACK_SPEED_INVALID = -1; // 0xffffffff
+    field public static final int PLAYBACK_SPEED_NORMAL = 1; // 0x1
+    field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0
+  }
+
+  public abstract class PlaybackGlue {
+    ctor public PlaybackGlue(android.content.Context);
+    method public android.content.Context getContext();
+    method public android.support.v17.leanback.media.PlaybackGlueHost getHost();
+    method public boolean isReadyForPlayback();
+    method public void next();
+    method protected void onAttachedToHost(android.support.v17.leanback.media.PlaybackGlueHost);
+    method protected void onDetachedFromHost();
+    method protected void onHostPause();
+    method protected void onHostResume();
+    method protected void onHostStart();
+    method protected void onHostStop();
+    method public void pause();
+    method public void play();
+    method public void previous();
+    method public final void setHost(android.support.v17.leanback.media.PlaybackGlueHost);
+    method public void setPlayerCallback(android.support.v17.leanback.media.PlaybackGlue.PlayerCallback);
+  }
+
+  public static abstract class PlaybackGlue.PlayerCallback {
+    ctor public PlaybackGlue.PlayerCallback();
+    method public abstract void onReadyForPlayback();
+  }
+
+  public abstract class PlaybackGlueHost {
+    ctor public PlaybackGlueHost();
+    method public void fadeOut();
+    method public void notifyPlaybackRowChanged();
+    method public void setFadingEnabled(boolean);
+    method public void setHostCallback(android.support.v17.leanback.media.PlaybackGlueHost.HostCallback);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public void setOnKeyInterceptListener(android.view.View.OnKeyListener);
+    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+  }
+
+  public static abstract class PlaybackGlueHost.HostCallback {
+    ctor public PlaybackGlueHost.HostCallback();
+    method public void onHostPause();
+    method public void onHostResume();
+    method public void onHostStart();
+    method public void onHostStop();
+  }
+
+  public abstract interface SurfaceHolderGlueHost {
+    method public abstract void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+}
+
 package android.support.v17.leanback.system {
 
   public class Settings {
@@ -9574,6 +9783,7 @@
     method public int findLastVisibleItemPosition();
     method public android.support.v7.widget.RecyclerView.LayoutParams generateDefaultLayoutParams();
     method protected int getExtraLayoutSpace(android.support.v7.widget.RecyclerView.State);
+    method public int getInitialItemPrefetchCount();
     method public int getOrientation();
     method public boolean getRecycleChildrenOnDetach();
     method public boolean getReverseLayout();
@@ -9581,6 +9791,7 @@
     method protected boolean isLayoutRTL();
     method public boolean isSmoothScrollbarEnabled();
     method public void scrollToPositionWithOffset(int, int);
+    method public void setInitialPrefetchItemCount(int);
     method public void setOrientation(int);
     method public void setRecycleChildrenOnDetach(boolean);
     method public void setReverseLayout(boolean);
@@ -9971,6 +10182,8 @@
     method public boolean canScrollVertically();
     method public boolean checkLayoutParams(android.support.v7.widget.RecyclerView.LayoutParams);
     method public static int chooseSize(int, int, int);
+    method public void collectAdjacentPrefetchPositions(int, int, android.support.v7.widget.RecyclerView.State, android.support.v7.widget.RecyclerView.LayoutManager.LayoutPrefetchRegistry);
+    method public void collectInitialPrefetchPositions(int, android.support.v7.widget.RecyclerView.LayoutManager.LayoutPrefetchRegistry);
     method public int computeHorizontalScrollExtent(android.support.v7.widget.RecyclerView.State);
     method public int computeHorizontalScrollOffset(android.support.v7.widget.RecyclerView.State);
     method public int computeHorizontalScrollRange(android.support.v7.widget.RecyclerView.State);
@@ -10096,6 +10309,10 @@
     method public boolean supportsPredictiveItemAnimations();
   }
 
+  public static abstract interface RecyclerView.LayoutManager.LayoutPrefetchRegistry {
+    method public abstract void addPosition(int, int);
+  }
+
   public static class RecyclerView.LayoutManager.Properties {
     ctor public RecyclerView.LayoutManager.Properties();
     field public int orientation;
diff --git a/api/current.txt b/api/current.txt
index b574160..defcf74 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1918,10 +1918,9 @@
     method public abstract void onHeaderSelected(android.support.v17.leanback.widget.RowHeaderPresenter.ViewHolder, android.support.v17.leanback.widget.Row);
   }
 
-  public abstract class MediaControllerGlue extends android.support.v17.leanback.app.PlaybackControlGlue {
-    ctor public deprecated MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]);
-    ctor public deprecated MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]);
-    ctor public MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost, int[], int[]);
+  public abstract deprecated class MediaControllerGlue extends android.support.v17.leanback.app.PlaybackControlGlue {
+    ctor public MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]);
+    ctor public MediaControllerGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]);
     method public void attachToMediaController(android.support.v4.media.session.MediaControllerCompat);
     method public void detach();
     method public int getCurrentPosition();
@@ -1980,59 +1979,25 @@
     method public final void setLogoResourceId(int);
   }
 
-  public abstract class PlaybackControlGlue extends android.support.v17.leanback.app.PlaybackGlue implements android.support.v17.leanback.widget.OnActionClickedListener android.view.View.OnKeyListener {
+  public abstract deprecated class PlaybackControlGlue extends android.support.v17.leanback.media.PlaybackControlGlue {
     ctor public PlaybackControlGlue(android.content.Context, int[]);
     ctor public PlaybackControlGlue(android.content.Context, int[], int[]);
-    ctor public deprecated PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]);
-    ctor public deprecated PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]);
-    ctor public PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost, int[], int[]);
+    ctor public PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[]);
+    ctor public PlaybackControlGlue(android.content.Context, android.support.v17.leanback.app.PlaybackOverlayFragment, int[], int[]);
     method public android.support.v17.leanback.widget.PlaybackControlsRowPresenter createControlsRowAndPresenter();
     method protected android.support.v17.leanback.widget.SparseArrayObjectAdapter createPrimaryActionsAdapter(android.support.v17.leanback.widget.PresenterSelector);
-    method public void enableProgressUpdating(boolean);
-    method public android.support.v17.leanback.widget.PlaybackControlsRow getControlsRow();
-    method public abstract int getCurrentPosition();
-    method public abstract int getCurrentSpeedId();
-    method public int[] getFastForwardSpeeds();
-    method public deprecated android.support.v17.leanback.app.PlaybackOverlayFragment getFragment();
-    method public abstract android.graphics.drawable.Drawable getMediaArt();
-    method public abstract int getMediaDuration();
-    method public abstract java.lang.CharSequence getMediaSubtitle();
-    method public abstract java.lang.CharSequence getMediaTitle();
+    method public android.support.v17.leanback.app.PlaybackOverlayFragment getFragment();
     method public deprecated android.support.v17.leanback.widget.OnItemViewClickedListener getOnItemViewClickedListener();
-    method public int[] getRewindSpeeds();
-    method public abstract long getSupportedActions();
-    method public int getUpdatePeriod();
-    method public abstract boolean hasValidMedia();
-    method public boolean isFadingEnabled();
-    method public abstract boolean isMediaPlaying();
-    method public void onActionClicked(android.support.v17.leanback.widget.Action);
-    method public boolean onKey(android.view.View, int, android.view.KeyEvent);
-    method protected void onMetadataChanged();
-    method protected deprecated void onRowChanged(android.support.v17.leanback.widget.PlaybackControlsRow);
-    method protected void onStateChanged();
+    method public final void next();
+    method protected void onRowChanged(android.support.v17.leanback.widget.PlaybackControlsRow);
+    method public final void pause();
     method protected deprecated void pausePlayback();
-    method public void setControlsRow(android.support.v17.leanback.widget.PlaybackControlsRow);
-    method public void setFadingEnabled(boolean);
+    method public final void play(int);
+    method public final void previous();
     method public deprecated void setOnItemViewClickedListener(android.support.v17.leanback.widget.OnItemViewClickedListener);
     method protected deprecated void skipToNext();
     method protected deprecated void skipToPrevious();
     method protected deprecated void startPlayback(int);
-    method public void updateProgress();
-    field public static final int ACTION_CUSTOM_LEFT_FIRST = 1; // 0x1
-    field public static final int ACTION_CUSTOM_RIGHT_FIRST = 4096; // 0x1000
-    field public static final int ACTION_FAST_FORWARD = 128; // 0x80
-    field public static final int ACTION_PLAY_PAUSE = 64; // 0x40
-    field public static final int ACTION_REWIND = 32; // 0x20
-    field public static final int ACTION_SKIP_TO_NEXT = 256; // 0x100
-    field public static final int ACTION_SKIP_TO_PREVIOUS = 16; // 0x10
-    field public static final int PLAYBACK_SPEED_FAST_L0 = 10; // 0xa
-    field public static final int PLAYBACK_SPEED_FAST_L1 = 11; // 0xb
-    field public static final int PLAYBACK_SPEED_FAST_L2 = 12; // 0xc
-    field public static final int PLAYBACK_SPEED_FAST_L3 = 13; // 0xd
-    field public static final int PLAYBACK_SPEED_FAST_L4 = 14; // 0xe
-    field public static final int PLAYBACK_SPEED_INVALID = -1; // 0xffffffff
-    field public static final int PLAYBACK_SPEED_NORMAL = 1; // 0x1
-    field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0
   }
 
   public static abstract deprecated interface PlaybackControlGlue.InputEventHandler {
@@ -2060,7 +2025,7 @@
     method public void setBackgroundType(int);
     method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackFragment.OnFadeCompleteListener);
     method public void setFadingEnabled(boolean);
-    method public void setHostLifecycleCallback(android.support.v17.leanback.app.PlaybackGlue.HostLifecycleCallback);
+    method public void setHostCallback(android.support.v17.leanback.media.PlaybackGlueHost.HostCallback);
     method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
     method public final void setOnKeyInterceptListener(android.view.View.OnKeyListener);
     method public void setOnPlaybackItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
@@ -2080,46 +2045,10 @@
     method public void onFadeOutComplete();
   }
 
-  public class PlaybackFragmentGlueHost extends android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost {
+  public class PlaybackFragmentGlueHost extends android.support.v17.leanback.media.PlaybackGlueHost {
     ctor public PlaybackFragmentGlueHost(android.support.v17.leanback.app.PlaybackFragment);
   }
 
-  public abstract class PlaybackGlue {
-    ctor public PlaybackGlue(android.content.Context);
-    method public android.content.Context getContext();
-    method public android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost getHost();
-    method public boolean isReadyForPlayback();
-    method public void next();
-    method public void onDetachedFromHost();
-    method public void pause();
-    method public void play();
-    method public void previous();
-    method public void setHost(android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost);
-    method public void setPlayerCallback(android.support.v17.leanback.app.PlaybackGlue.PlayerCallback);
-  }
-
-  public static abstract class PlaybackGlue.HostLifecycleCallback {
-    ctor public PlaybackGlue.HostLifecycleCallback();
-    method public abstract void onHostStart();
-    method public abstract void onHostStop();
-  }
-
-  public static class PlaybackGlue.PlaybackGlueHost {
-    ctor public PlaybackGlue.PlaybackGlueHost();
-    method public void notifyPlaybackRowChanged();
-    method public void setFadingEnabled(boolean);
-    method public void setHostLifeCycleCallback(android.support.v17.leanback.app.PlaybackGlue.HostLifecycleCallback);
-    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
-    method public void setOnKeyInterceptListener(android.view.View.OnKeyListener);
-    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
-    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
-  }
-
-  public static abstract class PlaybackGlue.PlayerCallback {
-    ctor public PlaybackGlue.PlayerCallback();
-    method public abstract void onReadyForPlayback();
-  }
-
   public deprecated class PlaybackOverlayFragment extends android.support.v17.leanback.app.DetailsFragment {
     ctor public PlaybackOverlayFragment();
     method public void fadeOut();
@@ -2190,7 +2119,7 @@
     method public void setBackgroundType(int);
     method public void setFadeCompleteListener(android.support.v17.leanback.app.PlaybackSupportFragment.OnFadeCompleteListener);
     method public void setFadingEnabled(boolean);
-    method public void setHostLifecycleCallback(android.support.v17.leanback.app.PlaybackGlue.HostLifecycleCallback);
+    method public void setHostCallback(android.support.v17.leanback.media.PlaybackGlueHost.HostCallback);
     method public void setOnItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
     method public final void setOnKeyInterceptListener(android.view.View.OnKeyListener);
     method public void setOnPlaybackItemViewClickedListener(android.support.v17.leanback.widget.BaseOnItemViewClickedListener);
@@ -2210,7 +2139,7 @@
     method public void onFadeOutComplete();
   }
 
-  public class PlaybackSupportFragmentGlueHost extends android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost {
+  public class PlaybackSupportFragmentGlueHost extends android.support.v17.leanback.media.PlaybackGlueHost {
     ctor public PlaybackSupportFragmentGlueHost(android.support.v17.leanback.app.PlaybackSupportFragment);
   }
 
@@ -2336,10 +2265,6 @@
     method public abstract boolean onQueryTextSubmit(java.lang.String);
   }
 
-  public abstract interface SurfaceHolderGlueHost {
-    method public abstract void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
-  }
-
   public class VerticalGridFragment extends android.support.v17.leanback.app.BrandedFragment {
     ctor public VerticalGridFragment();
     method protected java.lang.Object createEntranceTransition();
@@ -2411,6 +2336,123 @@
 
 }
 
+package android.support.v17.leanback.media {
+
+  public abstract class MediaControllerGlue extends android.support.v17.leanback.media.PlaybackControlGlue {
+    ctor public MediaControllerGlue(android.content.Context, int[], int[]);
+    method public void attachToMediaController(android.support.v4.media.session.MediaControllerCompat);
+    method public void detach();
+    method public int getCurrentPosition();
+    method public int getCurrentSpeedId();
+    method public android.graphics.drawable.Drawable getMediaArt();
+    method public final android.support.v4.media.session.MediaControllerCompat getMediaController();
+    method public int getMediaDuration();
+    method public java.lang.CharSequence getMediaSubtitle();
+    method public java.lang.CharSequence getMediaTitle();
+    method public long getSupportedActions();
+    method public boolean hasValidMedia();
+    method public boolean isMediaPlaying();
+  }
+
+  public abstract class PlaybackControlGlue extends android.support.v17.leanback.media.PlaybackGlue implements android.support.v17.leanback.widget.OnActionClickedListener android.view.View.OnKeyListener {
+    ctor public PlaybackControlGlue(android.content.Context, int[]);
+    ctor public PlaybackControlGlue(android.content.Context, int[], int[]);
+    method public void enableProgressUpdating(boolean);
+    method public android.support.v17.leanback.widget.PlaybackControlsRow getControlsRow();
+    method public android.support.v17.leanback.widget.PlaybackControlsRowPresenter getControlsRowPresenter();
+    method public abstract int getCurrentPosition();
+    method public abstract int getCurrentSpeedId();
+    method public int[] getFastForwardSpeeds();
+    method public abstract android.graphics.drawable.Drawable getMediaArt();
+    method public abstract int getMediaDuration();
+    method public abstract java.lang.CharSequence getMediaSubtitle();
+    method public abstract java.lang.CharSequence getMediaTitle();
+    method public int[] getRewindSpeeds();
+    method public abstract long getSupportedActions();
+    method public int getUpdatePeriod();
+    method public abstract boolean hasValidMedia();
+    method public boolean isFadingEnabled();
+    method public abstract boolean isMediaPlaying();
+    method public void onActionClicked(android.support.v17.leanback.widget.Action);
+    method protected void onCreateControlsRowAndPresenter();
+    method protected void onCreatePrimaryActions(android.support.v17.leanback.widget.SparseArrayObjectAdapter);
+    method protected void onCreateSecondaryActions(android.support.v17.leanback.widget.ArrayObjectAdapter);
+    method public boolean onKey(android.view.View, int, android.view.KeyEvent);
+    method protected void onMetadataChanged();
+    method protected void onStateChanged();
+    method public void play(int);
+    method public final void play();
+    method public void setControlsRow(android.support.v17.leanback.widget.PlaybackControlsRow);
+    method public void setControlsRowPresenter(android.support.v17.leanback.widget.PlaybackControlsRowPresenter);
+    method public void setFadingEnabled(boolean);
+    method public void updateProgress();
+    field public static final int ACTION_CUSTOM_LEFT_FIRST = 1; // 0x1
+    field public static final int ACTION_CUSTOM_RIGHT_FIRST = 4096; // 0x1000
+    field public static final int ACTION_FAST_FORWARD = 128; // 0x80
+    field public static final int ACTION_PLAY_PAUSE = 64; // 0x40
+    field public static final int ACTION_REWIND = 32; // 0x20
+    field public static final int ACTION_SKIP_TO_NEXT = 256; // 0x100
+    field public static final int ACTION_SKIP_TO_PREVIOUS = 16; // 0x10
+    field public static final int PLAYBACK_SPEED_FAST_L0 = 10; // 0xa
+    field public static final int PLAYBACK_SPEED_FAST_L1 = 11; // 0xb
+    field public static final int PLAYBACK_SPEED_FAST_L2 = 12; // 0xc
+    field public static final int PLAYBACK_SPEED_FAST_L3 = 13; // 0xd
+    field public static final int PLAYBACK_SPEED_FAST_L4 = 14; // 0xe
+    field public static final int PLAYBACK_SPEED_INVALID = -1; // 0xffffffff
+    field public static final int PLAYBACK_SPEED_NORMAL = 1; // 0x1
+    field public static final int PLAYBACK_SPEED_PAUSED = 0; // 0x0
+  }
+
+  public abstract class PlaybackGlue {
+    ctor public PlaybackGlue(android.content.Context);
+    method public android.content.Context getContext();
+    method public android.support.v17.leanback.media.PlaybackGlueHost getHost();
+    method public boolean isReadyForPlayback();
+    method public void next();
+    method protected void onAttachedToHost(android.support.v17.leanback.media.PlaybackGlueHost);
+    method protected void onDetachedFromHost();
+    method protected void onHostPause();
+    method protected void onHostResume();
+    method protected void onHostStart();
+    method protected void onHostStop();
+    method public void pause();
+    method public void play();
+    method public void previous();
+    method public final void setHost(android.support.v17.leanback.media.PlaybackGlueHost);
+    method public void setPlayerCallback(android.support.v17.leanback.media.PlaybackGlue.PlayerCallback);
+  }
+
+  public static abstract class PlaybackGlue.PlayerCallback {
+    ctor public PlaybackGlue.PlayerCallback();
+    method public abstract void onReadyForPlayback();
+  }
+
+  public abstract class PlaybackGlueHost {
+    ctor public PlaybackGlueHost();
+    method public void fadeOut();
+    method public void notifyPlaybackRowChanged();
+    method public void setFadingEnabled(boolean);
+    method public void setHostCallback(android.support.v17.leanback.media.PlaybackGlueHost.HostCallback);
+    method public void setOnActionClickedListener(android.support.v17.leanback.widget.OnActionClickedListener);
+    method public void setOnKeyInterceptListener(android.view.View.OnKeyListener);
+    method public void setPlaybackRow(android.support.v17.leanback.widget.Row);
+    method public void setPlaybackRowPresenter(android.support.v17.leanback.widget.PlaybackRowPresenter);
+  }
+
+  public static abstract class PlaybackGlueHost.HostCallback {
+    ctor public PlaybackGlueHost.HostCallback();
+    method public void onHostPause();
+    method public void onHostResume();
+    method public void onHostStart();
+    method public void onHostStop();
+  }
+
+  public abstract interface SurfaceHolderGlueHost {
+    method public abstract void setSurfaceHolderCallback(android.view.SurfaceHolder.Callback);
+  }
+
+}
+
 package android.support.v17.leanback.system {
 
   public class Settings {
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportFragment.java
index 1a8ab1f..fa1425b 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportFragment.java
@@ -17,6 +17,7 @@
 package com.example.android.leanback;
 
 import android.support.v4.app.Fragment;
+import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.Handler;
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsFragment.java
index 7803c29..75fa941 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsFragment.java
@@ -20,8 +20,8 @@
 import android.os.Handler;
 import android.support.v17.leanback.app.DetailsBackgroundParallaxHelper;
 import android.support.v17.leanback.app.DetailsFragmentVideoHelper;
-import android.support.v17.leanback.app.MediaPlayerGlue;
 import android.support.v17.leanback.app.VideoFragment;
+import android.support.v17.leanback.media.MediaPlayerGlue;
 import android.support.v17.leanback.widget.Action;
 import android.support.v17.leanback.widget.ArrayObjectAdapter;
 import android.support.v17.leanback.widget.ClassPresenterSelector;
@@ -97,7 +97,7 @@
                 getActivity(), getParallaxManager())
                 .setCoverImageMinVerticalOffset(mBitmapMinVerticalOffset)
                 .build();
-        mMediaPlayerGlue = new MediaPlayerGlue(getActivity(), null);
+        mMediaPlayerGlue = new MediaPlayerGlue(getActivity());
         mMediaPlayerGlue.setHost(createPlaybackGlueHost());
         mVideoHelper = new DetailsFragmentVideoHelper(mMediaPlayerGlue, getParallaxManager());
         mVideoHelper.setBackgroundDrawable(mParallaxHelper.getCoverImageDrawable());
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsSupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsSupportFragment.java
index 8f0172f..11d8b7e 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsSupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/NewDetailsSupportFragment.java
@@ -23,8 +23,8 @@
 import android.os.Handler;
 import android.support.v17.leanback.app.DetailsBackgroundParallaxHelper;
 import android.support.v17.leanback.app.DetailsFragmentVideoHelper;
-import android.support.v17.leanback.app.MediaPlayerGlue;
 import android.support.v17.leanback.app.VideoSupportFragment;
+import android.support.v17.leanback.media.MediaPlayerGlue;
 import android.support.v17.leanback.widget.Action;
 import android.support.v17.leanback.widget.ArrayObjectAdapter;
 import android.support.v17.leanback.widget.ClassPresenterSelector;
@@ -100,7 +100,7 @@
                 getActivity(), getParallaxManager())
                 .setCoverImageMinVerticalOffset(mBitmapMinVerticalOffset)
                 .build();
-        mMediaPlayerGlue = new MediaPlayerGlue(getActivity(), null);
+        mMediaPlayerGlue = new MediaPlayerGlue(getActivity());
         mMediaPlayerGlue.setHost(createPlaybackGlueHost());
         mVideoHelper = new DetailsFragmentVideoHelper(mMediaPlayerGlue, getParallaxManager());
         mVideoHelper.setBackgroundDrawable(mParallaxHelper.getCoverImageDrawable());
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackControlHelper.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackControlHelper.java
index 308c7f4..09892da 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackControlHelper.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackControlHelper.java
@@ -19,15 +19,11 @@
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
-import android.support.v17.leanback.app.MediaPlayerGlue;
-import android.support.v17.leanback.app.PlaybackControlGlue;
-import android.support.v17.leanback.app.PlaybackGlue;
+import android.support.v17.leanback.media.MediaPlayerGlue;
+import android.support.v17.leanback.media.PlaybackControlGlue;
 import android.support.v17.leanback.widget.Action;
 import android.support.v17.leanback.widget.ArrayObjectAdapter;
-import android.support.v17.leanback.widget.ControlButtonPresenterSelector;
 import android.support.v17.leanback.widget.PlaybackControlsRow;
-import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
-import android.support.v17.leanback.widget.PresenterSelector;
 import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
 import android.view.KeyEvent;
 import android.view.View;
@@ -65,8 +61,8 @@
         }
     };
 
-    public PlaybackControlHelper(Context context, PlaybackGlue.PlaybackGlueHost host) {
-        super(context, host, sFastForwardSpeeds, sFastForwardSpeeds);
+    PlaybackControlHelper(Context context) {
+        super(context, sFastForwardSpeeds, sFastForwardSpeeds);
         mThumbsUpAction = new PlaybackControlsRow.ThumbsUpAction(context);
         mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsUpAction.OUTLINE);
         mThumbsDownAction = new PlaybackControlsRow.ThumbsDownAction(context);
@@ -76,34 +72,26 @@
     }
 
     @Override
-    public PlaybackControlsRowPresenter createControlsRowAndPresenter() {
-        PlaybackControlsRowPresenter presenter = super.createControlsRowAndPresenter();
-
-        ArrayObjectAdapter adapter = new ArrayObjectAdapter(new ControlButtonPresenterSelector());
-        getControlsRow().setSecondaryActionsAdapter(adapter);
+    protected void onCreateSecondaryActions(ArrayObjectAdapter secondaryActionsAdapter) {
         if (!THUMBS_PRIMARY) {
-            adapter.add(mThumbsDownAction);
+            secondaryActionsAdapter.add(mThumbsDownAction);
         }
         if (android.os.Build.VERSION.SDK_INT > 23) {
-            adapter.add(mPipAction);
+            secondaryActionsAdapter.add(mPipAction);
         }
-        adapter.add(mRepeatAction);
+        secondaryActionsAdapter.add(mRepeatAction);
         if (!THUMBS_PRIMARY) {
-            adapter.add(mThumbsUpAction);
+            secondaryActionsAdapter.add(mThumbsUpAction);
         }
-
-        return presenter;
     }
 
     @Override
-    protected SparseArrayObjectAdapter createPrimaryActionsAdapter(
-            PresenterSelector presenterSelector) {
-        SparseArrayObjectAdapter adapter = new SparseArrayObjectAdapter(presenterSelector);
+    protected void onCreatePrimaryActions(SparseArrayObjectAdapter adapter) {
+        super.onCreatePrimaryActions(adapter);
         if (THUMBS_PRIMARY) {
             adapter.set(PlaybackControlGlue.ACTION_CUSTOM_LEFT_FIRST, mThumbsUpAction);
             adapter.set(PlaybackControlGlue.ACTION_CUSTOM_RIGHT_FIRST, mThumbsDownAction);
         }
-        return adapter;
     }
 
     @Override
@@ -234,9 +222,9 @@
             @Override
             public void run() {
                 if (mRepeatAction.getIndex() == PlaybackControlsRow.RepeatAction.NONE) {
-                    pausePlayback();
+                    pause();
                 } else {
-                    startPlayback(PlaybackControlGlue.PLAYBACK_SPEED_NORMAL);
+                    play(PlaybackControlGlue.PLAYBACK_SPEED_NORMAL);
                 }
                 mStartPosition = 0;
                 onStateChanged();
@@ -245,7 +233,7 @@
     }
 
     @Override
-    protected void startPlayback(int speed) {
+    public void play(int speed) {
         if (speed == mSpeed) {
             return;
         }
@@ -256,7 +244,7 @@
     }
 
     @Override
-    protected void pausePlayback() {
+    public void pause() {
         if (mSpeed == PlaybackControlGlue.PLAYBACK_SPEED_PAUSED) {
             return;
         }
@@ -266,12 +254,12 @@
     }
 
     @Override
-    protected void skipToNext() {
+    public void next() {
         // Not supported
     }
 
     @Override
-    protected void skipToPrevious() {
+    public void previous() {
         // Not supported
     }
 
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlayFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlayFragment.java
index 06ec502..1701153 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlayFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlayFragment.java
@@ -15,7 +15,6 @@
 
 import android.content.Context;
 import android.os.Bundle;
-import android.support.v17.leanback.app.BackgroundManager;
 import android.support.v17.leanback.app.PlaybackFragment;
 import android.support.v17.leanback.app.PlaybackFragmentGlueHost;
 import android.support.v17.leanback.widget.Action;
@@ -24,7 +23,6 @@
 import android.support.v17.leanback.widget.ListRow;
 import android.support.v17.leanback.widget.ListRowPresenter;
 import android.support.v17.leanback.widget.PlaybackControlsRow;
-import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.PresenterSelector;
 import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
@@ -53,7 +51,6 @@
     private static final int ROW_CONTROLS = 0;
 
     private PlaybackControlHelper mGlue;
-    private PlaybackControlsRowPresenter mPlaybackControlsRowPresenter;
     private ListRowPresenter mListRowPresenter;
 
     public SparseArrayObjectAdapter getAdapter() {
@@ -71,7 +68,7 @@
     }
 
     private void createComponents(Context context) {
-        mGlue = new PlaybackControlHelper(context, new PlaybackFragmentGlueHost(this)) {
+        mGlue = new PlaybackControlHelper(context) {
             @Override
             public int getUpdatePeriod() {
                 int totalTime = getControlsRow().getTotalTime();
@@ -82,17 +79,6 @@
             }
 
             @Override
-            protected void onRowChanged(PlaybackControlsRow row) {
-                if (getAdapter() == null) {
-                    return;
-                }
-                int index = getAdapter().indexOf(row);
-                if (index >= 0) {
-                    getAdapter().notifyArrayItemRangeChanged(index, 1);
-                }
-            }
-
-            @Override
             public void onActionClicked(Action action) {
                 if (action.getId() == R.id.lb_control_picture_in_picture) {
                     getActivity().enterPictureInPictureMode();
@@ -100,17 +86,22 @@
                 }
                 super.onActionClicked(action);
             }
+
+            @Override
+            protected void onCreateControlsRowAndPresenter() {
+                super.onCreateControlsRowAndPresenter();
+                getControlsRowPresenter().setSecondaryActionsHidden(SECONDARY_HIDDEN);
+            }
         };
 
-        mPlaybackControlsRowPresenter = mGlue.createControlsRowAndPresenter();
-        mPlaybackControlsRowPresenter.setSecondaryActionsHidden(SECONDARY_HIDDEN);
+        mGlue.setHost(new PlaybackFragmentGlueHost(this));
         mListRowPresenter = new ListRowPresenter();
 
         setAdapter(new SparseArrayObjectAdapter(new PresenterSelector() {
             @Override
             public Presenter getPresenter(Object object) {
                 if (object instanceof PlaybackControlsRow) {
-                    return mPlaybackControlsRowPresenter;
+                    return mGlue.getControlsRowPresenter();
                 } else if (object instanceof ListRow) {
                     return mListRowPresenter;
                 }
@@ -134,14 +125,11 @@
     @Override
     public void onStart() {
         super.onStart();
-        mGlue.setFadingEnabled(true);
-        mGlue.enableProgressUpdating(mGlue.hasValidMedia() && mGlue.isMediaPlaying());
         ((PlaybackOverlayActivity) getActivity()).registerPictureInPictureListener(this);
     }
 
     @Override
     public void onStop() {
-        mGlue.enableProgressUpdating(false);
         ((PlaybackOverlayActivity) getActivity()).unregisterPictureInPictureListener(this);
         super.onStop();
     }
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlaySupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlaySupportFragment.java
index c7dc7b7..baa2cb7 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlaySupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlaySupportFragment.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.os.Bundle;
-import android.support.v17.leanback.app.BackgroundManager;
 import android.support.v17.leanback.app.PlaybackSupportFragment;
 import android.support.v17.leanback.app.PlaybackSupportFragmentGlueHost;
 import android.support.v17.leanback.widget.Action;
@@ -27,7 +26,6 @@
 import android.support.v17.leanback.widget.ListRow;
 import android.support.v17.leanback.widget.ListRowPresenter;
 import android.support.v17.leanback.widget.PlaybackControlsRow;
-import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.PresenterSelector;
 import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
@@ -56,7 +54,6 @@
     private static final int ROW_CONTROLS = 0;
 
     private PlaybackControlHelper mGlue;
-    private PlaybackControlsRowPresenter mPlaybackControlsRowPresenter;
     private ListRowPresenter mListRowPresenter;
 
     public SparseArrayObjectAdapter getAdapter() {
@@ -74,7 +71,7 @@
     }
 
     private void createComponents(Context context) {
-        mGlue = new PlaybackControlHelper(context, new PlaybackSupportFragmentGlueHost(this)) {
+        mGlue = new PlaybackControlHelper(context) {
             @Override
             public int getUpdatePeriod() {
                 int totalTime = getControlsRow().getTotalTime();
@@ -85,17 +82,6 @@
             }
 
             @Override
-            protected void onRowChanged(PlaybackControlsRow row) {
-                if (getAdapter() == null) {
-                    return;
-                }
-                int index = getAdapter().indexOf(row);
-                if (index >= 0) {
-                    getAdapter().notifyArrayItemRangeChanged(index, 1);
-                }
-            }
-
-            @Override
             public void onActionClicked(Action action) {
                 if (action.getId() == R.id.lb_control_picture_in_picture) {
                     getActivity().enterPictureInPictureMode();
@@ -103,17 +89,22 @@
                 }
                 super.onActionClicked(action);
             }
+
+            @Override
+            protected void onCreateControlsRowAndPresenter() {
+                super.onCreateControlsRowAndPresenter();
+                getControlsRowPresenter().setSecondaryActionsHidden(SECONDARY_HIDDEN);
+            }
         };
 
-        mPlaybackControlsRowPresenter = mGlue.createControlsRowAndPresenter();
-        mPlaybackControlsRowPresenter.setSecondaryActionsHidden(SECONDARY_HIDDEN);
+        mGlue.setHost(new PlaybackSupportFragmentGlueHost(this));
         mListRowPresenter = new ListRowPresenter();
 
         setAdapter(new SparseArrayObjectAdapter(new PresenterSelector() {
             @Override
             public Presenter getPresenter(Object object) {
                 if (object instanceof PlaybackControlsRow) {
-                    return mPlaybackControlsRowPresenter;
+                    return mGlue.getControlsRowPresenter();
                 } else if (object instanceof ListRow) {
                     return mListRowPresenter;
                 }
@@ -137,14 +128,11 @@
     @Override
     public void onStart() {
         super.onStart();
-        mGlue.setFadingEnabled(true);
-        mGlue.enableProgressUpdating(mGlue.hasValidMedia() && mGlue.isMediaPlaying());
         ((PlaybackOverlaySupportActivity) getActivity()).registerPictureInPictureListener(this);
     }
 
     @Override
     public void onStop() {
-        mGlue.enableProgressUpdating(false);
         ((PlaybackOverlaySupportActivity) getActivity()).unregisterPictureInPictureListener(this);
         super.onStop();
     }
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleSupportVideoFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleSupportVideoFragment.java
index af95920..f9dd055 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleSupportVideoFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleSupportVideoFragment.java
@@ -16,9 +16,9 @@
 package com.example.android.leanback;
 
 import android.os.Bundle;
-import android.support.v17.leanback.app.MediaPlayerGlue;
-import android.support.v17.leanback.app.PlaybackGlue;
 import android.support.v17.leanback.app.VideoFragmentGlueHost;
+import android.support.v17.leanback.media.MediaPlayerGlue;
+import android.support.v17.leanback.media.PlaybackGlue;
 
 /**
  * Fragment demonstrating the use of {@link android.support.v17.leanback.app.VideoFragment} to
@@ -30,7 +30,7 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        mMediaPlayerGlue = new MediaPlayerGlue(getActivity(), null);
+        mMediaPlayerGlue = new MediaPlayerGlue(getActivity());
         mMediaPlayerGlue.setHost(new VideoFragmentGlueHost(this));
         mMediaPlayerGlue.setMode(MediaPlayerGlue.REPEAT_ALL);
     }
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleVideoFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleVideoFragment.java
index 3ffe161..0d55c89 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleVideoFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleVideoFragment.java
@@ -15,9 +15,9 @@
 
 import android.net.Uri;
 import android.os.Bundle;
-import android.support.v17.leanback.app.MediaPlayerGlue;
-import android.support.v17.leanback.app.PlaybackGlue;
 import android.support.v17.leanback.app.VideoFragmentGlueHost;
+import android.support.v17.leanback.media.MediaPlayerGlue;
+import android.support.v17.leanback.media.PlaybackGlue;
 
 /**
  * Fragment demonstrating the use of {@link android.support.v17.leanback.app.VideoFragment} to
@@ -44,7 +44,7 @@
         getView().postDelayed(new Runnable() {
             @Override
             public void run() {
-                mMediaPlayerGlue = new MediaPlayerGlue(getActivity(), null);
+                mMediaPlayerGlue = new MediaPlayerGlue(getActivity());
                 mMediaPlayerGlue.setMode(MediaPlayerGlue.REPEAT_ALL);
                 mMediaPlayerGlue.setPlayerCallback(new PlaybackGlue.PlayerCallback() {
                     @Override
@@ -64,7 +64,7 @@
         getView().postDelayed(new Runnable() {
             @Override
             public void run() {
-                mMediaPlayerGlue = new MediaPlayerGlue(getActivity(), null);
+                mMediaPlayerGlue = new MediaPlayerGlue(getActivity());
                 mMediaPlayerGlue.setMode(MediaPlayerGlue.REPEAT_ALL);
                 mMediaPlayerGlue.setPlayerCallback(new PlaybackGlue.PlayerCallback() {
                     @Override
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleVideoSupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleVideoSupportFragment.java
index dde7bd2..3c2c1ac 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleVideoSupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SampleVideoSupportFragment.java
@@ -18,9 +18,9 @@
 
 import android.net.Uri;
 import android.os.Bundle;
-import android.support.v17.leanback.app.MediaPlayerGlue;
-import android.support.v17.leanback.app.PlaybackGlue;
 import android.support.v17.leanback.app.VideoSupportFragmentGlueHost;
+import android.support.v17.leanback.media.MediaPlayerGlue;
+import android.support.v17.leanback.media.PlaybackGlue;
 
 /**
  * Fragment demonstrating the use of {@link android.support.v17.leanback.app.VideoSupportFragment} to
@@ -47,7 +47,7 @@
         getView().postDelayed(new Runnable() {
             @Override
             public void run() {
-                mMediaPlayerGlue = new MediaPlayerGlue(getActivity(), null);
+                mMediaPlayerGlue = new MediaPlayerGlue(getActivity());
                 mMediaPlayerGlue.setMode(MediaPlayerGlue.REPEAT_ALL);
                 mMediaPlayerGlue.setPlayerCallback(new PlaybackGlue.PlayerCallback() {
                     @Override
@@ -67,7 +67,7 @@
         getView().postDelayed(new Runnable() {
             @Override
             public void run() {
-                mMediaPlayerGlue = new MediaPlayerGlue(getActivity(), null);
+                mMediaPlayerGlue = new MediaPlayerGlue(getActivity());
                 mMediaPlayerGlue.setMode(MediaPlayerGlue.REPEAT_ALL);
                 mMediaPlayerGlue.setPlayerCallback(new PlaybackGlue.PlayerCallback() {
                     @Override
diff --git a/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java b/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java
index d4d8390..214ae64 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java
@@ -18,6 +18,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.support.v17.leanback.R;
+import android.support.v17.leanback.media.PlaybackGlueHost;
 import android.support.v17.leanback.transition.TransitionHelper;
 import android.support.v17.leanback.widget.BaseOnItemViewClickedListener;
 import android.support.v17.leanback.widget.BaseOnItemViewSelectedListener;
@@ -328,7 +329,7 @@
      * Creates an instance of {@link VideoFragment}. Subclasses can override this method
      * and provide their own instance of a {@link Fragment}. When you provide your own instance of
      * video fragment, you MUST also provide a custom
-     * {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost}.
+     * {@link android.support.v17.leanback.media.PlaybackGlueHost}.
      * @hide
      */
     public Fragment onCreateVideoFragment() {
@@ -337,11 +338,11 @@
 
     /**
      * Creates an instance of
-     * {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost}. The implementation
+     * {@link android.support.v17.leanback.media.PlaybackGlueHost}. The implementation
      * of this host depends on the instance of video fragment {@link #onCreateVideoFragment()}.
      * @hide
      */
-    public PlaybackGlue.PlaybackGlueHost onCreateVideoFragmentHost(Fragment fragment) {
+    public PlaybackGlueHost onCreateVideoFragmentHost(Fragment fragment) {
         return new VideoFragmentGlueHost((VideoFragment) fragment);
     }
 
@@ -366,11 +367,11 @@
 
     /**
      * This method initializes a video fragment, create an instance of
-     * {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost} using that fragment
+     * {@link android.support.v17.leanback.media.PlaybackGlueHost} using that fragment
      * and return it.
      * @hide
      */
-    public final PlaybackGlue.PlaybackGlueHost createPlaybackGlueHost() {
+    public final PlaybackGlueHost createPlaybackGlueHost() {
         Fragment fragment = findOrCreateVideoFragment();
         return onCreateVideoFragmentHost(fragment);
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/DetailsFragmentVideoHelper.java b/v17/leanback/src/android/support/v17/leanback/app/DetailsFragmentVideoHelper.java
index 7d58b13..415b850 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/DetailsFragmentVideoHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/DetailsFragmentVideoHelper.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.ValueAnimator;
 import android.graphics.drawable.Drawable;
+import android.support.v17.leanback.media.PlaybackGlue;
 import android.support.v17.leanback.widget.Parallax;
 import android.support.v17.leanback.widget.ParallaxRecyclerViewSource;
 import android.support.v17.leanback.widget.ParallaxTarget;
diff --git a/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java
index cbf6324..a969f1c 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java
@@ -21,6 +21,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.support.v17.leanback.R;
+import android.support.v17.leanback.media.PlaybackGlueHost;
 import android.support.v17.leanback.transition.TransitionHelper;
 import android.support.v17.leanback.widget.BaseOnItemViewClickedListener;
 import android.support.v17.leanback.widget.BaseOnItemViewSelectedListener;
@@ -331,7 +332,7 @@
      * Creates an instance of {@link VideoSupportFragment}. Subclasses can override this method
      * and provide their own instance of a {@link Fragment}. When you provide your own instance of
      * video fragment, you MUST also provide a custom
-     * {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost}.
+     * {@link android.support.v17.leanback.media.PlaybackGlueHost}.
      * @hide
      */
     public Fragment onCreateVideoSupportFragment() {
@@ -340,11 +341,11 @@
 
     /**
      * Creates an instance of
-     * {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost}. The implementation
+     * {@link android.support.v17.leanback.media.PlaybackGlueHost}. The implementation
      * of this host depends on the instance of video fragment {@link #onCreateVideoSupportFragment()}.
      * @hide
      */
-    public PlaybackGlue.PlaybackGlueHost onCreateVideoSupportFragmentHost(Fragment fragment) {
+    public PlaybackGlueHost onCreateVideoSupportFragmentHost(Fragment fragment) {
         return new VideoSupportFragmentGlueHost((VideoSupportFragment) fragment);
     }
 
@@ -369,11 +370,11 @@
 
     /**
      * This method initializes a video fragment, create an instance of
-     * {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost} using that fragment
+     * {@link android.support.v17.leanback.media.PlaybackGlueHost} using that fragment
      * and return it.
      * @hide
      */
-    public final PlaybackGlue.PlaybackGlueHost createPlaybackGlueHost() {
+    public final PlaybackGlueHost createPlaybackGlueHost() {
         Fragment fragment = findOrCreateVideoSupportFragment();
         return onCreateVideoSupportFragmentHost(fragment);
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/MediaControllerGlue.java b/v17/leanback/src/android/support/v17/leanback/app/MediaControllerGlue.java
index 5c75506..7949bfb 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/MediaControllerGlue.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/MediaControllerGlue.java
@@ -14,7 +14,9 @@
  * A helper class for implementing a glue layer between a
  * {@link PlaybackOverlayFragment} and a
  * {@link android.support.v4.media.session.MediaControllerCompat}.
+ * @deprecated Use {@link android.support.v17.leanback.media.MediaControllerGlue}.
  */
+@Deprecated
 public abstract class MediaControllerGlue extends PlaybackControlGlue {
     static final String TAG = "MediaControllerGlue";
     static final boolean DEBUG = false;
@@ -55,10 +57,7 @@
      * @param context
      * @param fragment
      * @param seekSpeeds Array of seek speeds for fast forward and rewind.
-     * @deprecated Use
-     * {@link #MediaControllerGlue(Context, PlaybackGlue.PlaybackGlueHost, int[], int[])}.
      */
-    @Deprecated
     public MediaControllerGlue(Context context,
                                PlaybackOverlayFragment fragment,
                                int[] seekSpeeds) {
@@ -78,10 +77,7 @@
      * @param fragment
      * @param fastForwardSpeeds Array of seek speeds for fast forward.
      * @param rewindSpeeds Array of seek speeds for rewind.
-     * @deprecated Use
-     * {@link #MediaControllerGlue(Context, PlaybackGlue.PlaybackGlueHost, int[], int[])}.
      */
-    @Deprecated
     public MediaControllerGlue(Context context,
                                PlaybackOverlayFragment fragment,
                                int[] fastForwardSpeeds,
@@ -90,21 +86,6 @@
     }
 
     /**
-     * Constructor for the glue.
-     *
-     * @param context
-     * @param host Optional; if using a {@link PlaybackGlue.PlaybackGlueHost}, pass it in.
-     * @param fastForwardSpeeds Array of seek speeds for fast forward.
-     * @param rewindSpeeds Array of seek speeds for rewind.
-     */
-    public MediaControllerGlue(Context context,
-            PlaybackGlueHost host,
-            int[] fastForwardSpeeds,
-            int[] rewindSpeeds) {
-        super(context, host, fastForwardSpeeds, rewindSpeeds);
-    }
-
-    /**
      * Attaches to the given media controller.
      */
     public void attachToMediaController(MediaControllerCompat mediaController) {
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlGlue.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlGlue.java
index 4892b69..56bd8da 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlGlue.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlGlue.java
@@ -14,12 +14,8 @@
 package android.support.v17.leanback.app;
 
 import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Handler;
-import android.os.Message;
-import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter;
+import android.support.v17.leanback.media.PlaybackGlueHost;
 import android.support.v17.leanback.widget.Action;
-import android.support.v17.leanback.widget.ControlButtonPresenterSelector;
 import android.support.v17.leanback.widget.OnActionClickedListener;
 import android.support.v17.leanback.widget.OnItemViewClickedListener;
 import android.support.v17.leanback.widget.PlaybackControlsRow;
@@ -29,16 +25,13 @@
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.RowPresenter;
 import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
-import android.util.Log;
 import android.view.InputEvent;
 import android.view.KeyEvent;
 import android.view.View;
 
-import java.lang.ref.WeakReference;
-
 /**
  * A helper class for managing a {@link android.support.v17.leanback.widget.PlaybackControlsRow}
- * and {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost} that implements a
+ * and {@link PlaybackGlueHost} that implements a
  * recommended approach to handling standard playback control actions such as play/pause,
  * fast forward/rewind at progressive speed levels, and skip to next/previous. This helper class
  * is a glue layer in that manages the configuration of and interaction between the
@@ -51,8 +44,7 @@
  * </p>
  *
  * <p>To use an instance of the glue layer, first construct an instance.  Constructor parameters
- * inform the glue what speed levels are supported for fast forward/rewind. Providing a
- * {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost} is optional.
+ * inform the glue what speed levels are supported for fast forward/rewind.
  * </p>
  *
  * <p>If you have your own controls row you must pass it to {@link #setControlsRow}.
@@ -73,7 +65,7 @@
  * </p>
  *
  * <p>This helper implements a key event handler. If you pass a
- * {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost}, it will configure it's
+ * {@link PlaybackOverlayFragment}, it will configure it's
  * fragment to intercept all key events.  Otherwise, you should set the glue object as key event
  * handler to the ViewHolder when bound by your row presenter; see
  * {@link RowPresenter.ViewHolder#setOnKeyListener(android.view.View.OnKeyListener)}.
@@ -83,144 +75,13 @@
  * to manage the lifecycle of a periodic callback to {@link #updateProgress()}.
  * {@link #getUpdatePeriod()} provides a recommended update period.
  * </p>
- *
+ * @deprecated Use {@link android.support.v17.leanback.media.PlaybackControlGlue}
  */
-public abstract class PlaybackControlGlue extends PlaybackGlue
-        implements OnActionClickedListener, View.OnKeyListener {
-    /**
-     * The adapter key for the first custom control on the left side
-     * of the predefined primary controls.
-     */
-    public static final int ACTION_CUSTOM_LEFT_FIRST = 0x1;
+@Deprecated
+public abstract class PlaybackControlGlue extends
+        android.support.v17.leanback.media.PlaybackControlGlue {
 
-    /**
-     * The adapter key for the skip to previous control.
-     */
-    public static final int ACTION_SKIP_TO_PREVIOUS = 0x10;
-
-    /**
-     * The adapter key for the rewind control.
-     */
-    public static final int ACTION_REWIND = 0x20;
-
-    /**
-     * The adapter key for the play/pause control.
-     */
-    public static final int ACTION_PLAY_PAUSE = 0x40;
-
-    /**
-     * The adapter key for the fast forward control.
-     */
-    public static final int ACTION_FAST_FORWARD = 0x80;
-
-    /**
-     * The adapter key for the skip to next control.
-     */
-    public static final int ACTION_SKIP_TO_NEXT = 0x100;
-
-    /**
-     * The adapter key for the first custom control on the right side
-     * of the predefined primary controls.
-     */
-    public static final int ACTION_CUSTOM_RIGHT_FIRST = 0x1000;
-
-    /**
-     * Invalid playback speed.
-     */
-    public static final int PLAYBACK_SPEED_INVALID = -1;
-
-    /**
-     * Speed representing playback state that is paused.
-     */
-    public static final int PLAYBACK_SPEED_PAUSED = 0;
-
-    /**
-     * Speed representing playback state that is playing normally.
-     */
-    public static final int PLAYBACK_SPEED_NORMAL = 1;
-
-    /**
-     * The initial (level 0) fast forward playback speed.
-     * The negative of this value is for rewind at the same speed.
-     */
-    public static final int PLAYBACK_SPEED_FAST_L0 = 10;
-
-    /**
-     * The level 1 fast forward playback speed.
-     * The negative of this value is for rewind at the same speed.
-     */
-    public static final int PLAYBACK_SPEED_FAST_L1 = 11;
-
-    /**
-     * The level 2 fast forward playback speed.
-     * The negative of this value is for rewind at the same speed.
-     */
-    public static final int PLAYBACK_SPEED_FAST_L2 = 12;
-
-    /**
-     * The level 3 fast forward playback speed.
-     * The negative of this value is for rewind at the same speed.
-     */
-    public static final int PLAYBACK_SPEED_FAST_L3 = 13;
-
-    /**
-     * The level 4 fast forward playback speed.
-     * The negative of this value is for rewind at the same speed.
-     */
-    public static final int PLAYBACK_SPEED_FAST_L4 = 14;
-
-    static final String TAG = "PlaybackControlGlue";
-    static final boolean DEBUG = false;
-
-    static final int MSG_UPDATE_PLAYBACK_STATE = 100;
-    private static final int UPDATE_PLAYBACK_STATE_DELAY_MS = 2000;
-    private static final int NUMBER_OF_SEEK_SPEEDS = PLAYBACK_SPEED_FAST_L4
-            - PLAYBACK_SPEED_FAST_L0 + 1;
-
-    private final int[] mFastForwardSpeeds;
-    private final int[] mRewindSpeeds;
-    private PlaybackControlsRow mControlsRow;
-    private SparseArrayObjectAdapter mPrimaryActionsAdapter;
-    private PlaybackControlsRow.PlayPauseAction mPlayPauseAction;
-    private PlaybackControlsRow.SkipNextAction mSkipNextAction;
-    private PlaybackControlsRow.SkipPreviousAction mSkipPreviousAction;
-    private PlaybackControlsRow.FastForwardAction mFastForwardAction;
-    private PlaybackControlsRow.RewindAction mRewindAction;
     OnItemViewClickedListener mExternalOnItemViewClickedListener;
-    private int mPlaybackSpeed = PLAYBACK_SPEED_NORMAL;
-    private boolean mFadeWhenPlaying = true;
-
-    static class UpdatePlaybackStateHandler extends Handler {
-        @Override
-        public void handleMessage(Message msg) {
-            if (msg.what == MSG_UPDATE_PLAYBACK_STATE) {
-                PlaybackControlGlue glue = ((WeakReference<PlaybackControlGlue>) msg.obj).get();
-                if (glue != null) {
-                    glue.updatePlaybackState();
-                }
-            }
-        }
-    }
-
-    static final Handler sHandler = new UpdatePlaybackStateHandler();
-
-    final WeakReference<PlaybackControlGlue> mGlueWeakReference =  new WeakReference(this);
-
-    /**
-     * Interface allowing the application to handle input events.
-     * @deprecated Use
-     * {@link PlaybackGlue.PlaybackGlueHost#setOnKeyInterceptListener(View.OnKeyListener)}.
-     */
-    @Deprecated
-    public interface InputEventHandler {
-        /**
-         * Called when an {@link InputEvent} is received.
-         *
-         * @return If the event should be consumed, return true. To allow the event to
-         * continue on to the next handler, return false.
-         */
-        boolean handleInputEvent(InputEvent event);
-    }
 
     /**
      * Constructor for the glue.
@@ -229,7 +90,7 @@
      * @param seekSpeeds Array of seek speeds for fast forward and rewind.
      */
     public PlaybackControlGlue(Context context, int[] seekSpeeds) {
-        this(context, (PlaybackGlueHost) null, seekSpeeds, seekSpeeds);
+        super(context, seekSpeeds, seekSpeeds);
     }
 
     /**
@@ -242,7 +103,7 @@
     public PlaybackControlGlue(Context context,
                                int[] fastForwardSpeeds,
                                int[] rewindSpeeds) {
-        this(context, (PlaybackGlueHost) null, fastForwardSpeeds, rewindSpeeds);
+        super(context, fastForwardSpeeds, rewindSpeeds);
     }
 
     /**
@@ -251,10 +112,7 @@
      * @param context
      * @param fragment Optional; if using a {@link PlaybackOverlayFragment}, pass it in.
      * @param seekSpeeds Array of seek speeds for fast forward and rewind.
-     * @deprecated Use
-     * {@link #PlaybackControlGlue(Context, PlaybackGlue.PlaybackGlueHost, int[], int[])}.
      */
-    @Deprecated
     public PlaybackControlGlue(Context context,
                                PlaybackOverlayFragment fragment,
                                int[] seekSpeeds) {
@@ -268,110 +126,26 @@
      * @param fragment Optional; if using a {@link PlaybackOverlayFragment}, pass it in.
      * @param fastForwardSpeeds Array of seek speeds for fast forward.
      * @param rewindSpeeds Array of seek speeds for rewind.
-     * @deprecated Use
-     * {@link #PlaybackControlGlue(Context, PlaybackGlue.PlaybackGlueHost, int[], int[])}.
      */
-    @Deprecated
     public PlaybackControlGlue(Context context,
                                PlaybackOverlayFragment fragment,
                                int[] fastForwardSpeeds,
                                int[] rewindSpeeds) {
-        this(context, fragment == null ? (PlaybackGlueHost) null:
-                new PlaybackGlueHostOld(fragment), fastForwardSpeeds, rewindSpeeds);
-    }
-
-    /**
-     * Constructor for the glue.
-     *
-     * @param context
-     * @param host Optional; if using a {@link PlaybackGlue.PlaybackGlueHost}, pass it in.
-     * @param fastForwardSpeeds Array of seek speeds for fast forward.
-     * @param rewindSpeeds Array of seek speeds for rewind.
-     */
-    public PlaybackControlGlue(Context context,
-                               PlaybackGlueHost host,
-                               int[] fastForwardSpeeds,
-                               int[] rewindSpeeds) {
-        super(context);
-        if (fastForwardSpeeds.length == 0 || fastForwardSpeeds.length > NUMBER_OF_SEEK_SPEEDS) {
-            throw new IllegalStateException("invalid fastForwardSpeeds array size");
-        }
-        mFastForwardSpeeds = fastForwardSpeeds;
-        if (rewindSpeeds.length == 0 || rewindSpeeds.length > NUMBER_OF_SEEK_SPEEDS) {
-            throw new IllegalStateException("invalid rewindSpeeds array size");
-        }
-        mRewindSpeeds = rewindSpeeds;
-        setHost(host);
+        super(context, fastForwardSpeeds, rewindSpeeds);
+        setHost(fragment == null ? (PlaybackGlueHost) null : new PlaybackGlueHostOld(fragment));
     }
 
     @Override
-    public void setHost(PlaybackGlueHost host) {
-        super.setHost(host);
-        if (getHost() != null) {
-            if (getHost() instanceof PlaybackGlueHostOld) {
-                ((PlaybackGlueHostOld) getHost()).mGlue = this;
-            }
-            getHost().setOnKeyInterceptListener(this);
-            getHost().setOnActionClickedListener(this);
-            getHost().setPlaybackRowPresenter(createControlsRowAndPresenter());
-            getHost().setPlaybackRow(getControlsRow());
-            getHost().setHostLifeCycleCallback(new HostLifecycleCallback() {
-                @Override
-                public void onHostStart() {
-                    enableProgressUpdating(true);
-                }
-
-                @Override
-                public void onHostStop() {
-                    enableProgressUpdating(false);
-                }
-            });
+    protected void onAttachedToHost(PlaybackGlueHost host) {
+        super.onAttachedToHost(host);
+        if (host instanceof PlaybackGlueHostOld) {
+            ((PlaybackGlueHostOld) host).mGlue = this;
         }
     }
 
     /**
-     * Helper method for instantiating a {@link PlaybackControlsRow} and corresponding
-     * {@link PlaybackControlsRowPresenter}.
-     */
-    public PlaybackControlsRowPresenter createControlsRowAndPresenter() {
-        PlaybackControlsRow controlsRow = new PlaybackControlsRow(this);
-        setControlsRow(controlsRow);
-
-        final AbstractDetailsDescriptionPresenter detailsPresenter =
-                new AbstractDetailsDescriptionPresenter() {
-            @Override
-            protected void onBindDescription(AbstractDetailsDescriptionPresenter.ViewHolder
-                                                     viewHolder, Object object) {
-                PlaybackControlGlue glue = (PlaybackControlGlue) object;
-                if (glue.hasValidMedia()) {
-                    viewHolder.getTitle().setText(glue.getMediaTitle());
-                    viewHolder.getSubtitle().setText(glue.getMediaSubtitle());
-                } else {
-                    viewHolder.getTitle().setText("");
-                    viewHolder.getSubtitle().setText("");
-                }
-            }
-        };
-
-        return new PlaybackControlsRowPresenter(detailsPresenter) {
-            @Override
-            protected void onBindRowViewHolder(RowPresenter.ViewHolder vh, Object item) {
-                super.onBindRowViewHolder(vh, item);
-                vh.setOnKeyListener(PlaybackControlGlue.this);
-            }
-            @Override
-            protected void onUnbindRowViewHolder(RowPresenter.ViewHolder vh) {
-                super.onUnbindRowViewHolder(vh);
-                vh.setOnKeyListener(null);
-            }
-        };
-    }
-
-    /**
      * Returns the fragment.
-     * @deprecated The glue is no longer associated with a fragment, use {@link #getHost()}.
      */
-    @Deprecated
     public PlaybackOverlayFragment getFragment() {
         if (getHost() instanceof PlaybackGlueHostOld) {
             return ((PlaybackGlueHostOld)getHost()).mFragment;
@@ -380,456 +154,6 @@
     }
 
     /**
-     * Returns the fast forward speeds.
-     */
-    public int[] getFastForwardSpeeds() {
-        return mFastForwardSpeeds;
-    }
-
-    /**
-     * Returns the rewind speeds.
-     */
-    public int[] getRewindSpeeds() {
-        return mRewindSpeeds;
-    }
-
-    /**
-     * Sets the controls to fade after a timeout when media is playing.
-     */
-    public void setFadingEnabled(boolean enable) {
-        mFadeWhenPlaying = enable;
-        if (!mFadeWhenPlaying && getHost() != null) {
-            getHost().setFadingEnabled(false);
-        }
-    }
-
-    /**
-     * Returns true if controls are set to fade when media is playing.
-     */
-    public boolean isFadingEnabled() {
-        return mFadeWhenPlaying;
-    }
-
-    /**
-     * Set the {@link OnItemViewClickedListener} to be called if the click event
-     * is not handled internally.
-     * @param listener
-     * @deprecated Don't call this. Instead use the listener on the fragment yourself.
-     */
-    @Deprecated
-    public void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
-        mExternalOnItemViewClickedListener = listener;
-    }
-
-    /**
-     * Returns the {@link OnItemViewClickedListener}.
-     * @deprecated Don't call this. Instead use the listener on the fragment yourself.
-     */
-    @Deprecated
-    public OnItemViewClickedListener getOnItemViewClickedListener() {
-        return mExternalOnItemViewClickedListener;
-    }
-
-    /**
-     * Sets the controls row to be managed by the glue layer.
-     * The primary actions and playback state related aspects of the row
-     * are updated by the glue.
-     */
-    public void setControlsRow(PlaybackControlsRow controlsRow) {
-        mControlsRow = controlsRow;
-        mPrimaryActionsAdapter = createPrimaryActionsAdapter(
-                new ControlButtonPresenterSelector());
-        mControlsRow.setPrimaryActionsAdapter(mPrimaryActionsAdapter);
-        updateControlsRow();
-    }
-
-    /**
-     * Returns the playback controls row managed by the glue layer.
-     */
-    public PlaybackControlsRow getControlsRow() {
-        return mControlsRow;
-    }
-
-    /**
-     * Override this to start/stop a runnable to call {@link #updateProgress} at
-     * an interval such as {@link #getUpdatePeriod}.
-     */
-    public void enableProgressUpdating(boolean enable) {
-    }
-
-    /**
-     * Returns the time period in milliseconds that should be used
-     * to update the progress.  See {@link #updateProgress()}.
-     */
-    public int getUpdatePeriod() {
-        // TODO: calculate a better update period based on total duration and screen size
-        return 500;
-    }
-
-    /**
-     * Updates the progress bar based on the current media playback position.
-     */
-    public void updateProgress() {
-        int position = getCurrentPosition();
-        if (DEBUG) Log.v(TAG, "updateProgress " + position);
-        mControlsRow.setCurrentTime(position);
-    }
-
-    /**
-     * Handles action clicks.  A subclass may override this add support for additional actions.
-     */
-    @Override
-    public void onActionClicked(Action action) {
-        dispatchAction(action, null);
-    }
-
-    /**
-     * Handles key events and returns true if handled.  A subclass may override this to provide
-     * additional support.
-     */
-    @Override
-    public boolean onKey(View v, int keyCode, KeyEvent event) {
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_DPAD_UP:
-            case KeyEvent.KEYCODE_DPAD_DOWN:
-            case KeyEvent.KEYCODE_DPAD_RIGHT:
-            case KeyEvent.KEYCODE_DPAD_LEFT:
-            case KeyEvent.KEYCODE_BACK:
-            case KeyEvent.KEYCODE_ESCAPE:
-                boolean abortSeek = mPlaybackSpeed >= PLAYBACK_SPEED_FAST_L0
-                        || mPlaybackSpeed <= -PLAYBACK_SPEED_FAST_L0;
-                if (abortSeek) {
-                    mPlaybackSpeed = PLAYBACK_SPEED_NORMAL;
-                    startPlayback(mPlaybackSpeed);
-                    updatePlaybackStatusAfterUserAction();
-                    return keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE;
-                }
-                return false;
-        }
-        Action action = mControlsRow.getActionForKeyCode(mPrimaryActionsAdapter, keyCode);
-
-        if (action != null) {
-            if (action == mPrimaryActionsAdapter.lookup(ACTION_PLAY_PAUSE)
-                    || action == mPrimaryActionsAdapter.lookup(ACTION_REWIND)
-                    || action == mPrimaryActionsAdapter.lookup(ACTION_FAST_FORWARD)
-                    || action == mPrimaryActionsAdapter.lookup(ACTION_SKIP_TO_PREVIOUS)
-                    || action == mPrimaryActionsAdapter.lookup(ACTION_SKIP_TO_NEXT)) {
-                if (event.getAction() == KeyEvent.ACTION_DOWN) {
-                    dispatchAction(action, event);
-                }
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Called when the given action is invoked, either by click or keyevent.
-     */
-    boolean dispatchAction(Action action, KeyEvent keyEvent) {
-        boolean handled = false;
-        if (action == mPlayPauseAction) {
-            boolean canPlay = keyEvent == null
-                    || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
-                    || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY;
-            boolean canPause = keyEvent == null
-                    || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
-                    || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PAUSE;
-            //            PLAY_PAUSE    PLAY      PAUSE
-            // playing    paused                  paused
-            // paused     playing       playing
-            // ff/rw      playing       playing   paused
-            if (canPause &&
-                (canPlay ? mPlaybackSpeed == PLAYBACK_SPEED_NORMAL :
-                        mPlaybackSpeed != PLAYBACK_SPEED_PAUSED)) {
-                mPlaybackSpeed = PLAYBACK_SPEED_PAUSED;
-                pausePlayback();
-            } else if (canPlay && mPlaybackSpeed != PLAYBACK_SPEED_NORMAL) {
-                mPlaybackSpeed = PLAYBACK_SPEED_NORMAL;
-                startPlayback(mPlaybackSpeed);
-            }
-            updatePlaybackStatusAfterUserAction();
-            handled = true;
-        } else if (action == mSkipNextAction) {
-            skipToNext();
-            handled = true;
-        } else if (action == mSkipPreviousAction) {
-            skipToPrevious();
-            handled = true;
-        } else if (action == mFastForwardAction) {
-            if (mPlaybackSpeed < getMaxForwardSpeedId()) {
-                switch (mPlaybackSpeed) {
-                    case PLAYBACK_SPEED_FAST_L0:
-                    case PLAYBACK_SPEED_FAST_L1:
-                    case PLAYBACK_SPEED_FAST_L2:
-                    case PLAYBACK_SPEED_FAST_L3:
-                        mPlaybackSpeed++;
-                        break;
-                    default:
-                        mPlaybackSpeed = PLAYBACK_SPEED_FAST_L0;
-                        break;
-                }
-                startPlayback(mPlaybackSpeed);
-                updatePlaybackStatusAfterUserAction();
-            }
-            handled = true;
-        } else if (action == mRewindAction) {
-            if (mPlaybackSpeed > -getMaxRewindSpeedId()) {
-                switch (mPlaybackSpeed) {
-                    case -PLAYBACK_SPEED_FAST_L0:
-                    case -PLAYBACK_SPEED_FAST_L1:
-                    case -PLAYBACK_SPEED_FAST_L2:
-                    case -PLAYBACK_SPEED_FAST_L3:
-                        mPlaybackSpeed--;
-                        break;
-                    default:
-                        mPlaybackSpeed = -PLAYBACK_SPEED_FAST_L0;
-                        break;
-                }
-                startPlayback(mPlaybackSpeed);
-                updatePlaybackStatusAfterUserAction();
-            }
-            handled = true;
-        }
-        return handled;
-    }
-
-    private int getMaxForwardSpeedId() {
-        return PLAYBACK_SPEED_FAST_L0 + (mFastForwardSpeeds.length - 1);
-    }
-
-    private int getMaxRewindSpeedId() {
-        return PLAYBACK_SPEED_FAST_L0 + (mRewindSpeeds.length - 1);
-    }
-
-    private void updateControlsRow() {
-        updateRowMetadata();
-        sHandler.removeMessages(MSG_UPDATE_PLAYBACK_STATE, mGlueWeakReference);
-        updatePlaybackState();
-    }
-
-    private void updatePlaybackStatusAfterUserAction() {
-        updatePlaybackState(mPlaybackSpeed);
-        // Sync playback state after a delay
-        sHandler.removeMessages(MSG_UPDATE_PLAYBACK_STATE, mGlueWeakReference);
-        sHandler.sendMessageDelayed(sHandler.obtainMessage(MSG_UPDATE_PLAYBACK_STATE,
-                mGlueWeakReference), UPDATE_PLAYBACK_STATE_DELAY_MS);
-    }
-
-    private void updateRowMetadata() {
-        if (mControlsRow == null) {
-            return;
-        }
-
-        if (DEBUG) Log.v(TAG, "updateRowMetadata");
-
-        if (!hasValidMedia()) {
-            mControlsRow.setImageDrawable(null);
-            mControlsRow.setTotalTime(0);
-            mControlsRow.setCurrentTime(0);
-        } else {
-            mControlsRow.setImageDrawable(getMediaArt());
-            mControlsRow.setTotalTime(getMediaDuration());
-            mControlsRow.setCurrentTime(getCurrentPosition());
-        }
-
-        if (getHost() != null) {
-            getHost().notifyPlaybackRowChanged();
-        }
-    }
-
-    void updatePlaybackState() {
-        if (hasValidMedia()) {
-            mPlaybackSpeed = getCurrentSpeedId();
-            updatePlaybackState(mPlaybackSpeed);
-        }
-    }
-
-    private void updatePlaybackState(int playbackSpeed) {
-        if (mControlsRow == null) {
-            return;
-        }
-
-        final long actions = getSupportedActions();
-        if ((actions & ACTION_SKIP_TO_PREVIOUS) != 0) {
-            if (mSkipPreviousAction == null) {
-                mSkipPreviousAction = new PlaybackControlsRow.SkipPreviousAction(getContext());
-            }
-            mPrimaryActionsAdapter.set(ACTION_SKIP_TO_PREVIOUS, mSkipPreviousAction);
-        } else {
-            mPrimaryActionsAdapter.clear(ACTION_SKIP_TO_PREVIOUS);
-            mSkipPreviousAction = null;
-        }
-        if ((actions & ACTION_REWIND) != 0) {
-            if (mRewindAction == null) {
-                mRewindAction = new PlaybackControlsRow.RewindAction(
-                        getContext(),
-                        mRewindSpeeds.length);
-            }
-            mPrimaryActionsAdapter.set(ACTION_REWIND, mRewindAction);
-        } else {
-            mPrimaryActionsAdapter.clear(ACTION_REWIND);
-            mRewindAction = null;
-        }
-        if ((actions & ACTION_PLAY_PAUSE) != 0) {
-            if (mPlayPauseAction == null) {
-                mPlayPauseAction = new PlaybackControlsRow.PlayPauseAction(getContext());
-            }
-            mPrimaryActionsAdapter.set(ACTION_PLAY_PAUSE, mPlayPauseAction);
-        } else {
-            mPrimaryActionsAdapter.clear(ACTION_PLAY_PAUSE);
-            mPlayPauseAction = null;
-        }
-        if ((actions & ACTION_FAST_FORWARD) != 0) {
-            if (mFastForwardAction == null) {
-                mFastForwardAction = new PlaybackControlsRow.FastForwardAction(
-                        getContext(),
-                        mFastForwardSpeeds.length);
-            }
-            mPrimaryActionsAdapter.set(ACTION_FAST_FORWARD, mFastForwardAction);
-        } else {
-            mPrimaryActionsAdapter.clear(ACTION_FAST_FORWARD);
-            mFastForwardAction = null;
-        }
-        if ((actions & ACTION_SKIP_TO_NEXT) != 0) {
-            if (mSkipNextAction == null) {
-                mSkipNextAction = new PlaybackControlsRow.SkipNextAction(getContext());
-            }
-            mPrimaryActionsAdapter.set(ACTION_SKIP_TO_NEXT, mSkipNextAction);
-        } else {
-            mPrimaryActionsAdapter.clear(ACTION_SKIP_TO_NEXT);
-            mSkipNextAction = null;
-        }
-
-        if (mFastForwardAction != null) {
-            int index = 0;
-            if (playbackSpeed >= PLAYBACK_SPEED_FAST_L0) {
-                index = playbackSpeed - PLAYBACK_SPEED_FAST_L0 + 1;
-            }
-            if (mFastForwardAction.getIndex() != index) {
-                mFastForwardAction.setIndex(index);
-                notifyItemChanged(mPrimaryActionsAdapter, mFastForwardAction);
-            }
-        }
-        if (mRewindAction != null) {
-            int index = 0;
-            if (playbackSpeed <= -PLAYBACK_SPEED_FAST_L0) {
-                index = -playbackSpeed - PLAYBACK_SPEED_FAST_L0 + 1;
-            }
-            if (mRewindAction.getIndex() != index) {
-                mRewindAction.setIndex(index);
-                notifyItemChanged(mPrimaryActionsAdapter, mRewindAction);
-            }
-        }
-
-        if (playbackSpeed == PLAYBACK_SPEED_PAUSED) {
-            updateProgress();
-            enableProgressUpdating(false);
-        } else {
-            enableProgressUpdating(true);
-        }
-
-        if (mFadeWhenPlaying && getHost() != null) {
-            getHost().setFadingEnabled(playbackSpeed == PLAYBACK_SPEED_NORMAL);
-        }
-
-        if (mPlayPauseAction != null) {
-            int index = playbackSpeed == PLAYBACK_SPEED_PAUSED
-                    ? PlaybackControlsRow.PlayPauseAction.PLAY
-                    : PlaybackControlsRow.PlayPauseAction.PAUSE;
-            if (mPlayPauseAction.getIndex() != index) {
-                mPlayPauseAction.setIndex(index);
-                notifyItemChanged(mPrimaryActionsAdapter, mPlayPauseAction);
-            }
-        }
-    }
-
-    private static void notifyItemChanged(SparseArrayObjectAdapter adapter, Object object) {
-        int index = adapter.indexOf(object);
-        if (index >= 0) {
-            adapter.notifyArrayItemRangeChanged(index, 1);
-        }
-    }
-
-    private static String getSpeedString(int speed) {
-        switch (speed) {
-            case PLAYBACK_SPEED_INVALID:
-                return "PLAYBACK_SPEED_INVALID";
-            case PLAYBACK_SPEED_PAUSED:
-                return "PLAYBACK_SPEED_PAUSED";
-            case PLAYBACK_SPEED_NORMAL:
-                return "PLAYBACK_SPEED_NORMAL";
-            case PLAYBACK_SPEED_FAST_L0:
-                return "PLAYBACK_SPEED_FAST_L0";
-            case PLAYBACK_SPEED_FAST_L1:
-                return "PLAYBACK_SPEED_FAST_L1";
-            case PLAYBACK_SPEED_FAST_L2:
-                return "PLAYBACK_SPEED_FAST_L2";
-            case PLAYBACK_SPEED_FAST_L3:
-                return "PLAYBACK_SPEED_FAST_L3";
-            case PLAYBACK_SPEED_FAST_L4:
-                return "PLAYBACK_SPEED_FAST_L4";
-            case -PLAYBACK_SPEED_FAST_L0:
-                return "-PLAYBACK_SPEED_FAST_L0";
-            case -PLAYBACK_SPEED_FAST_L1:
-                return "-PLAYBACK_SPEED_FAST_L1";
-            case -PLAYBACK_SPEED_FAST_L2:
-                return "-PLAYBACK_SPEED_FAST_L2";
-            case -PLAYBACK_SPEED_FAST_L3:
-                return "-PLAYBACK_SPEED_FAST_L3";
-            case -PLAYBACK_SPEED_FAST_L4:
-                return "-PLAYBACK_SPEED_FAST_L4";
-        }
-        return null;
-    }
-
-    /**
-     * Returns true if there is a valid media item.
-     */
-    public abstract boolean hasValidMedia();
-
-    /**
-     * Returns true if media is currently playing.
-     */
-    public abstract boolean isMediaPlaying();
-
-    /**
-     * Returns the title of the media item.
-     */
-    public abstract CharSequence getMediaTitle();
-
-    /**
-     * Returns the subtitle of the media item.
-     */
-    public abstract CharSequence getMediaSubtitle();
-
-    /**
-     * Returns the duration of the media item in milliseconds.
-     */
-    public abstract int getMediaDuration();
-
-    /**
-     * Returns a bitmap of the art for the media item.
-     */
-    public abstract Drawable getMediaArt();
-
-    /**
-     * Returns a bitmask of actions supported by the media player.
-     */
-    public abstract long getSupportedActions();
-
-    /**
-     * Returns the current playback speed.  When playing normally,
-     * {@link #PLAYBACK_SPEED_NORMAL} should be returned.
-     */
-    public abstract int getCurrentSpeedId();
-
-    /**
-     * Returns the current position of the media item in milliseconds.
-     */
-    public abstract int getCurrentPosition();
-
-    /**
      * Start playback at the given speed.
      * @deprecated use {@link #play()} instead.
      *
@@ -861,13 +185,30 @@
     @Deprecated
     protected void skipToPrevious() {}
 
+    @Override
+    public final void next() {
+        skipToNext();
+    }
+
+    @Override
+    public final void previous() {
+        skipToPrevious();
+    }
+
+    @Override
+    public final void play(int speed) {
+        startPlayback(speed);
+    }
+
+    @Override
+    public final void pause() {
+        pausePlayback();
+    }
+
     /**
      * This method invoked when the playback controls row has changed. The adapter
-     * containing this row should be notified. This method would be delegated to
-     * {@see android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost#notifyPlaybackRowChanged}.
-     * @deprecated see {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost}.
+     * containing this row should be notified.
      */
-    @Deprecated
     protected void onRowChanged(PlaybackControlsRow row) {
         if (getHost() != null) {
             getHost().notifyPlaybackRowChanged();
@@ -875,46 +216,61 @@
     }
 
     /**
-     * Creates the primary action adapter.  May be overridden to add additional primary
-     * actions to the adapter.
+     * Set the {@link OnItemViewClickedListener} to be called if the click event
+     * is not handled internally.
+     * @param listener
+     * @deprecated Don't call this. Instead use the listener on the fragment yourself.
      */
+    @Deprecated
+    public void setOnItemViewClickedListener(OnItemViewClickedListener listener) {
+        mExternalOnItemViewClickedListener = listener;
+    }
+
+    /**
+     * Returns the {@link OnItemViewClickedListener}.
+     * @deprecated Don't call this. Instead use the listener on the fragment yourself.
+     */
+    @Deprecated
+    public OnItemViewClickedListener getOnItemViewClickedListener() {
+        return mExternalOnItemViewClickedListener;
+    }
+
+    @Override
+    protected void onCreateControlsRowAndPresenter() {
+        // backward compatible, we dont create row / presenter by default.
+        // User is expected to call createControlsRowAndPresenter() or setControlsRow()
+        // explicitly.
+    }
+
+    /**
+     * Helper method for instantiating a
+     * {@link android.support.v17.leanback.widget.PlaybackControlsRow} and corresponding
+     * {@link android.support.v17.leanback.widget.PlaybackControlsRowPresenter}.
+     */
+    public PlaybackControlsRowPresenter createControlsRowAndPresenter() {
+        super.onCreateControlsRowAndPresenter();
+        return getControlsRowPresenter();
+    }
+
     protected SparseArrayObjectAdapter createPrimaryActionsAdapter(
             PresenterSelector presenterSelector) {
-        return new SparseArrayObjectAdapter(presenterSelector);
+        return super.createPrimaryActionsAdapter(presenterSelector);
     }
 
     /**
-     * Must be called appropriately by a subclass when the playback state has changed.
-     * It updates the playback state displayed on the media player.
+     * Interface allowing the application to handle input events.
+     * @deprecated Use
+     * {@link PlaybackGlueHost#setOnKeyInterceptListener(View.OnKeyListener)}.
      */
-    protected void onStateChanged() {
-        if (DEBUG) Log.v(TAG, "onStateChanged");
-        // If a pending control button update is present, delay
-        // the update until the state settles.
-        if (!hasValidMedia()) {
-            return;
-        }
-        if (sHandler.hasMessages(MSG_UPDATE_PLAYBACK_STATE, mGlueWeakReference)) {
-            sHandler.removeMessages(MSG_UPDATE_PLAYBACK_STATE, mGlueWeakReference);
-            if (getCurrentSpeedId() != mPlaybackSpeed) {
-                if (DEBUG) Log.v(TAG, "Status expectation mismatch, delaying update");
-                sHandler.sendMessageDelayed(sHandler.obtainMessage(MSG_UPDATE_PLAYBACK_STATE,
-                        mGlueWeakReference), UPDATE_PLAYBACK_STATE_DELAY_MS);
-            } else {
-                if (DEBUG) Log.v(TAG, "Update state matches expectation");
-                updatePlaybackState();
-            }
-        } else {
-            updatePlaybackState();
-        }
-    }
-
-    /**
-     * Must be called appropriately by a subclass when the metadata state has changed.
-     */
-    protected void onMetadataChanged() {
-        if (DEBUG) Log.v(TAG, "onMetadataChanged");
-        updateRowMetadata();
+    @Deprecated
+    public interface InputEventHandler {
+        /**
+         * Called when an {@link InputEvent} is received.
+         *
+         * @return If the event should be consumed, return true. To allow the event to
+         * continue on to the next handler, return false.
+         */
+        boolean handleInputEvent(InputEvent event);
     }
 
     static final class PlaybackGlueHostOld extends PlaybackGlueHost {
@@ -931,13 +287,13 @@
         }
 
         @Override
-        public void setOnKeyInterceptListener(final View.OnKeyListener onKeyListenerr) {
+        public void setOnKeyInterceptListener(final View.OnKeyListener onKeyListener) {
             mFragment.setEventHandler( new InputEventHandler() {
                 @Override
                 public boolean handleInputEvent(InputEvent event) {
                     if (event instanceof KeyEvent) {
                         KeyEvent keyEvent = (KeyEvent) event;
-                        return onKeyListenerr.onKey(null, keyEvent.getKeyCode(), keyEvent);
+                        return onKeyListener.onKey(null, keyEvent.getKeyCode(), keyEvent);
                     }
                     return false;
                 }
@@ -952,13 +308,23 @@
                                           RowPresenter.ViewHolder rowViewHolder, Row row) {
                     if (item instanceof Action) {
                         listener.onActionClicked((Action)item);
-                        if (mGlue.mExternalOnItemViewClickedListener != null) {
-                            mGlue.mExternalOnItemViewClickedListener.onItemClicked(itemViewHolder,
+                        if (mGlue.getOnItemViewClickedListener() != null) {
+                            mGlue.getOnItemViewClickedListener().onItemClicked(itemViewHolder,
                                     item, rowViewHolder, row);
                         }
                     }
                 }
             });
         }
+
+        @Override
+        public void setHostCallback(HostCallback callback) {
+            mFragment.setHostCallback(callback);
+        }
+
+        @Override
+        public void fadeOut() {
+            mFragment.fadeOut();
+        }
     }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlSupportGlue.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlSupportGlue.java
index ec3cc1e..8c1884d 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlSupportGlue.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackControlSupportGlue.java
@@ -3,6 +3,7 @@
 package android.support.v17.leanback.app;
 
 import android.content.Context;
+import android.support.v17.leanback.media.PlaybackGlueHost;
 import android.support.v17.leanback.widget.Action;
 import android.support.v17.leanback.widget.OnActionClickedListener;
 import android.support.v17.leanback.widget.OnItemViewClickedListener;
@@ -14,29 +15,26 @@
 import android.view.View;
 
 /**
- * @deprecated Use {@link PlaybackControlGlue} and {@link PlaybackSupportFragmentGlueHost} for
- * {@link PlaybackSupportFragment}.
+ * @deprecated Use {@link android.support.v17.leanback.media.PlaybackControlGlue} and
+ * {@link PlaybackSupportFragmentGlueHost} for {@link PlaybackSupportFragment}.
  */
 @Deprecated
 public abstract class PlaybackControlSupportGlue extends PlaybackControlGlue {
 
     public PlaybackControlSupportGlue(Context context, int[] seekSpeeds) {
-        super(context, seekSpeeds);
+        this(context, null, seekSpeeds, seekSpeeds);
     }
 
     public PlaybackControlSupportGlue(
             Context context, int[] fastForwardSpeeds, int[] rewindSpeeds) {
-        super(context, fastForwardSpeeds, rewindSpeeds);
+        this(context, null, fastForwardSpeeds, rewindSpeeds);
     }
 
     public PlaybackControlSupportGlue(
             Context context,
             PlaybackOverlaySupportFragment fragment,
             int[] seekSpeeds) {
-        super(context,
-                fragment == null ? null: new PlaybackSupportGlueHostOld(fragment),
-                seekSpeeds,
-                seekSpeeds);
+        this(context, fragment, seekSpeeds, seekSpeeds);
     }
 
     public PlaybackControlSupportGlue(
@@ -44,15 +42,13 @@
             PlaybackOverlaySupportFragment fragment,
             int[] fastForwardSpeeds,
             int[] rewindSpeeds) {
-        super(context,
-                fragment == null ? null: new PlaybackSupportGlueHostOld(fragment),
-                fastForwardSpeeds,
-                rewindSpeeds);
+        super(context, fastForwardSpeeds, rewindSpeeds);
+        setHost(fragment == null ? null : new PlaybackSupportGlueHostOld(fragment));
     }
 
     @Override
-    public void setHost(PlaybackGlueHost host) {
-        super.setHost(host);
+    protected void onAttachedToHost(PlaybackGlueHost host) {
+        super.onAttachedToHost(host);
         if (host instanceof PlaybackSupportGlueHostOld) {
             ((PlaybackSupportGlueHostOld) host).mGlue = this;
         }
@@ -101,5 +97,15 @@
                 }
             });
         }
+
+        @Override
+        public void setHostCallback(HostCallback callback) {
+            mFragment.setHostCallback(callback);
+        }
+
+        @Override
+        public void fadeOut() {
+            mFragment.fadeOut();
+        }
     }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragment.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragment.java
index b97930a..5e12431 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragment.java
@@ -28,6 +28,7 @@
 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.ClassPresenterSelector;
@@ -74,7 +75,7 @@
      * A dark translucent background.
      */
     public static final int BG_DARK = 1;
-    private PlaybackGlue.HostLifecycleCallback mHostLifecycleCallback;
+    private PlaybackGlueHost.HostCallback mHostCallback;
 
     /**
      * Resets the focus on the button in the middle of control row.
@@ -409,6 +410,9 @@
         }
         getVerticalGridView().setOnTouchInterceptListener(mOnTouchInterceptListener);
         getVerticalGridView().setOnKeyInterceptListener(mOnKeyInterceptListener);
+        if (mHostCallback != null) {
+            mHostCallback.onHostResume();
+        }
     }
 
     private void startFadeTimer() {
@@ -751,11 +755,11 @@
     }
 
     /**
-     * Sets the {@link PlaybackGlue.HostLifecycleCallback}. Implementor of this interface will
+     * 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 setHostLifecycleCallback(PlaybackGlue.HostLifecycleCallback hostLifecycleCallback) {
-        this.mHostLifecycleCallback = hostLifecycleCallback;
+    public void setHostCallback(PlaybackGlueHost.HostCallback hostCallback) {
+        this.mHostCallback = hostCallback;
     }
 
     @Override
@@ -763,17 +767,25 @@
         super.onStart();
         setupChildFragmentLayout();
         mRowsFragment.setAdapter(mAdapter);
-        if (mHostLifecycleCallback != null) {
-            mHostLifecycleCallback.onHostStart();
+        if (mHostCallback != null) {
+            mHostCallback.onHostStart();
         }
     }
 
     @Override
     public void onStop() {
-        super.onStop();
-        if (mHostLifecycleCallback != null) {
-            mHostLifecycleCallback.onHostStop();
+        if (mHostCallback != null) {
+            mHostCallback.onHostStop();
         }
+        super.onStop();
+    }
+
+    @Override
+    public void onPause() {
+        if (mHostCallback != null) {
+            mHostCallback.onHostPause();
+        }
+        super.onPause();
     }
 
     /**
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragmentGlueHost.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragmentGlueHost.java
index fc384a5..d0acced 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragmentGlueHost.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragmentGlueHost.java
@@ -13,6 +13,7 @@
  */
 package android.support.v17.leanback.app;
 
+import android.support.v17.leanback.media.PlaybackGlueHost;
 import android.support.v17.leanback.widget.Action;
 import android.support.v17.leanback.widget.OnActionClickedListener;
 import android.support.v17.leanback.widget.OnItemViewClickedListener;
@@ -23,10 +24,10 @@
 import android.view.View;
 
 /**
- * {@link PlaybackGlue.PlaybackGlueHost} implementation
+ * {@link PlaybackGlueHost} implementation
  * the interaction between this class and {@link PlaybackFragment}.
  */
-public class PlaybackFragmentGlueHost extends PlaybackGlue.PlaybackGlueHost {
+public class PlaybackFragmentGlueHost extends PlaybackGlueHost {
     private final PlaybackFragment mFragment;
 
     public PlaybackFragmentGlueHost(PlaybackFragment fragment) {
@@ -57,8 +58,8 @@
     }
 
     @Override
-    public void setHostLifeCycleCallback(PlaybackGlue.HostLifecycleCallback callback) {
-        mFragment.setHostLifecycleCallback(callback);
+    public void setHostCallback(HostCallback callback) {
+        mFragment.setHostCallback(callback);
     }
 
     @Override
@@ -75,4 +76,9 @@
     public void setPlaybackRow(Row row) {
         mFragment.setPlaybackRow(row);
     }
+
+    @Override
+    public void fadeOut() {
+        mFragment.fadeOut();
+    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackGlue.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackGlue.java
deleted file mode 100644
index 2243186..0000000
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackGlue.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * 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.content.Context;
-import android.support.v17.leanback.widget.OnActionClickedListener;
-import android.support.v17.leanback.widget.PlaybackRowPresenter;
-import android.support.v17.leanback.widget.Row;
-import android.view.View;
-
-/**
- * Base class for {@link PlaybackControlGlue}.
- */
-public abstract class PlaybackGlue {
-    private final Context mContext;
-    private PlaybackGlueHost mPlaybackGlueHost;
-
-    /**
-     * Returns true when the media player is ready to start media playback. Subclasses must
-     * implement this method correctly.
-     */
-    public boolean isReadyForPlayback() { return true; }
-
-    /**
-     * Interface to allow clients to take action once the video is ready to play.
-     */
-    public static abstract class PlayerCallback {
-        /**
-         * This method is fired when the video is ready for playback.
-         */
-        public abstract void onReadyForPlayback();
-    }
-
-    /**
-     * Lifecycle callbacks triggered by the host(fragment e.g.) hosting the video controls/surface.
-     */
-    public static abstract class HostLifecycleCallback {
-        /**
-         * Callback triggered once the host(fragment) has started.
-         */
-        public abstract void onHostStart();
-
-        /**
-         * Callback triggered once the host(fragment) has finished.
-         */
-        public abstract void onHostStop();
-    }
-
-    /**
-     * Sets the {@link PlayerCallback} callback.
-     */
-    public void setPlayerCallback(MediaPlayerGlue.PlayerCallback mPlayerCallback) {}
-
-    /**
-     * Starts the media player.
-     */
-    public void play() {}
-
-    /**
-     * Pauses the media player.
-     */
-    public void pause() {}
-
-    /**
-     * Goes to the next media item.
-     */
-    public void next() {}
-
-    /**
-     * Goes to the previous media item.
-     */
-    public void previous() {}
-
-    /**
-     * This class represents the UI {@link PlaybackFragment} hosting playback controls and
-     * defines the interaction between {@link PlaybackGlue} and the host.
-     */
-    public static class PlaybackGlueHost {
-        PlaybackGlue mGlue;
-
-        /**
-         * 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.
-         */
-        public void setFadingEnabled(boolean enable) {
-        }
-
-        /**
-         * Sets the {@link android.view.View.OnKeyListener} on the host. This would trigger
-         * the listener when a {@link android.view.KeyEvent} is unhandled by the host.
-         */
-        public void setOnKeyInterceptListener(View.OnKeyListener onKeyListener) {
-        }
-
-        /**
-         * Sets the {@link android.view.View.OnClickListener} on this fragment.
-         */
-        public void setOnActionClickedListener(OnActionClickedListener listener) {}
-
-        /**
-         * Sets the host {@link HostLifecycleCallback} callback on the host.
-         */
-        public void setHostLifeCycleCallback(HostLifecycleCallback callback) {
-        }
-
-        /**
-         * Notifies host about a change so it can update the view.
-         */
-        public void notifyPlaybackRowChanged() {}
-
-        /**
-         * Sets {@link PlaybackRowPresenter} for rendering the playback controls.
-         */
-        public void setPlaybackRowPresenter(PlaybackRowPresenter presenter) {}
-
-        /**
-         * Sets the {@link Row} that represents the information on control items that needs
-         * to be rendered.
-         */
-        public void setPlaybackRow(Row row) {}
-
-        final void attachToGlue(PlaybackGlue glue) {
-            if (mGlue != null) {
-                mGlue.onDetachedFromHost();
-            }
-            mGlue = glue;
-        }
-    }
-
-    /**
-     * Constructor.
-     */
-    public PlaybackGlue(Context context) {
-        this.mContext = context;
-    }
-
-    /**
-     * Returns the context.
-     */
-    public Context getContext() {
-        return mContext;
-    }
-
-    /**
-     * This method is used to configure the {@link PlaybackGlueHost} with required listeners
-     * and presenters.
-     */
-    public void setHost(PlaybackGlueHost host) {
-        mPlaybackGlueHost = host;
-        if (host != null) {
-            host.attachToGlue(this);
-        }
-    }
-
-    /**
-     * This method is called when we try to attach a new host by calling
-     * {@link #setHost(PlaybackGlueHost)}.
-     */
-    public void onDetachedFromHost() {
-        mPlaybackGlueHost = null;
-    }
-
-    /**
-     * @return Associated {@link PlaybackGlueHost} or null if not attached to host.
-     */
-    public PlaybackGlueHost getHost() {
-        return mPlaybackGlueHost;
-    }
-}
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java
index 32b9c92..33e35eb 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java
@@ -27,6 +27,7 @@
 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.ItemBridgeAdapter;
 import android.support.v17.leanback.widget.ObjectAdapter;
 import android.support.v17.leanback.widget.ObjectAdapter.DataObserver;
@@ -123,6 +124,7 @@
     private ValueAnimator mDescriptionFadeInAnimator, mDescriptionFadeOutAnimator;
     private ValueAnimator mOtherRowFadeInAnimator, mOtherRowFadeOutAnimator;
     boolean mResetControlsToPrimaryActionsPending;
+    PlaybackGlueHost.HostCallback mHostCallback;
 
     private final Animator.AnimatorListener mFadeListener =
             new Animator.AnimatorListener() {
@@ -323,6 +325,30 @@
         fade(false);
     }
 
+    /**
+     * Sets the {@link PlaybackGlueHost.HostCallback}. Implementor of this interface will
+     * take appropriate actions to take action when the hosting fragment starts/stops processing.
+     */
+    void setHostCallback(PlaybackGlueHost.HostCallback hostCallback) {
+        this.mHostCallback = hostCallback;
+    }
+
+    @Override
+    public void onStop() {
+        if (mHostCallback != null) {
+            mHostCallback.onHostStop();
+        }
+        super.onStop();
+    }
+
+    @Override
+    public void onPause() {
+        if (mHostCallback != null) {
+            mHostCallback.onHostPause();
+        }
+        super.onPause();
+    }
+
     private boolean areControlsHidden() {
         return mFadingStatus == IDLE && mBgAlpha == 0;
     }
@@ -382,6 +408,9 @@
         }
         getVerticalGridView().setOnTouchInterceptListener(mOnTouchInterceptListener);
         getVerticalGridView().setOnKeyInterceptListener(mOnKeyInterceptListener);
+        if (mHostCallback != null) {
+            mHostCallback.onHostResume();
+        }
     }
 
     void startFadeTimer() {
@@ -765,6 +794,9 @@
         super.onStart();
         // Workaround problem VideoView forcing itself to focused, let controls take focus.
         getRowsFragment().getView().requestFocus();
+        if (mHostCallback != null) {
+            mHostCallback.onHostStart();
+        }
     }
 
     private final DataObserver mObserver = new DataObserver() {
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java
index c1d1452..d41d65f 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java
@@ -30,6 +30,7 @@
 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.ItemBridgeAdapter;
 import android.support.v17.leanback.widget.ObjectAdapter;
 import android.support.v17.leanback.widget.ObjectAdapter.DataObserver;
@@ -126,6 +127,7 @@
     private ValueAnimator mDescriptionFadeInAnimator, mDescriptionFadeOutAnimator;
     private ValueAnimator mOtherRowFadeInAnimator, mOtherRowFadeOutAnimator;
     boolean mResetControlsToPrimaryActionsPending;
+    PlaybackGlueHost.HostCallback mHostCallback;
 
     private final Animator.AnimatorListener mFadeListener =
             new Animator.AnimatorListener() {
@@ -326,6 +328,30 @@
         fade(false);
     }
 
+    /**
+     * Sets the {@link PlaybackGlueHost.HostCallback}. Implementor of this interface will
+     * take appropriate actions to take action when the hosting fragment starts/stops processing.
+     */
+    void setHostCallback(PlaybackGlueHost.HostCallback hostCallback) {
+        this.mHostCallback = hostCallback;
+    }
+
+    @Override
+    public void onStop() {
+        if (mHostCallback != null) {
+            mHostCallback.onHostStop();
+        }
+        super.onStop();
+    }
+
+    @Override
+    public void onPause() {
+        if (mHostCallback != null) {
+            mHostCallback.onHostPause();
+        }
+        super.onPause();
+    }
+
     private boolean areControlsHidden() {
         return mFadingStatus == IDLE && mBgAlpha == 0;
     }
@@ -385,6 +411,9 @@
         }
         getVerticalGridView().setOnTouchInterceptListener(mOnTouchInterceptListener);
         getVerticalGridView().setOnKeyInterceptListener(mOnKeyInterceptListener);
+        if (mHostCallback != null) {
+            mHostCallback.onHostResume();
+        }
     }
 
     void startFadeTimer() {
@@ -768,6 +797,9 @@
         super.onStart();
         // Workaround problem VideoView forcing itself to focused, let controls take focus.
         getRowsSupportFragment().getView().requestFocus();
+        if (mHostCallback != null) {
+            mHostCallback.onHostStart();
+        }
     }
 
     private final DataObserver mObserver = new DataObserver() {
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragment.java
index f01313d..3e9f70c 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragment.java
@@ -31,6 +31,7 @@
 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.ClassPresenterSelector;
@@ -77,7 +78,7 @@
      * A dark translucent background.
      */
     public static final int BG_DARK = 1;
-    private PlaybackGlue.HostLifecycleCallback mHostLifecycleCallback;
+    private PlaybackGlueHost.HostCallback mHostCallback;
 
     /**
      * Resets the focus on the button in the middle of control row.
@@ -412,6 +413,9 @@
         }
         getVerticalGridView().setOnTouchInterceptListener(mOnTouchInterceptListener);
         getVerticalGridView().setOnKeyInterceptListener(mOnKeyInterceptListener);
+        if (mHostCallback != null) {
+            mHostCallback.onHostResume();
+        }
     }
 
     private void startFadeTimer() {
@@ -754,11 +758,11 @@
     }
 
     /**
-     * Sets the {@link PlaybackGlue.HostLifecycleCallback}. Implementor of this interface will
+     * 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 setHostLifecycleCallback(PlaybackGlue.HostLifecycleCallback hostLifecycleCallback) {
-        this.mHostLifecycleCallback = hostLifecycleCallback;
+    public void setHostCallback(PlaybackGlueHost.HostCallback hostCallback) {
+        this.mHostCallback = hostCallback;
     }
 
     @Override
@@ -766,17 +770,25 @@
         super.onStart();
         setupChildFragmentLayout();
         mRowsSupportFragment.setAdapter(mAdapter);
-        if (mHostLifecycleCallback != null) {
-            mHostLifecycleCallback.onHostStart();
+        if (mHostCallback != null) {
+            mHostCallback.onHostStart();
         }
     }
 
     @Override
     public void onStop() {
-        super.onStop();
-        if (mHostLifecycleCallback != null) {
-            mHostLifecycleCallback.onHostStop();
+        if (mHostCallback != null) {
+            mHostCallback.onHostStop();
         }
+        super.onStop();
+    }
+
+    @Override
+    public void onPause() {
+        if (mHostCallback != null) {
+            mHostCallback.onHostPause();
+        }
+        super.onPause();
     }
 
     /**
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragmentGlueHost.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragmentGlueHost.java
index 3227747..145bcbd 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragmentGlueHost.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragmentGlueHost.java
@@ -16,6 +16,7 @@
  */
 package android.support.v17.leanback.app;
 
+import android.support.v17.leanback.media.PlaybackGlueHost;
 import android.support.v17.leanback.widget.Action;
 import android.support.v17.leanback.widget.OnActionClickedListener;
 import android.support.v17.leanback.widget.OnItemViewClickedListener;
@@ -26,10 +27,10 @@
 import android.view.View;
 
 /**
- * {@link PlaybackGlue.PlaybackGlueHost} implementation
+ * {@link PlaybackGlueHost} implementation
  * the interaction between this class and {@link PlaybackSupportFragment}.
  */
-public class PlaybackSupportFragmentGlueHost extends PlaybackGlue.PlaybackGlueHost {
+public class PlaybackSupportFragmentGlueHost extends PlaybackGlueHost {
     private final PlaybackSupportFragment mFragment;
 
     public PlaybackSupportFragmentGlueHost(PlaybackSupportFragment fragment) {
@@ -60,8 +61,8 @@
     }
 
     @Override
-    public void setHostLifeCycleCallback(PlaybackGlue.HostLifecycleCallback callback) {
-        mFragment.setHostLifecycleCallback(callback);
+    public void setHostCallback(HostCallback callback) {
+        mFragment.setHostCallback(callback);
     }
 
     @Override
@@ -78,4 +79,9 @@
     public void setPlaybackRow(Row row) {
         mFragment.setPlaybackRow(row);
     }
+
+    @Override
+    public void fadeOut() {
+        mFragment.fadeOut();
+    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/SurfaceHolderGlueHost.java b/v17/leanback/src/android/support/v17/leanback/app/SurfaceHolderGlueHost.java
deleted file mode 100644
index 64498f6..0000000
--- a/v17/leanback/src/android/support/v17/leanback/app/SurfaceHolderGlueHost.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2015 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.view.SurfaceHolder;
-
-/**
- * Interface to be implemented by any host that contains a {@link android.view.SurfaceView}. This
- * will allow MediaPlayerGlue to setup the surface holder callback on the host.
- */
-public interface SurfaceHolderGlueHost {
-    /**
-     * Sets the {@link SurfaceHolder.Callback} on the the host.
-     */
-    void setSurfaceHolderCallback(SurfaceHolder.Callback callback);
-}
diff --git a/v17/leanback/src/android/support/v17/leanback/app/VideoFragmentGlueHost.java b/v17/leanback/src/android/support/v17/leanback/app/VideoFragmentGlueHost.java
index 0db2a38..0b4e34b 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/VideoFragmentGlueHost.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/VideoFragmentGlueHost.java
@@ -13,10 +13,12 @@
  */
 package android.support.v17.leanback.app;
 
+import android.support.v17.leanback.media.PlaybackGlueHost;
+import android.support.v17.leanback.media.SurfaceHolderGlueHost;
 import android.view.SurfaceHolder;
 
 /**
- * {@link PlaybackGlue.PlaybackGlueHost} implementation
+ * {@link PlaybackGlueHost} implementation
  * the interaction between this class and {@link VideoFragment}.
  * @hide
  */
@@ -31,7 +33,7 @@
 
     /**
      * Sets the {@link android.view.SurfaceHolder.Callback} on the host.
-     * {@link PlaybackGlue.PlaybackGlueHost} is assumed to either host the {@link SurfaceHolder} or
+     * {@link PlaybackGlueHost} is assumed to either host the {@link SurfaceHolder} or
      * have a reference to the component hosting it for rendering the video.
      */
     @Override
diff --git a/v17/leanback/src/android/support/v17/leanback/app/VideoSupportFragmentGlueHost.java b/v17/leanback/src/android/support/v17/leanback/app/VideoSupportFragmentGlueHost.java
index eb9d9d4..83769ab 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/VideoSupportFragmentGlueHost.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/VideoSupportFragmentGlueHost.java
@@ -16,10 +16,12 @@
  */
 package android.support.v17.leanback.app;
 
+import android.support.v17.leanback.media.PlaybackGlueHost;
+import android.support.v17.leanback.media.SurfaceHolderGlueHost;
 import android.view.SurfaceHolder;
 
 /**
- * {@link PlaybackGlue.PlaybackGlueHost} implementation
+ * {@link PlaybackGlueHost} implementation
  * the interaction between this class and {@link VideoSupportFragment}.
  * @hide
  */
@@ -34,7 +36,7 @@
 
     /**
      * Sets the {@link android.view.SurfaceHolder.Callback} on the host.
-     * {@link PlaybackGlue.PlaybackGlueHost} is assumed to either host the {@link SurfaceHolder} or
+     * {@link PlaybackGlueHost} is assumed to either host the {@link SurfaceHolder} or
      * have a reference to the component hosting it for rendering the video.
      */
     @Override
diff --git a/v17/leanback/src/android/support/v17/leanback/media/MediaControllerGlue.java b/v17/leanback/src/android/support/v17/leanback/media/MediaControllerGlue.java
new file mode 100644
index 0000000..730bf3a
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/media/MediaControllerGlue.java
@@ -0,0 +1,221 @@
+/*
+ * 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.media;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.v4.media.MediaMetadataCompat;
+import android.support.v4.media.session.MediaControllerCompat;
+import android.support.v4.media.session.PlaybackStateCompat;
+import android.util.Log;
+
+/**
+ * A helper class for implementing a glue layer for {@link MediaControllerCompat}.
+ */
+public abstract class MediaControllerGlue extends PlaybackControlGlue {
+    static final String TAG = "MediaControllerGlue";
+    static final boolean DEBUG = false;
+
+    MediaControllerCompat mMediaController;
+
+    private final MediaControllerCompat.Callback mCallback = new MediaControllerCompat.Callback() {
+        @Override
+        public void onMetadataChanged(MediaMetadataCompat metadata) {
+            if (DEBUG) Log.v(TAG, "onMetadataChanged");
+            MediaControllerGlue.this.onMetadataChanged();
+        }
+        @Override
+        public void onPlaybackStateChanged(PlaybackStateCompat state) {
+            if (DEBUG) Log.v(TAG, "onPlaybackStateChanged");
+            onStateChanged();
+        }
+        @Override
+        public void onSessionDestroyed() {
+            if (DEBUG) Log.v(TAG, "onSessionDestroyed");
+            mMediaController = null;
+        }
+        @Override
+        public void onSessionEvent(String event, Bundle extras) {
+            if (DEBUG) Log.v(TAG, "onSessionEvent");
+        }
+    };
+
+    /**
+     * Constructor for the glue.
+     *
+     * @param context
+     * @param fastForwardSpeeds Array of seek speeds for fast forward.
+     * @param rewindSpeeds Array of seek speeds for rewind.
+     */
+    public MediaControllerGlue(Context context,
+                               int[] fastForwardSpeeds,
+                               int[] rewindSpeeds) {
+        super(context, fastForwardSpeeds, rewindSpeeds);
+    }
+
+    /**
+     * Attaches to the given media controller.
+     */
+    public void attachToMediaController(MediaControllerCompat mediaController) {
+        if (mediaController != mMediaController) {
+            if (DEBUG) Log.v(TAG, "New media controller " + mediaController);
+            detach();
+            mMediaController = mediaController;
+            if (mMediaController != null) {
+                mMediaController.registerCallback(mCallback);
+            }
+            onMetadataChanged();
+            onStateChanged();
+        }
+    }
+
+    /**
+     * Detaches from the media controller.  Must be called when the object is no longer
+     * needed.
+     */
+    public void detach() {
+        if (mMediaController != null) {
+            mMediaController.unregisterCallback(mCallback);
+        }
+        mMediaController = null;
+    }
+
+    /**
+     * Returns the media controller currently attached.
+     */
+    public final MediaControllerCompat getMediaController() {
+        return mMediaController;
+    }
+
+    @Override
+    public boolean hasValidMedia() {
+        return mMediaController != null && mMediaController.getMetadata() != null;
+    }
+
+    @Override
+    public boolean isMediaPlaying() {
+        return mMediaController.getPlaybackState().getState() == PlaybackStateCompat.STATE_PLAYING;
+    }
+
+    @Override
+    public int getCurrentSpeedId() {
+        int speed = (int) mMediaController.getPlaybackState().getPlaybackSpeed();
+        if (speed == 0) {
+            return PLAYBACK_SPEED_PAUSED;
+        } else if (speed == 1) {
+            return PLAYBACK_SPEED_NORMAL;
+        } else if (speed > 0) {
+            int[] seekSpeeds = getFastForwardSpeeds();
+            for (int index = 0; index < seekSpeeds.length; index++) {
+                if (speed == seekSpeeds[index]) {
+                    return PLAYBACK_SPEED_FAST_L0 + index;
+                }
+            }
+        } else {
+            int[] seekSpeeds = getRewindSpeeds();
+            for (int index = 0; index < seekSpeeds.length; index++) {
+                if (-speed == seekSpeeds[index]) {
+                    return -PLAYBACK_SPEED_FAST_L0 - index;
+                }
+            }
+        }
+        Log.w(TAG, "Couldn't find index for speed " + speed);
+        return PLAYBACK_SPEED_INVALID;
+    }
+
+    @Override
+    public CharSequence getMediaTitle() {
+        return mMediaController.getMetadata().getDescription().getTitle();
+    }
+
+    @Override
+    public CharSequence getMediaSubtitle() {
+        return mMediaController.getMetadata().getDescription().getSubtitle();
+    }
+
+    @Override
+    public int getMediaDuration() {
+        return (int) mMediaController.getMetadata().getLong(
+                MediaMetadataCompat.METADATA_KEY_DURATION);
+    }
+
+    @Override
+    public int getCurrentPosition() {
+        return (int) mMediaController.getPlaybackState().getPosition();
+    }
+
+    @Override
+    public Drawable getMediaArt() {
+        Bitmap bitmap = mMediaController.getMetadata().getDescription().getIconBitmap();
+        return bitmap == null ? null : new BitmapDrawable(getContext().getResources(), bitmap);
+    }
+
+    @Override
+    public long getSupportedActions() {
+        long result = 0;
+        long actions = mMediaController.getPlaybackState().getActions();
+        if ((actions & PlaybackStateCompat.ACTION_PLAY_PAUSE) != 0) {
+            result |= ACTION_PLAY_PAUSE;
+        }
+        if ((actions & PlaybackStateCompat.ACTION_SKIP_TO_NEXT) != 0) {
+            result |= ACTION_SKIP_TO_NEXT;
+        }
+        if ((actions & PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) != 0) {
+            result |= ACTION_SKIP_TO_PREVIOUS;
+        }
+        if ((actions & PlaybackStateCompat.ACTION_FAST_FORWARD) != 0) {
+            result |= ACTION_FAST_FORWARD;
+        }
+        if ((actions & PlaybackStateCompat.ACTION_REWIND) != 0) {
+            result |= ACTION_REWIND;
+        }
+        return result;
+    }
+
+    @Override
+    public void play(int speed) {
+        if (DEBUG) Log.v(TAG, "startPlayback speed " + speed);
+        if (speed == PLAYBACK_SPEED_NORMAL) {
+            mMediaController.getTransportControls().play();
+        } else if (speed > 0) {
+            mMediaController.getTransportControls().fastForward();
+        } else {
+            mMediaController.getTransportControls().rewind();
+        }
+    }
+
+    @Override
+    public void pause() {
+        if (DEBUG) Log.v(TAG, "pausePlayback");
+        mMediaController.getTransportControls().pause();
+    }
+
+    @Override
+    public void next() {
+        if (DEBUG) Log.v(TAG, "skipToNext");
+        mMediaController.getTransportControls().skipToNext();
+    }
+
+    @Override
+    public void previous() {
+        if (DEBUG) Log.v(TAG, "skipToPrevious");
+        mMediaController.getTransportControls().skipToPrevious();
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/app/MediaPlayerGlue.java b/v17/leanback/src/android/support/v17/leanback/media/MediaPlayerGlue.java
similarity index 80%
rename from v17/leanback/src/android/support/v17/leanback/app/MediaPlayerGlue.java
rename to v17/leanback/src/android/support/v17/leanback/media/MediaPlayerGlue.java
index e06316c..abee2dc 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/MediaPlayerGlue.java
+++ b/v17/leanback/src/android/support/v17/leanback/media/MediaPlayerGlue.java
@@ -1,19 +1,20 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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
+ * 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
+ *      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.
- *
+ * 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;
+package android.support.v17.leanback.media;
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
@@ -23,10 +24,8 @@
 import android.os.Handler;
 import android.support.v17.leanback.widget.Action;
 import android.support.v17.leanback.widget.ArrayObjectAdapter;
-import android.support.v17.leanback.widget.ControlButtonPresenterSelector;
 import android.support.v17.leanback.widget.OnItemViewSelectedListener;
 import android.support.v17.leanback.widget.PlaybackControlsRow;
-import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.Row;
 import android.support.v17.leanback.widget.RowPresenter;
@@ -38,8 +37,8 @@
 import java.io.IOException;
 
 /**
- * This glue extends the {@link PlaybackControlGlue} with a {@link MediaPlayer} synchronization. It
- * supports 7 actions:
+ * This glue extends the {@link android.support.v17.leanback.media.PlaybackControlGlue} with a
+ * {@link MediaPlayer} synchronization. It supports 7 actions:
  *
  * <ul>
  * <li>{@link android.support.v17.leanback.widget.PlaybackControlsRow.FastForwardAction}</li>
@@ -64,10 +63,8 @@
     private static final String TAG = "MediaPlayerGlue";
     protected final PlaybackControlsRow.ThumbsDownAction mThumbsDownAction;
     protected final PlaybackControlsRow.ThumbsUpAction mThumbsUpAction;
-    private final Context mContext;
     MediaPlayer mPlayer = new MediaPlayer();
     private final PlaybackControlsRow.RepeatAction mRepeatAction;
-    private PlaybackControlsRow mControlsRow;
     private Runnable mRunnable;
     private Handler mHandler = new Handler();
     private boolean mInitialized = false; // true when the MediaPlayer is prepared/initialized
@@ -113,32 +110,31 @@
     /**
      * Constructor.
      */
-    public MediaPlayerGlue(Context context, PlaybackGlueHost host) {
-        this(context, host, new int[]{1}, new int[]{1});
+    public MediaPlayerGlue(Context context) {
+        this(context, new int[]{1}, new int[]{1});
     }
 
     /**
      * Constructor.
      */
     public MediaPlayerGlue(
-            Context context, PlaybackGlueHost host, int[] fastForwardSpeeds, int[] rewindSpeeds) {
-        super(context, host, fastForwardSpeeds, rewindSpeeds);
-        mContext = context;
+            Context context, int[] fastForwardSpeeds, int[] rewindSpeeds) {
+        super(context, fastForwardSpeeds, rewindSpeeds);
 
         // Instantiate secondary actions
-        mRepeatAction = new PlaybackControlsRow.RepeatAction(mContext);
-        mThumbsDownAction = new PlaybackControlsRow.ThumbsDownAction(mContext);
-        mThumbsUpAction = new PlaybackControlsRow.ThumbsUpAction(mContext);
+        mRepeatAction = new PlaybackControlsRow.RepeatAction(getContext());
+        mThumbsDownAction = new PlaybackControlsRow.ThumbsDownAction(getContext());
+        mThumbsUpAction = new PlaybackControlsRow.ThumbsUpAction(getContext());
         mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
         mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsAction.OUTLINE);
     }
 
     @Override
-    public void setHost(PlaybackGlueHost host) {
-        super.setHost(host);
-        if (getHost() instanceof SurfaceHolderGlueHost) {
-            ((SurfaceHolderGlueHost) getHost()).setSurfaceHolderCallback(
-                    new VideoFragmentSurfaceHolderCallback());
+    protected void onAttachedToHost(PlaybackGlueHost host) {
+        super.onAttachedToHost(host);
+        if (host instanceof SurfaceHolderGlueHost) {
+            ((SurfaceHolderGlueHost) host).setSurfaceHolderCallback(
+                    new VideoPlayerSurfaceHolderCallback());
         }
     }
 
@@ -159,23 +155,22 @@
         mPlayer.reset();
     }
 
+    /**
+     * Release internal MediaPlayer. Should not use the object after call release().
+     */
     public void release() {
         mPlayer.release();
     }
 
     @Override
-    public void onDetachedFromHost() {
+    protected void onDetachedFromHost() {
         super.onDetachedFromHost();
         reset();
         release();
     }
 
-    /**
-     * Override this method in case you need to add different secondary actions.
-     *
-     * @param secondaryActionsAdapter The adapter you need to add the {@link Action}s to.
-     */
-    protected void addSecondaryActions(ArrayObjectAdapter secondaryActionsAdapter) {
+    @Override
+    protected void onCreateSecondaryActions(ArrayObjectAdapter secondaryActionsAdapter) {
         secondaryActionsAdapter.add(mRepeatAction);
         secondaryActionsAdapter.add(mThumbsDownAction);
         secondaryActionsAdapter.add(mThumbsUpAction);
@@ -188,22 +183,6 @@
         mPlayer.setDisplay(surfaceHolder);
     }
 
-    /**
-     * Creates a presenter ({@link PlaybackControlsRowPresenter}) for rendering playback controls.
-     * @return
-     */
-    public PlaybackControlsRowPresenter createControlsRowAndPresenter() {
-        PlaybackControlsRowPresenter presenter = super.createControlsRowAndPresenter();
-        mControlsRow = getControlsRow();
-
-        // Add secondary actions and change the control row color.
-        ArrayObjectAdapter secondaryActions = new ArrayObjectAdapter(
-                new ControlButtonPresenterSelector());
-        mControlsRow.setSecondaryActionsAdapter(secondaryActions);
-        addSecondaryActions(secondaryActions);
-        return presenter;
-    }
-
     @Override
     public void enableProgressUpdating(final boolean enabled) {
         if (!enabled) {
@@ -325,27 +304,7 @@
     }
 
     @Override
-    protected void startPlayback(int speed) throws IllegalStateException {
-        play();
-    }
-
-    @Override
-    protected void pausePlayback() {
-        pause();
-    }
-
-    @Override
-    protected void skipToNext() {
-        // Not supported.
-    }
-
-    @Override
-    protected void skipToPrevious() {
-        // Not supported.
-    }
-
-    @Override
-    public void play() {
+    public void play(int speed) {
         mPlayer.start();
         onMetadataChanged();
         onStateChanged();
@@ -477,7 +436,7 @@
         mPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
             @Override
             public void onBufferingUpdate(MediaPlayer mp, int percent) {
-                mControlsRow.setBufferedProgress((int) (mp.getDuration() * (percent / 100f)));
+                getControlsRow().setBufferedProgress((int) (mp.getDuration() * (percent / 100f)));
             }
         });
         mPlayer.prepareAsync();
@@ -485,23 +444,12 @@
     }
 
     /**
-     * Call to <code>startPlayback(1)</code>.
-     *
-     * @throws IllegalStateException See {@link MediaPlayer} for further information about it's
-     *                               different states when setting a data source and preparing it
-     *                               to be played.
-     */
-    public void startPlayback() throws IllegalStateException {
-        startPlayback(1);
-    }
-
-    /**
-     * This is a listener implementation for the {@link OnItemViewSelectedListener} of the {@link
-     * PlaybackFragment}. This implementation is required in order to detect KEY_DOWN events
+     * This is a listener implementation for the {@link OnItemViewSelectedListener}.
+     * This implementation is required in order to detect KEY_DOWN events
      * on the {@link android.support.v17.leanback.widget.PlaybackControlsRow.FastForwardAction} and
      * {@link android.support.v17.leanback.widget.PlaybackControlsRow.RewindAction}. Thus you
      * should <u>NOT</u> set another {@link OnItemViewSelectedListener} on your
-     * {@link PlaybackFragment}. Instead, override this method and call its super (this)
+     * Fragment. Instead, override this method and call its super (this)
      * implementation.
      *
      * @see OnItemViewSelectedListener#onItemSelected(
@@ -523,10 +471,10 @@
     }
 
     /**
-     * Implements {@link SurfaceHolder.Callback} that can then be set on the \
-     * {@link android.support.v17.leanback.app.PlaybackGlue.PlaybackGlueHost}
+     * Implements {@link SurfaceHolder.Callback} that can then be set on the
+     * {@link PlaybackGlueHost}.
      */
-    private class VideoFragmentSurfaceHolderCallback implements SurfaceHolder.Callback {
+    class VideoPlayerSurfaceHolderCallback implements SurfaceHolder.Callback {
         private boolean mMediaPlayerReset = true;
 
         @Override
diff --git a/v17/leanback/src/android/support/v17/leanback/media/PlaybackControlGlue.java b/v17/leanback/src/android/support/v17/leanback/media/PlaybackControlGlue.java
new file mode 100644
index 0000000..6402fc6
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/media/PlaybackControlGlue.java
@@ -0,0 +1,820 @@
+/*
+ * 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.media;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter;
+import android.support.v17.leanback.widget.Action;
+import android.support.v17.leanback.widget.ArrayObjectAdapter;
+import android.support.v17.leanback.widget.ControlButtonPresenterSelector;
+import android.support.v17.leanback.widget.OnActionClickedListener;
+import android.support.v17.leanback.widget.PlaybackControlsRow;
+import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
+import android.support.v17.leanback.widget.PresenterSelector;
+import android.support.v17.leanback.widget.RowPresenter;
+import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * A helper class for managing a {@link PlaybackControlsRow}
+ * and {@link PlaybackGlueHost} that implements a
+ * recommended approach to handling standard playback control actions such as play/pause,
+ * fast forward/rewind at progressive speed levels, and skip to next/previous. This helper class
+ * is a glue layer in that manages the configuration of and interaction between the
+ * leanback UI components by defining a functional interface to the media player.
+ *
+ * <p>You can instantiate a concrete subclass such as MediaPlayerGlue or you must
+ * subclass this abstract helper.  To create a subclass you must implement all of the
+ * abstract methods and the subclass must invoke {@link #onMetadataChanged()} and
+ * {@link #onStateChanged()} appropriately.
+ * </p>
+ *
+ * <p>To use an instance of the glue layer, first construct an instance.  Constructor parameters
+ * inform the glue what speed levels are supported for fast forward/rewind.
+ * </p>
+ *
+ * <p>You may override {@link #onCreateControlsRowAndPresenter()} which will set a controls
+ * row and return a row presenter you can use to present the row.
+ * </p>
+ *
+ * <p>The helper sets a {@link SparseArrayObjectAdapter}
+ * on the controls row as the primary actions adapter, and adds actions to it. You can provide
+ * additional actions by overriding {@link #onCreatePrimaryActions}. This helper does not
+ * deal in secondary actions so those you may add separately.
+ * </p>
+ *
+ * <p>Provide a click listener on your fragment and if an action is clicked, call
+ * {@link #onActionClicked}.
+ * </p>
+ *
+ * <p>This helper implements a key event handler. If you pass a
+ * {@link PlaybackGlueHost}, it will configure it's
+ * fragment to intercept all key events.  Otherwise, you should set the glue object as key event
+ * handler to the ViewHolder when bound by your row presenter; see
+ * {@link RowPresenter.ViewHolder#setOnKeyListener(android.view.View.OnKeyListener)}.
+ * </p>
+ *
+ * <p>To update the controls row progress during playback, override {@link #enableProgressUpdating}
+ * to manage the lifecycle of a periodic callback to {@link #updateProgress()}.
+ * {@link #getUpdatePeriod()} provides a recommended update period.
+ * </p>
+ *
+ */
+public abstract class PlaybackControlGlue extends PlaybackGlue
+        implements OnActionClickedListener, View.OnKeyListener {
+    /**
+     * The adapter key for the first custom control on the left side
+     * of the predefined primary controls.
+     */
+    public static final int ACTION_CUSTOM_LEFT_FIRST = 0x1;
+
+    /**
+     * The adapter key for the skip to previous control.
+     */
+    public static final int ACTION_SKIP_TO_PREVIOUS = 0x10;
+
+    /**
+     * The adapter key for the rewind control.
+     */
+    public static final int ACTION_REWIND = 0x20;
+
+    /**
+     * The adapter key for the play/pause control.
+     */
+    public static final int ACTION_PLAY_PAUSE = 0x40;
+
+    /**
+     * The adapter key for the fast forward control.
+     */
+    public static final int ACTION_FAST_FORWARD = 0x80;
+
+    /**
+     * The adapter key for the skip to next control.
+     */
+    public static final int ACTION_SKIP_TO_NEXT = 0x100;
+
+    /**
+     * The adapter key for the first custom control on the right side
+     * of the predefined primary controls.
+     */
+    public static final int ACTION_CUSTOM_RIGHT_FIRST = 0x1000;
+
+    /**
+     * Invalid playback speed.
+     */
+    public static final int PLAYBACK_SPEED_INVALID = -1;
+
+    /**
+     * Speed representing playback state that is paused.
+     */
+    public static final int PLAYBACK_SPEED_PAUSED = 0;
+
+    /**
+     * Speed representing playback state that is playing normally.
+     */
+    public static final int PLAYBACK_SPEED_NORMAL = 1;
+
+    /**
+     * The initial (level 0) fast forward playback speed.
+     * The negative of this value is for rewind at the same speed.
+     */
+    public static final int PLAYBACK_SPEED_FAST_L0 = 10;
+
+    /**
+     * The level 1 fast forward playback speed.
+     * The negative of this value is for rewind at the same speed.
+     */
+    public static final int PLAYBACK_SPEED_FAST_L1 = 11;
+
+    /**
+     * The level 2 fast forward playback speed.
+     * The negative of this value is for rewind at the same speed.
+     */
+    public static final int PLAYBACK_SPEED_FAST_L2 = 12;
+
+    /**
+     * The level 3 fast forward playback speed.
+     * The negative of this value is for rewind at the same speed.
+     */
+    public static final int PLAYBACK_SPEED_FAST_L3 = 13;
+
+    /**
+     * The level 4 fast forward playback speed.
+     * The negative of this value is for rewind at the same speed.
+     */
+    public static final int PLAYBACK_SPEED_FAST_L4 = 14;
+
+    static final String TAG = "PlaybackControlGlue";
+    static final boolean DEBUG = false;
+
+    static final int MSG_UPDATE_PLAYBACK_STATE = 100;
+    private static final int UPDATE_PLAYBACK_STATE_DELAY_MS = 2000;
+    private static final int NUMBER_OF_SEEK_SPEEDS = PLAYBACK_SPEED_FAST_L4
+            - PLAYBACK_SPEED_FAST_L0 + 1;
+
+    private final int[] mFastForwardSpeeds;
+    private final int[] mRewindSpeeds;
+    private PlaybackControlsRow mControlsRow;
+    private PlaybackControlsRowPresenter mControlsRowPresenter;
+    private PlaybackControlsRow.PlayPauseAction mPlayPauseAction;
+    private PlaybackControlsRow.SkipNextAction mSkipNextAction;
+    private PlaybackControlsRow.SkipPreviousAction mSkipPreviousAction;
+    private PlaybackControlsRow.FastForwardAction mFastForwardAction;
+    private PlaybackControlsRow.RewindAction mRewindAction;
+    private int mPlaybackSpeed = PLAYBACK_SPEED_NORMAL;
+    private boolean mFadeWhenPlaying = true;
+
+    static class UpdatePlaybackStateHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what == MSG_UPDATE_PLAYBACK_STATE) {
+                PlaybackControlGlue glue = ((WeakReference<PlaybackControlGlue>) msg.obj).get();
+                if (glue != null) {
+                    glue.updatePlaybackState();
+                }
+            }
+        }
+    }
+
+    static final Handler sHandler = new UpdatePlaybackStateHandler();
+
+    final WeakReference<PlaybackControlGlue> mGlueWeakReference =  new WeakReference(this);
+
+    /**
+     * Constructor for the glue.
+     *
+     * @param context
+     * @param seekSpeeds Array of seek speeds for fast forward and rewind.
+     */
+    public PlaybackControlGlue(Context context, int[] seekSpeeds) {
+        this(context, seekSpeeds, seekSpeeds);
+    }
+
+    /**
+     * Constructor for the glue.
+     *
+     * @param context
+     * @param fastForwardSpeeds Array of seek speeds for fast forward.
+     * @param rewindSpeeds Array of seek speeds for rewind.
+     */
+    public PlaybackControlGlue(Context context,
+                               int[] fastForwardSpeeds,
+                               int[] rewindSpeeds) {
+        super(context);
+        if (fastForwardSpeeds.length == 0 || fastForwardSpeeds.length > NUMBER_OF_SEEK_SPEEDS) {
+            throw new IllegalStateException("invalid fastForwardSpeeds array size");
+        }
+        mFastForwardSpeeds = fastForwardSpeeds;
+        if (rewindSpeeds.length == 0 || rewindSpeeds.length > NUMBER_OF_SEEK_SPEEDS) {
+            throw new IllegalStateException("invalid rewindSpeeds array size");
+        }
+        mRewindSpeeds = rewindSpeeds;
+    }
+
+    @Override
+    protected void onAttachedToHost(PlaybackGlueHost host) {
+        super.onAttachedToHost(host);
+        host.setOnKeyInterceptListener(this);
+        host.setOnActionClickedListener(this);
+        if (getControlsRow() == null || getControlsRowPresenter() == null) {
+            onCreateControlsRowAndPresenter();
+        }
+        host.setPlaybackRowPresenter(getControlsRowPresenter());
+        host.setPlaybackRow(getControlsRow());
+    }
+
+    @Override
+    protected void onHostStart() {
+        enableProgressUpdating(true);
+    }
+
+    @Override
+    protected void onHostStop() {
+        enableProgressUpdating(false);
+    }
+
+    /**
+     * Instantiating a {@link PlaybackControlsRow} and corresponding
+     * {@link PlaybackControlsRowPresenter}. Subclass may override.
+     */
+    protected void onCreateControlsRowAndPresenter() {
+        PlaybackControlsRow controlsRow = new PlaybackControlsRow(this);
+        setControlsRow(controlsRow);
+
+        final AbstractDetailsDescriptionPresenter detailsPresenter =
+                new AbstractDetailsDescriptionPresenter() {
+            @Override
+            protected void onBindDescription(ViewHolder
+                                                     viewHolder, Object object) {
+                PlaybackControlGlue glue = (PlaybackControlGlue) object;
+                if (glue.hasValidMedia()) {
+                    viewHolder.getTitle().setText(glue.getMediaTitle());
+                    viewHolder.getSubtitle().setText(glue.getMediaSubtitle());
+                } else {
+                    viewHolder.getTitle().setText("");
+                    viewHolder.getSubtitle().setText("");
+                }
+            }
+        };
+
+        setControlsRowPresenter(new PlaybackControlsRowPresenter(detailsPresenter) {
+            @Override
+            protected void onBindRowViewHolder(RowPresenter.ViewHolder vh, Object item) {
+                super.onBindRowViewHolder(vh, item);
+                vh.setOnKeyListener(PlaybackControlGlue.this);
+            }
+            @Override
+            protected void onUnbindRowViewHolder(RowPresenter.ViewHolder vh) {
+                super.onUnbindRowViewHolder(vh);
+                vh.setOnKeyListener(null);
+            }
+        });
+    }
+
+    /**
+     * Returns the fast forward speeds.
+     */
+    public int[] getFastForwardSpeeds() {
+        return mFastForwardSpeeds;
+    }
+
+    /**
+     * Returns the rewind speeds.
+     */
+    public int[] getRewindSpeeds() {
+        return mRewindSpeeds;
+    }
+
+    /**
+     * Sets the controls to fade after a timeout when media is playing.
+     */
+    public void setFadingEnabled(boolean enable) {
+        mFadeWhenPlaying = enable;
+        if (!mFadeWhenPlaying && getHost() != null) {
+            getHost().setFadingEnabled(false);
+        }
+    }
+
+    /**
+     * Returns true if controls are set to fade when media is playing.
+     */
+    public boolean isFadingEnabled() {
+        return mFadeWhenPlaying;
+    }
+
+    /**
+     * Sets the controls row to be managed by the glue layer.
+     * The primary actions and playback state related aspects of the row
+     * are updated by the glue.
+     */
+    public void setControlsRow(PlaybackControlsRow controlsRow) {
+        mControlsRow = controlsRow;
+        mControlsRow.setPrimaryActionsAdapter(
+                createPrimaryActionsAdapter(new ControlButtonPresenterSelector()));
+        // Add secondary actions
+        ArrayObjectAdapter secondaryActions = new ArrayObjectAdapter(
+                new ControlButtonPresenterSelector());
+        getControlsRow().setSecondaryActionsAdapter(secondaryActions);
+        updateControlsRow();
+    }
+
+    /**
+     * @hide
+     */
+    protected SparseArrayObjectAdapter createPrimaryActionsAdapter(
+            PresenterSelector presenterSelector) {
+        SparseArrayObjectAdapter adapter = new SparseArrayObjectAdapter(presenterSelector);
+        onCreatePrimaryActions(adapter);
+        return adapter;
+    }
+
+    /**
+     * Sets the controls row Presenter to be managed by the glue layer.
+     */
+    public void setControlsRowPresenter(PlaybackControlsRowPresenter presenter) {
+        mControlsRowPresenter = presenter;
+    }
+
+    /**
+     * Returns the playback controls row managed by the glue layer.
+     */
+    public PlaybackControlsRow getControlsRow() {
+        return mControlsRow;
+    }
+
+    /**
+     * Returns the playback controls row Presenter managed by the glue layer.
+     */
+    public PlaybackControlsRowPresenter getControlsRowPresenter() {
+        return mControlsRowPresenter;
+    }
+
+    /**
+     * Override this to start/stop a runnable to call {@link #updateProgress} at
+     * an interval such as {@link #getUpdatePeriod}.
+     */
+    public void enableProgressUpdating(boolean enable) {
+    }
+
+    /**
+     * Returns the time period in milliseconds that should be used
+     * to update the progress.  See {@link #updateProgress()}.
+     */
+    public int getUpdatePeriod() {
+        // TODO: calculate a better update period based on total duration and screen size
+        return 500;
+    }
+
+    /**
+     * Updates the progress bar based on the current media playback position.
+     */
+    public void updateProgress() {
+        int position = getCurrentPosition();
+        if (DEBUG) Log.v(TAG, "updateProgress " + position);
+        mControlsRow.setCurrentTime(position);
+    }
+
+    /**
+     * Handles action clicks.  A subclass may override this add support for additional actions.
+     */
+    @Override
+    public void onActionClicked(Action action) {
+        dispatchAction(action, null);
+    }
+
+    /**
+     * Handles key events and returns true if handled.  A subclass may override this to provide
+     * additional support.
+     */
+    @Override
+    public boolean onKey(View v, int keyCode, KeyEvent event) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_UP:
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+            case KeyEvent.KEYCODE_BACK:
+            case KeyEvent.KEYCODE_ESCAPE:
+                boolean abortSeek = mPlaybackSpeed >= PLAYBACK_SPEED_FAST_L0
+                        || mPlaybackSpeed <= -PLAYBACK_SPEED_FAST_L0;
+                if (abortSeek) {
+                    mPlaybackSpeed = PLAYBACK_SPEED_NORMAL;
+                    play(mPlaybackSpeed);
+                    updatePlaybackStatusAfterUserAction();
+                    return keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE;
+                }
+                return false;
+        }
+        final SparseArrayObjectAdapter primaryActionsAdapter = (SparseArrayObjectAdapter)
+                mControlsRow.getPrimaryActionsAdapter();
+        Action action = mControlsRow.getActionForKeyCode(primaryActionsAdapter, keyCode);
+
+        if (action != null) {
+            if (action == primaryActionsAdapter.lookup(ACTION_PLAY_PAUSE)
+                    || action == primaryActionsAdapter.lookup(ACTION_REWIND)
+                    || action == primaryActionsAdapter.lookup(ACTION_FAST_FORWARD)
+                    || action == primaryActionsAdapter.lookup(ACTION_SKIP_TO_PREVIOUS)
+                    || action == primaryActionsAdapter.lookup(ACTION_SKIP_TO_NEXT)) {
+                if (event.getAction() == KeyEvent.ACTION_DOWN) {
+                    dispatchAction(action, event);
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Called when the given action is invoked, either by click or keyevent.
+     */
+    boolean dispatchAction(Action action, KeyEvent keyEvent) {
+        boolean handled = false;
+        if (action == mPlayPauseAction) {
+            boolean canPlay = keyEvent == null
+                    || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
+                    || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY;
+            boolean canPause = keyEvent == null
+                    || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE
+                    || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PAUSE;
+            //            PLAY_PAUSE    PLAY      PAUSE
+            // playing    paused                  paused
+            // paused     playing       playing
+            // ff/rw      playing       playing   paused
+            if (canPause
+                    && (canPlay ? mPlaybackSpeed == PLAYBACK_SPEED_NORMAL :
+                        mPlaybackSpeed != PLAYBACK_SPEED_PAUSED)) {
+                mPlaybackSpeed = PLAYBACK_SPEED_PAUSED;
+                pause();
+            } else if (canPlay && mPlaybackSpeed != PLAYBACK_SPEED_NORMAL) {
+                mPlaybackSpeed = PLAYBACK_SPEED_NORMAL;
+                play(mPlaybackSpeed);
+            }
+            updatePlaybackStatusAfterUserAction();
+            handled = true;
+        } else if (action == mSkipNextAction) {
+            next();
+            handled = true;
+        } else if (action == mSkipPreviousAction) {
+            previous();
+            handled = true;
+        } else if (action == mFastForwardAction) {
+            if (mPlaybackSpeed < getMaxForwardSpeedId()) {
+                switch (mPlaybackSpeed) {
+                    case PLAYBACK_SPEED_FAST_L0:
+                    case PLAYBACK_SPEED_FAST_L1:
+                    case PLAYBACK_SPEED_FAST_L2:
+                    case PLAYBACK_SPEED_FAST_L3:
+                        mPlaybackSpeed++;
+                        break;
+                    default:
+                        mPlaybackSpeed = PLAYBACK_SPEED_FAST_L0;
+                        break;
+                }
+                play(mPlaybackSpeed);
+                updatePlaybackStatusAfterUserAction();
+            }
+            handled = true;
+        } else if (action == mRewindAction) {
+            if (mPlaybackSpeed > -getMaxRewindSpeedId()) {
+                switch (mPlaybackSpeed) {
+                    case -PLAYBACK_SPEED_FAST_L0:
+                    case -PLAYBACK_SPEED_FAST_L1:
+                    case -PLAYBACK_SPEED_FAST_L2:
+                    case -PLAYBACK_SPEED_FAST_L3:
+                        mPlaybackSpeed--;
+                        break;
+                    default:
+                        mPlaybackSpeed = -PLAYBACK_SPEED_FAST_L0;
+                        break;
+                }
+                play(mPlaybackSpeed);
+                updatePlaybackStatusAfterUserAction();
+            }
+            handled = true;
+        }
+        return handled;
+    }
+
+    private int getMaxForwardSpeedId() {
+        return PLAYBACK_SPEED_FAST_L0 + (mFastForwardSpeeds.length - 1);
+    }
+
+    private int getMaxRewindSpeedId() {
+        return PLAYBACK_SPEED_FAST_L0 + (mRewindSpeeds.length - 1);
+    }
+
+    private void updateControlsRow() {
+        updateRowMetadata();
+        sHandler.removeMessages(MSG_UPDATE_PLAYBACK_STATE, mGlueWeakReference);
+        updatePlaybackState();
+    }
+
+    private void updatePlaybackStatusAfterUserAction() {
+        updatePlaybackState(mPlaybackSpeed);
+        // Sync playback state after a delay
+        sHandler.removeMessages(MSG_UPDATE_PLAYBACK_STATE, mGlueWeakReference);
+        sHandler.sendMessageDelayed(sHandler.obtainMessage(MSG_UPDATE_PLAYBACK_STATE,
+                mGlueWeakReference), UPDATE_PLAYBACK_STATE_DELAY_MS);
+    }
+
+    /**
+     * Start playback at the given speed.
+     *
+     * @param speed The desired playback speed.  For normal playback this will be
+     *              {@link #PLAYBACK_SPEED_NORMAL}; higher positive values for fast forward,
+     *              and negative values for rewind.
+     */
+    public void play(int speed) {
+    }
+
+    @Override
+    public final void play() {
+        play(PLAYBACK_SPEED_NORMAL);
+    }
+
+    private void updateRowMetadata() {
+        if (mControlsRow == null) {
+            return;
+        }
+
+        if (DEBUG) Log.v(TAG, "updateRowMetadata");
+
+        if (!hasValidMedia()) {
+            mControlsRow.setImageDrawable(null);
+            mControlsRow.setTotalTime(0);
+            mControlsRow.setCurrentTime(0);
+        } else {
+            mControlsRow.setImageDrawable(getMediaArt());
+            mControlsRow.setTotalTime(getMediaDuration());
+            mControlsRow.setCurrentTime(getCurrentPosition());
+        }
+
+        if (getHost() != null) {
+            getHost().notifyPlaybackRowChanged();
+        }
+    }
+
+    void updatePlaybackState() {
+        if (hasValidMedia()) {
+            mPlaybackSpeed = getCurrentSpeedId();
+            updatePlaybackState(mPlaybackSpeed);
+        }
+    }
+
+    private void updatePlaybackState(int playbackSpeed) {
+        if (mControlsRow == null) {
+            return;
+        }
+
+        final SparseArrayObjectAdapter primaryActionsAdapter = (SparseArrayObjectAdapter)
+                getControlsRow().getPrimaryActionsAdapter();
+        final long actions = getSupportedActions();
+        if ((actions & ACTION_SKIP_TO_PREVIOUS) != 0) {
+            if (mSkipPreviousAction == null) {
+                mSkipPreviousAction = new PlaybackControlsRow.SkipPreviousAction(getContext());
+            }
+            primaryActionsAdapter.set(ACTION_SKIP_TO_PREVIOUS, mSkipPreviousAction);
+        } else {
+            primaryActionsAdapter.clear(ACTION_SKIP_TO_PREVIOUS);
+            mSkipPreviousAction = null;
+        }
+        if ((actions & ACTION_REWIND) != 0) {
+            if (mRewindAction == null) {
+                mRewindAction = new PlaybackControlsRow.RewindAction(
+                        getContext(),
+                        mRewindSpeeds.length);
+            }
+            primaryActionsAdapter.set(ACTION_REWIND, mRewindAction);
+        } else {
+            primaryActionsAdapter.clear(ACTION_REWIND);
+            mRewindAction = null;
+        }
+        if ((actions & ACTION_PLAY_PAUSE) != 0) {
+            if (mPlayPauseAction == null) {
+                mPlayPauseAction = new PlaybackControlsRow.PlayPauseAction(getContext());
+            }
+            primaryActionsAdapter.set(ACTION_PLAY_PAUSE, mPlayPauseAction);
+        } else {
+            primaryActionsAdapter.clear(ACTION_PLAY_PAUSE);
+            mPlayPauseAction = null;
+        }
+        if ((actions & ACTION_FAST_FORWARD) != 0) {
+            if (mFastForwardAction == null) {
+                mFastForwardAction = new PlaybackControlsRow.FastForwardAction(
+                        getContext(),
+                        mFastForwardSpeeds.length);
+            }
+            primaryActionsAdapter.set(ACTION_FAST_FORWARD, mFastForwardAction);
+        } else {
+            primaryActionsAdapter.clear(ACTION_FAST_FORWARD);
+            mFastForwardAction = null;
+        }
+        if ((actions & ACTION_SKIP_TO_NEXT) != 0) {
+            if (mSkipNextAction == null) {
+                mSkipNextAction = new PlaybackControlsRow.SkipNextAction(getContext());
+            }
+            primaryActionsAdapter.set(ACTION_SKIP_TO_NEXT, mSkipNextAction);
+        } else {
+            primaryActionsAdapter.clear(ACTION_SKIP_TO_NEXT);
+            mSkipNextAction = null;
+        }
+
+        if (mFastForwardAction != null) {
+            int index = 0;
+            if (playbackSpeed >= PLAYBACK_SPEED_FAST_L0) {
+                index = playbackSpeed - PLAYBACK_SPEED_FAST_L0 + 1;
+            }
+            if (mFastForwardAction.getIndex() != index) {
+                mFastForwardAction.setIndex(index);
+                notifyItemChanged(primaryActionsAdapter, mFastForwardAction);
+            }
+        }
+        if (mRewindAction != null) {
+            int index = 0;
+            if (playbackSpeed <= -PLAYBACK_SPEED_FAST_L0) {
+                index = -playbackSpeed - PLAYBACK_SPEED_FAST_L0 + 1;
+            }
+            if (mRewindAction.getIndex() != index) {
+                mRewindAction.setIndex(index);
+                notifyItemChanged(primaryActionsAdapter, mRewindAction);
+            }
+        }
+
+        if (playbackSpeed == PLAYBACK_SPEED_PAUSED) {
+            updateProgress();
+            enableProgressUpdating(false);
+        } else {
+            enableProgressUpdating(true);
+        }
+
+        if (mFadeWhenPlaying && getHost() != null) {
+            getHost().setFadingEnabled(playbackSpeed == PLAYBACK_SPEED_NORMAL);
+        }
+
+        if (mPlayPauseAction != null) {
+            int index = playbackSpeed == PLAYBACK_SPEED_PAUSED
+                    ? PlaybackControlsRow.PlayPauseAction.PLAY
+                    : PlaybackControlsRow.PlayPauseAction.PAUSE;
+            if (mPlayPauseAction.getIndex() != index) {
+                mPlayPauseAction.setIndex(index);
+                notifyItemChanged(primaryActionsAdapter, mPlayPauseAction);
+            }
+        }
+    }
+
+    private static void notifyItemChanged(SparseArrayObjectAdapter adapter, Object object) {
+        int index = adapter.indexOf(object);
+        if (index >= 0) {
+            adapter.notifyArrayItemRangeChanged(index, 1);
+        }
+    }
+
+    private static String getSpeedString(int speed) {
+        switch (speed) {
+            case PLAYBACK_SPEED_INVALID:
+                return "PLAYBACK_SPEED_INVALID";
+            case PLAYBACK_SPEED_PAUSED:
+                return "PLAYBACK_SPEED_PAUSED";
+            case PLAYBACK_SPEED_NORMAL:
+                return "PLAYBACK_SPEED_NORMAL";
+            case PLAYBACK_SPEED_FAST_L0:
+                return "PLAYBACK_SPEED_FAST_L0";
+            case PLAYBACK_SPEED_FAST_L1:
+                return "PLAYBACK_SPEED_FAST_L1";
+            case PLAYBACK_SPEED_FAST_L2:
+                return "PLAYBACK_SPEED_FAST_L2";
+            case PLAYBACK_SPEED_FAST_L3:
+                return "PLAYBACK_SPEED_FAST_L3";
+            case PLAYBACK_SPEED_FAST_L4:
+                return "PLAYBACK_SPEED_FAST_L4";
+            case -PLAYBACK_SPEED_FAST_L0:
+                return "-PLAYBACK_SPEED_FAST_L0";
+            case -PLAYBACK_SPEED_FAST_L1:
+                return "-PLAYBACK_SPEED_FAST_L1";
+            case -PLAYBACK_SPEED_FAST_L2:
+                return "-PLAYBACK_SPEED_FAST_L2";
+            case -PLAYBACK_SPEED_FAST_L3:
+                return "-PLAYBACK_SPEED_FAST_L3";
+            case -PLAYBACK_SPEED_FAST_L4:
+                return "-PLAYBACK_SPEED_FAST_L4";
+        }
+        return null;
+    }
+
+    /**
+     * Returns true if there is a valid media item.
+     */
+    public abstract boolean hasValidMedia();
+
+    /**
+     * Returns true if media is currently playing.
+     */
+    public abstract boolean isMediaPlaying();
+
+    /**
+     * Returns the title of the media item.
+     */
+    public abstract CharSequence getMediaTitle();
+
+    /**
+     * Returns the subtitle of the media item.
+     */
+    public abstract CharSequence getMediaSubtitle();
+
+    /**
+     * Returns the duration of the media item in milliseconds.
+     */
+    public abstract int getMediaDuration();
+
+    /**
+     * Returns a bitmap of the art for the media item.
+     */
+    public abstract Drawable getMediaArt();
+
+    /**
+     * Returns a bitmask of actions supported by the media player.
+     */
+    public abstract long getSupportedActions();
+
+    /**
+     * Returns the current playback speed.  When playing normally,
+     * {@link #PLAYBACK_SPEED_NORMAL} should be returned.
+     */
+    public abstract int getCurrentSpeedId();
+
+    /**
+     * Returns the current position of the media item in milliseconds.
+     */
+    public abstract int getCurrentPosition();
+
+    /**
+     * May be overridden to add primary actions to the adapter.
+     *
+     * @param primaryActionsAdapter The adapter to add primary {@link Action}s.
+     */
+    protected void onCreatePrimaryActions(SparseArrayObjectAdapter primaryActionsAdapter) {
+    }
+
+    /**
+     * May be overridden to add secondary actions to the adapter.
+     *
+     * @param secondaryActionsAdapter The adapter you need to add the {@link Action}s to.
+     */
+    protected void onCreateSecondaryActions(ArrayObjectAdapter secondaryActionsAdapter) {
+    }
+
+    /**
+     * Must be called appropriately by a subclass when the playback state has changed.
+     * It updates the playback state displayed on the media player.
+     */
+    protected void onStateChanged() {
+        if (DEBUG) Log.v(TAG, "onStateChanged");
+        // If a pending control button update is present, delay
+        // the update until the state settles.
+        if (!hasValidMedia()) {
+            return;
+        }
+        if (sHandler.hasMessages(MSG_UPDATE_PLAYBACK_STATE, mGlueWeakReference)) {
+            sHandler.removeMessages(MSG_UPDATE_PLAYBACK_STATE, mGlueWeakReference);
+            if (getCurrentSpeedId() != mPlaybackSpeed) {
+                if (DEBUG) Log.v(TAG, "Status expectation mismatch, delaying update");
+                sHandler.sendMessageDelayed(sHandler.obtainMessage(MSG_UPDATE_PLAYBACK_STATE,
+                        mGlueWeakReference), UPDATE_PLAYBACK_STATE_DELAY_MS);
+            } else {
+                if (DEBUG) Log.v(TAG, "Update state matches expectation");
+                updatePlaybackState();
+            }
+        } else {
+            updatePlaybackState();
+        }
+    }
+
+    /**
+     * Must be called appropriately by a subclass when the metadata state has changed.
+     */
+    protected void onMetadataChanged() {
+        if (DEBUG) Log.v(TAG, "onMetadataChanged");
+        updateRowMetadata();
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/media/PlaybackGlue.java b/v17/leanback/src/android/support/v17/leanback/media/PlaybackGlue.java
new file mode 100644
index 0000000..8ed4b93
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/media/PlaybackGlue.java
@@ -0,0 +1,186 @@
+/*
+ * 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.media;
+
+import android.content.Context;
+import android.support.annotation.CallSuper;
+
+/**
+ * Base class for abstraction of media play/pause feature. A subclass of PlaybackGlue will contain
+ * implementation of Media Player. App initializes PlaybackGlue subclass, associated it with a
+ * {@link PlaybackGlueHost}. {@link PlaybackGlueHost} is typically implemented by a Fragment or
+ * an Activity, it provides the environment to render UI for PlaybackGlue object, it optionally
+ * provides SurfaceHolder via {@link SurfaceHolderGlueHost} to render video.
+ *
+ * @see PlaybackGlueHost
+ */
+public abstract class PlaybackGlue {
+    private final Context mContext;
+    private PlaybackGlueHost mPlaybackGlueHost;
+
+    /**
+     * Interface to allow clients to take action once the video is ready to play.
+     */
+    public abstract static class PlayerCallback {
+        /**
+         * This method is fired when the video is ready for playback.
+         */
+        public abstract void onReadyForPlayback();
+    }
+
+    /**
+     * Constructor.
+     */
+    public PlaybackGlue(Context context) {
+        this.mContext = context;
+    }
+
+    /**
+     * Returns the context.
+     */
+    public Context getContext() {
+        return mContext;
+    }
+
+    /**
+     * Returns true when the media player is ready to start media playback. Subclasses must
+     * implement this method correctly.
+     */
+    public boolean isReadyForPlayback() {
+        return true;
+    }
+
+    /**
+     * Sets the {@link PlayerCallback} callback.
+     */
+    public void setPlayerCallback(PlayerCallback playerCallback) {
+    }
+
+    /**
+     * Starts the media player.
+     */
+    public void play() {
+    }
+
+    /**
+     * Pauses the media player.
+     */
+    public void pause() {
+    }
+
+    /**
+     * Goes to the next media item. This method is optional.
+     */
+    public void next() {
+    }
+
+    /**
+     * Goes to the previous media item. This method is optional.
+     */
+    public void previous() {
+    }
+
+    /**
+     * This method is used to configure the {@link PlaybackGlueHost} with required listeners.
+     */
+    public final void setHost(PlaybackGlueHost host) {
+        if (mPlaybackGlueHost == host) {
+            return;
+        }
+        if (mPlaybackGlueHost != null) {
+            mPlaybackGlueHost.attachToGlue(null);
+        }
+        mPlaybackGlueHost = host;
+        if (mPlaybackGlueHost != null) {
+            mPlaybackGlueHost.attachToGlue(this);
+        }
+    }
+
+    /**
+     * This method is called when {@link PlaybackGlueHost is started. Subclass may override.
+     */
+    protected void onHostStart() {
+    }
+
+    /**
+     * This method is called when {@link PlaybackGlueHost is stopped. Subclass may override.
+     */
+    protected void onHostStop() {
+    }
+
+    /**
+     * This method is called when {@link PlaybackGlueHost is resumed. Subclass may override.
+     */
+    protected void onHostResume() {
+    }
+
+    /**
+     * This method is called when {@link PlaybackGlueHost is paused. Subclass may override.
+     */
+    protected void onHostPause() {
+    }
+
+    /**
+     * This method is called attached to associated {@link PlaybackGlueHost}. Subclass may override
+     * and call super.onAttachedToHost().
+     */
+    @CallSuper
+    protected void onAttachedToHost(PlaybackGlueHost host) {
+        mPlaybackGlueHost = host;
+        mPlaybackGlueHost.setHostCallback(new PlaybackGlueHost.HostCallback() {
+            @Override
+            public void onHostStart() {
+                PlaybackGlue.this.onHostStart();
+            }
+
+            @Override
+            public void onHostStop() {
+                PlaybackGlue.this.onHostStop();
+            }
+
+            @Override
+            public void onHostResume() {
+                PlaybackGlue.this.onHostResume();
+            }
+
+            @Override
+            public void onHostPause() {
+                PlaybackGlue.this.onHostPause();
+            }
+        });
+    }
+
+    /**
+     * This method is called when current associated {@link PlaybackGlueHost} is attached to a
+     * different {@link PlaybackGlue}. Subclass may override and call super.onDetachedFromHost()
+     * at last.
+     */
+    @CallSuper
+    protected void onDetachedFromHost() {
+        if (mPlaybackGlueHost != null) {
+            mPlaybackGlueHost.setHostCallback(null);
+            mPlaybackGlueHost = null;
+        }
+    }
+
+    /**
+     * @return Associated {@link PlaybackGlueHost} or null if not attached to host.
+     */
+    public PlaybackGlueHost getHost() {
+        return mPlaybackGlueHost;
+    }
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/media/PlaybackGlueHost.java b/v17/leanback/src/android/support/v17/leanback/media/PlaybackGlueHost.java
new file mode 100644
index 0000000..3cf086b
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/media/PlaybackGlueHost.java
@@ -0,0 +1,136 @@
+/*
+ * 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.media;
+
+import android.support.v17.leanback.widget.OnActionClickedListener;
+import android.support.v17.leanback.widget.PlaybackRowPresenter;
+import android.support.v17.leanback.widget.Row;
+import android.view.View;
+
+/**
+ * This class represents the UI (e.g. Fragment/Activity) hosting playback controls and
+ * defines the interaction between {@link PlaybackGlue} and the host.
+ * PlaybackGlueHost provides the following functions:
+ * <li>Render UI of PlaybackGlue: {@link #setPlaybackRow(Row)},
+ * {@link #setPlaybackRowPresenter(PlaybackRowPresenter)}.
+ * </li>
+ * <li>Callback for fragment/activity onStart/onStop: {@link #setHostCallback(HostCallback)}.
+ * </li>
+ * <li>Auto fade out controls after a short period: {@link #setFadingEnabled(boolean)}.
+ * </li>
+ * <li>Key listener and ActionListener. {@link #setOnKeyInterceptListener(View.OnKeyListener)},
+ * {@link #setOnActionClickedListener(OnActionClickedListener)}.
+ * </li>
+ *
+ * Subclass of PlaybackGlueHost may implement optional interface e.g. {@link SurfaceHolderGlueHost}
+ * to provide SurfaceView. These optional interface should be used during
+ * {@link PlaybackGlue#setHost(PlaybackGlueHost)}.
+ */
+public abstract class PlaybackGlueHost {
+    PlaybackGlue mGlue;
+
+    /**
+     * Callbacks triggered by the host(e.g. fragment) hosting the video controls/surface.
+     *
+     * @see #setHostCallback(HostCallback)
+     */
+    public abstract static class HostCallback {
+        /**
+         * Callback triggered once the host(fragment) has started.
+         */
+        public void onHostStart() {
+        }
+
+        /**
+         * Callback triggered once the host(fragment) has stopped.
+         */
+        public void onHostStop() {
+        }
+
+        /**
+         * Callback triggered once the host(fragment) has paused.
+         */
+        public void onHostPause() {
+        }
+
+        /**
+         * Callback triggered once the host(fragment) has resumed.
+         */
+        public void onHostResume() {
+        }
+    }
+
+    /**
+     * 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.
+     */
+    public void setFadingEnabled(boolean enable) {
+    }
+
+    /**
+     * Fade out views immediately.
+     */
+    public void fadeOut() {
+    }
+
+    /**
+     * Sets the {@link android.view.View.OnKeyListener} on the host. This would trigger
+     * the listener when a {@link android.view.KeyEvent} is unhandled by the host.
+     */
+    public void setOnKeyInterceptListener(View.OnKeyListener onKeyListener) {
+    }
+
+    /**
+     * Sets the {@link View.OnClickListener} on this fragment.
+     */
+    public void setOnActionClickedListener(OnActionClickedListener listener) {}
+
+    /**
+     * Sets the host {@link HostCallback} callback on the host. This method should only be called
+     * by {@link PlaybackGlue}. App should not directly call this method, app should override
+     * {@link PlaybackGlue#onHostStart()} etc.
+     */
+    public void setHostCallback(HostCallback callback) {
+    }
+
+    /**
+     * Notifies host about a change so it can update the view.
+     */
+    public void notifyPlaybackRowChanged() {}
+
+    /**
+     * Sets {@link PlaybackRowPresenter} for rendering the playback controls.
+     */
+    public void setPlaybackRowPresenter(PlaybackRowPresenter presenter) {}
+
+    /**
+     * Sets the {@link Row} that represents the information on control items that needs
+     * to be rendered.
+     */
+    public void setPlaybackRow(Row row) {}
+
+    final void attachToGlue(PlaybackGlue glue) {
+        if (mGlue != null) {
+            mGlue.onDetachedFromHost();
+        }
+        mGlue = glue;
+        if (mGlue != null) {
+            mGlue.onAttachedToHost(this);
+        }
+    }
+
+}
diff --git a/v17/leanback/src/android/support/v17/leanback/media/SurfaceHolderGlueHost.java b/v17/leanback/src/android/support/v17/leanback/media/SurfaceHolderGlueHost.java
new file mode 100644
index 0000000..119626a
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/media/SurfaceHolderGlueHost.java
@@ -0,0 +1,34 @@
+/*
+ * 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.media;
+
+import android.view.SurfaceHolder;
+
+/**
+ * Optional interface to be implemented by any subclass of {@link PlaybackGlueHost} that contains
+ * a {@link android.view.SurfaceView}. This will allow subclass of {@link PlaybackGlue} to setup
+ * the surface holder callback during {@link PlaybackGlue#setHost(PlaybackGlueHost)}.
+ *
+ * @see PlaybackGlue#setHost(PlaybackGlueHost)
+ * @see MediaPlayerGlue
+ */
+public interface SurfaceHolderGlueHost {
+    /**
+     * Sets the {@link SurfaceHolder.Callback} on the the host.
+     */
+    void setSurfaceHolderCallback(SurfaceHolder.Callback callback);
+}