diff --git a/.gitignore b/.gitignore index 1a946e2ade..db5a8c4305 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,9 @@ cmake-build-debug dist tmp +# External native builds +.externalNativeBuild + # VP9 extension extensions/vp9/src/main/jni/libvpx extensions/vp9/src/main/jni/libvpx_android_configs @@ -62,3 +65,4 @@ extensions/cronet/jniLibs/* !extensions/cronet/jniLibs/README.md extensions/cronet/libs/* !extensions/cronet/libs/README.md + diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 2eae5f5902..c7ad92c09b 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -35,6 +35,7 @@ header ([#3926](https://github.com/google/ExoPlayer/issues/3926)). * Video: force rendering a frame periodically in `MediaCodecVideoRenderer` and `LibvpxVideoRenderer`, even if it is late. +* Add PlayerNotificationManager. ### 2.7.0 ### diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ControlDispatcher.java b/library/core/src/main/java/com/google/android/exoplayer2/ControlDispatcher.java index 21c596e6d4..f8749fc1a8 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ControlDispatcher.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ControlDispatcher.java @@ -64,4 +64,12 @@ public interface ControlDispatcher { */ boolean dispatchSetShuffleModeEnabled(Player player, boolean shuffleModeEnabled); + /** + * Dispatches a {@link Player#stop()} operation. + * + * @param player The {@link Player} to which the operation should be dispatched. + * @param reset Whether the player should be reset. + * @return True if the operation was dispatched. False if suppressed. + */ + boolean dispatchStop(Player player, boolean reset); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/DefaultControlDispatcher.java b/library/core/src/main/java/com/google/android/exoplayer2/DefaultControlDispatcher.java index 84711d752a..df3ef36b88 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/DefaultControlDispatcher.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/DefaultControlDispatcher.java @@ -47,4 +47,9 @@ public class DefaultControlDispatcher implements ControlDispatcher { return true; } + @Override + public boolean dispatchStop(Player player, boolean reset) { + player.stop(reset); + return true; + } } diff --git a/library/ui/build.gradle b/library/ui/build.gradle index 9689fcef97..017d7e3e14 100644 --- a/library/ui/build.gradle +++ b/library/ui/build.gradle @@ -34,6 +34,7 @@ android { dependencies { implementation project(modulePrefix + 'library-core') + implementation 'com.android.support:support-media-compat:' + supportLibraryVersion implementation 'com.android.support:support-annotations:' + supportLibraryVersion } diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerNotificationManager.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerNotificationManager.java new file mode 100644 index 0000000000..abdf6c7934 --- /dev/null +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerNotificationManager.java @@ -0,0 +1,931 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2.ui; + +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.app.Notification; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.os.Handler; +import android.os.Looper; +import android.support.annotation.IntDef; +import android.support.annotation.Nullable; +import android.support.v4.app.NotificationCompat; +import android.support.v4.app.NotificationManagerCompat; +import android.support.v4.media.app.NotificationCompat.MediaStyle; +import android.support.v4.media.session.MediaSessionCompat; +import android.support.v4.util.Pair; +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.ControlDispatcher; +import com.google.android.exoplayer2.DefaultControlDispatcher; +import com.google.android.exoplayer2.PlaybackParameters; +import com.google.android.exoplayer2.Player; +import com.google.android.exoplayer2.Timeline; +import com.google.android.exoplayer2.util.Util; +import java.lang.annotation.Retention; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * A notification manager to start, update and cancel a media style notification reflecting the + * player state. + * + *

The notification is cancelled when {@code null} is passed to {@link #setPlayer(Player)} or + * when an intent with action {@link #ACTION_STOP} is received. + * + *

If the player is released it must be removed from the manager by calling {@code + * setPlayer(null)} which will cancel the notification. + * + *

Action customization

+ * + * Standard playback actions can be shown or omitted as follows: + * + * + */ +public class PlayerNotificationManager { + + /** An adapter to provide content assets of the media currently playing. */ + public interface MediaDescriptionAdapter { + + /** + * Gets the content title for the current media item. + * + *

See {@link NotificationCompat.Builder#setContentTitle(CharSequence)}. + * + * @param player The {@link Player} for which a notification is being built. + */ + String getCurrentContentTitle(Player player); + + /** + * Creates a content intent for the current media item. + * + *

See {@link NotificationCompat.Builder#setContentIntent(PendingIntent)}. + * + * @param player The {@link Player} for which a notification is being built. + */ + @Nullable + PendingIntent createCurrentContentIntent(Player player); + + /** + * Gets the content text for the current media item. + * + *

See {@link NotificationCompat.Builder#setContentText(CharSequence)}. + * + * @param player The {@link Player} for which a notification is being built. + */ + @Nullable + String getCurrentContentText(Player player); + + /** + * Gets the large icon for the current media item. + * + *

When a bitmap initially needs to be asynchronously loaded, a placeholder (or null) can be + * returned and the bitmap asynchronously passed to the {@link BitmapCallback} once it is + * loaded. Because the adapter may be called multiple times for the same media item, the bitmap + * should be cached by the app and whenever possible be returned synchronously at subsequent + * calls for the same media item. + * + *

See {@link NotificationCompat.Builder#setLargeIcon(Bitmap)}. + * + * @param player The {@link Player} for which a notification is being built. + * @param callback A {@link BitmapCallback} to provide a {@link Bitmap} asynchronously. + */ + @Nullable + Bitmap getCurrentLargeIcon(Player player, BitmapCallback callback); + } + + /** Defines and handles custom actions. */ + public interface CustomActionReceiver { + + /** Gets the actions handled by this receiver. */ + Map createCustomActions(Context context); + + /** + * Gets the actions to be included in the notification given the current player state. + * + * @param player The {@link Player} for which a notification is being built. + * @return The actions to be included in the notification. + */ + List getCustomActions(Player player); + + /** + * Called when a custom action has been received. + * + * @param player The player. + * @param action The action from {@link Intent#getAction()}. + * @param intent The received {@link Intent}. + */ + void onCustomAction(Player player, String action, Intent intent); + } + + /** A listener for start and cancellation of the notification. */ + public interface NotificationListener { + + /** + * Called after the notification has been started. + * + * @param notificationId The id with which the notification has been posted. + * @param notification The {@link Notification}. + */ + void onNotificationStarted(int notificationId, Notification notification); + + /** + * Called after the notification has been cancelled. + * + * @param notificationId The id of the notification which has been cancelled. + */ + void onNotificationCancelled(int notificationId); + } + + /** Receives a {@link Bitmap}. */ + public final class BitmapCallback { + private final int notificationTag; + + /** Create the receiver. */ + private BitmapCallback(int notificationTag) { + this.notificationTag = notificationTag; + } + + /** + * Called when {@link Bitmap} is available. + * + * @param bitmap The bitmap to use as the large icon of the notification. + */ + public void onBitmap(final Bitmap bitmap) { + if (bitmap != null) { + mainHandler.post( + new Runnable() { + @Override + public void run() { + if (notificationTag == currentNotificationTag && isNotificationStarted) { + updateNotification(bitmap); + } + } + }); + } + } + } + + /** The action which starts playback. */ + public static final String ACTION_PLAY = "com.google.android.exoplayer.play"; + /** The action which pauses playback. */ + public static final String ACTION_PAUSE = "com.google.android.exoplayer.pause"; + /** The action which skips to the previous window. */ + public static final String ACTION_PREVIOUS = "com.google.android.exoplayer.prev"; + /** The action which skips to the next window. */ + public static final String ACTION_NEXT = "com.google.android.exoplayer.next"; + /** The action which fast forwards. */ + public static final String ACTION_FAST_FORWARD = "com.google.android.exoplayer.ffwd"; + /** The action which rewinds. */ + public static final String ACTION_REWIND = "com.google.android.exoplayer.rewind"; + /** The action which cancels the notification and stops playback. */ + public static final String ACTION_STOP = "com.google.android.exoplayer.stop"; + + /** Visibility of notification on the lock screen. */ + @Retention(SOURCE) + @IntDef({ + NotificationCompat.VISIBILITY_PRIVATE, + NotificationCompat.VISIBILITY_PUBLIC, + NotificationCompat.VISIBILITY_SECRET + }) + public @interface Visibility {} + + /** The default fast forward increment, in milliseconds. */ + public static final int DEFAULT_FAST_FORWARD_MS = 15000; + /** The default rewind increment, in milliseconds. */ + public static final int DEFAULT_REWIND_MS = 5000; + + private static final long MAX_POSITION_FOR_SEEK_TO_PREVIOUS = 3000; + + private final Context context; + private final Handler mainHandler; + private final NotificationManagerCompat notificationManager; + private final IntentFilter intentFilter; + private final Player.EventListener playerListener; + private final NotificationBroadcastReceiver notificationBroadcastReceiver; + private final MediaDescriptionAdapter mediaDescriptionAdapter; + private final int notificationId; + private final String channelId; + private final Map playbackActions; + private final Map customActions; + private final CustomActionReceiver customActionReceiver; + + private Player player; + private ControlDispatcher controlDispatcher; + private boolean isNotificationStarted; + private int currentNotificationTag; + private NotificationListener notificationListener; + private MediaSessionCompat.Token mediaSessionToken; + private boolean useNavigationActions; + private Pair stopAction; + private long fastForwardMs; + private long rewindMs; + private int badgeIconType; + private boolean colorized; + private int defaults; + private int color; + private int smallIconResourceId; + private int visibility; + private boolean ongoing; + private boolean useChronometer; + private boolean wasPlayWhenReady; + private int lastPlaybackState; + + /** + * Creates the manager. + * + * @param context The {@link Context}. + * @param mediaDescriptionAdapter The {@link MediaDescriptionAdapter}. + * @param channelId The id of the notification channel. + * @param notificationId The id of the notification. + */ + public PlayerNotificationManager( + Context context, + MediaDescriptionAdapter mediaDescriptionAdapter, + String channelId, + int notificationId) { + this(context, mediaDescriptionAdapter, channelId, notificationId, null); + } + + /** + * Creates the manager with a {@link CustomActionReceiver}. + * + * @param context The {@link Context}. + * @param mediaDescriptionAdapter The {@link MediaDescriptionAdapter}. + * @param channelId The id of the notification channel. + * @param notificationId The id of the notification. + * @param customActionReceiver The {@link CustomActionReceiver}. + */ + public PlayerNotificationManager( + Context context, + MediaDescriptionAdapter mediaDescriptionAdapter, + String channelId, + int notificationId, + @Nullable CustomActionReceiver customActionReceiver) { + this.context = context.getApplicationContext(); + this.mediaDescriptionAdapter = mediaDescriptionAdapter; + this.channelId = channelId; + this.customActionReceiver = customActionReceiver; + this.notificationId = notificationId; + this.controlDispatcher = new DefaultControlDispatcher(); + mainHandler = new Handler(Looper.getMainLooper()); + notificationManager = NotificationManagerCompat.from(context); + playerListener = new PlayerListener(); + notificationBroadcastReceiver = new NotificationBroadcastReceiver(); + intentFilter = new IntentFilter(); + + // initialize actions + playbackActions = createPlaybackActions(context); + for (String action : playbackActions.keySet()) { + intentFilter.addAction(action); + } + customActions = + customActionReceiver != null + ? customActionReceiver.createCustomActions(context) + : Collections.emptyMap(); + for (String action : customActions.keySet()) { + intentFilter.addAction(action); + } + + setStopAction(ACTION_STOP); + + useNavigationActions = true; + ongoing = true; + colorized = true; + useChronometer = true; + color = Color.TRANSPARENT; + smallIconResourceId = R.drawable.exo_notification_small_icon; + defaults = 0; + fastForwardMs = DEFAULT_FAST_FORWARD_MS; + rewindMs = DEFAULT_REWIND_MS; + setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL); + setVisibility(NotificationCompat.VISIBILITY_PUBLIC); + } + + /** + * Sets the {@link Player}. + * + *

Setting the player starts a notification immediately unless the player is in {@link + * Player#STATE_IDLE}, in which case the notification is started as soon as the player transitions + * away from being idle. + * + *

If the player is released it must be removed from the manager by calling {@code + * setPlayer(null)}. This will cancel the notification. + */ + public final void setPlayer(Player player) { + if (this.player == player) { + return; + } + if (this.player != null) { + this.player.removeListener(playerListener); + if (player == null) { + stopNotification(); + } + } + this.player = player; + if (player != null) { + wasPlayWhenReady = player.getPlayWhenReady(); + lastPlaybackState = player.getPlaybackState(); + player.addListener(playerListener); + if (lastPlaybackState != Player.STATE_IDLE) { + startOrUpdateNotification(); + } + } + } + + /** + * Sets the {@link ControlDispatcher}. + * + * @param controlDispatcher The {@link ControlDispatcher}, or null to use {@link + * DefaultControlDispatcher}. + */ + public final void setControlDispatcher(ControlDispatcher controlDispatcher) { + this.controlDispatcher = + controlDispatcher != null ? controlDispatcher : new DefaultControlDispatcher(); + } + + /** + * Sets the {@link NotificationListener}. + * + * @param notificationListener The {@link NotificationListener}. + */ + public final void setNotificationListener(NotificationListener notificationListener) { + this.notificationListener = notificationListener; + } + + /** + * Sets the fast forward increment in milliseconds. + * + * @param fastForwardMs The fast forward increment in milliseconds. A value of zero will cause the + * fast forward action to be disabled. + */ + public final void setFastForwardIncrementMs(long fastForwardMs) { + if (this.fastForwardMs == fastForwardMs) { + return; + } + this.fastForwardMs = fastForwardMs; + maybeUpdateNotification(); + } + + /** + * Sets the rewind increment in milliseconds. + * + * @param rewindMs The rewind increment in milliseconds. A value of zero will cause the rewind + * action to be disabled. + */ + public final void setRewindIncrementMs(long rewindMs) { + if (this.rewindMs == rewindMs) { + return; + } + this.rewindMs = rewindMs; + maybeUpdateNotification(); + } + + /** + * Sets whether the navigation actions should be used. + * + * @param useNavigationActions Whether to use navigation actions or not. + */ + public final void setUseNavigationActions(boolean useNavigationActions) { + if (this.useNavigationActions != useNavigationActions) { + this.useNavigationActions = useNavigationActions; + maybeUpdateNotification(); + } + } + + /** + * Sets the name of the action to be used as stop action to cancel the notification. If {@code + * null} is passed the stop action is not displayed. + * + * @param stopAction The name of the stop action which must be {@link #ACTION_STOP} or an action + * provided by the {@link CustomActionReceiver}. {@code null} to omit the stop action. + */ + public final void setStopAction(@Nullable String stopAction) { + if ((this.stopAction == null && stopAction == null) + || (this.stopAction != null && this.stopAction.first.equals(stopAction))) { + return; + } + if (stopAction == null) { + this.stopAction = null; + } else if (ACTION_STOP.equals(stopAction)) { + this.stopAction = new Pair<>(stopAction, playbackActions.get(ACTION_STOP)); + } else if (customActions.containsKey(stopAction)) { + this.stopAction = new Pair<>(stopAction, customActions.get(stopAction)); + } else { + throw new IllegalArgumentException(); + } + maybeUpdateNotification(); + } + + /** + * Sets the {@link MediaSessionCompat.Token}. + * + * @param token The {@link MediaSessionCompat.Token}. + */ + public final void setMediaSessionToken(MediaSessionCompat.Token token) { + if (!Util.areEqual(this.mediaSessionToken, token)) { + mediaSessionToken = token; + maybeUpdateNotification(); + } + } + + /** + * Sets the badge icon type of the notification. + * + *

See {@link NotificationCompat.Builder#setBadgeIconType(int)}. + * + * @param badgeIconType The badge icon type. + */ + public final void setBadgeIconType(@NotificationCompat.BadgeIconType int badgeIconType) { + if (this.badgeIconType == badgeIconType) { + return; + } + switch (badgeIconType) { + case NotificationCompat.BADGE_ICON_NONE: + case NotificationCompat.BADGE_ICON_SMALL: + case NotificationCompat.BADGE_ICON_LARGE: + this.badgeIconType = badgeIconType; + break; + default: + throw new IllegalArgumentException(); + } + maybeUpdateNotification(); + } + + /** + * Sets whether the notification should be colorized (whether the notification color should be + * applied). + * + *

See {@link NotificationCompat.Builder#setColorized(boolean)}. + * + * @param colorized Whether to colorize the notification. + */ + public final void setColorized(boolean colorized) { + if (this.colorized != colorized) { + this.colorized = colorized; + maybeUpdateNotification(); + } + } + + /** + * Sets the defaults. + * + *

See {@link NotificationCompat.Builder#setDefaults(int)}. + * + * @param defaults The default notification options. + */ + public final void setDefaults(int defaults) { + if (this.defaults != defaults) { + this.defaults = defaults; + maybeUpdateNotification(); + } + } + + /** + * Sets the color of the notification as a color value like for instance solid {@link + * Color#BLACK)}. When set to {@link Color#TRANSPARENT} and a large icon is provided the + * notification is colored with a color taken from the palette if the icon (when a media session + * token is provided). + * + *

See {@link NotificationCompat.Builder#setColor(int)}. + * + * @param color The color. + */ + public final void setColor(int color) { + if (this.color != color) { + this.color = color; + maybeUpdateNotification(); + } + } + + /** + * Sets whether the notification should be ongoing. If {@code false} the user can dismiss the + * notification by swiping. If in addition the stop action is enabled dismissing the notification + * triggers the stop action. + * + *

See {@link NotificationCompat.Builder#setOngoing(boolean)}. + * + * @param ongoing Whether {@code true} the notification is ongoing and not dismissible. + */ + public final void setOngoing(boolean ongoing) { + if (this.ongoing != ongoing) { + this.ongoing = ongoing; + maybeUpdateNotification(); + } + } + + /** + * Sets the small icon of the notification which is also shown in the system status bar. + * + *

See {@link NotificationCompat.Builder#setSmallIcon(int)}. + * + * @param smallIconResourceId The resource id of the small icon. + */ + public final void setSmallIcon(int smallIconResourceId) { + if (this.smallIconResourceId != smallIconResourceId) { + this.smallIconResourceId = smallIconResourceId; + maybeUpdateNotification(); + } + } + + /** + * Sets whether the elapsed time of the media playback should be displayed + * + *

See {@link NotificationCompat.Builder#setUsesChronometer(boolean)}. + * + * @param useChronometer Whether to use chronometer. + */ + public final void setUseChronometer(boolean useChronometer) { + if (this.useChronometer != useChronometer) { + this.useChronometer = useChronometer; + maybeUpdateNotification(); + } + } + + /** + * Sets the visibility of the notification which determines whether and how the notification is + * shown when the device is in lock screen mode. + * + *

See {@link NotificationCompat.Builder#setVisibility(int)}. + * + * @param visibility The visibility which must be one of {@link + * NotificationCompat#VISIBILITY_PUBLIC}, {@link NotificationCompat#VISIBILITY_PRIVATE} or + * {@link NotificationCompat#VISIBILITY_SECRET}. + */ + public final void setVisibility(@Visibility int visibility) { + if (this.visibility == visibility) { + return; + } + switch (visibility) { + case NotificationCompat.VISIBILITY_PRIVATE: + case NotificationCompat.VISIBILITY_PUBLIC: + case NotificationCompat.VISIBILITY_SECRET: + this.visibility = visibility; + break; + default: + throw new IllegalStateException(); + } + maybeUpdateNotification(); + } + + private Notification updateNotification(Bitmap bitmap) { + Notification notification = createNotification(player, bitmap); + notificationManager.notify(notificationId, notification); + return notification; + } + + private void startOrUpdateNotification() { + Notification notification = updateNotification(null); + if (!isNotificationStarted) { + isNotificationStarted = true; + context.registerReceiver(notificationBroadcastReceiver, intentFilter); + if (notificationListener != null) { + notificationListener.onNotificationStarted(notificationId, notification); + } + } + } + + private void maybeUpdateNotification() { + if (isNotificationStarted) { + updateNotification(null); + } + } + + private void stopNotification() { + if (isNotificationStarted) { + notificationManager.cancel(notificationId); + isNotificationStarted = false; + context.unregisterReceiver(notificationBroadcastReceiver); + if (notificationListener != null) { + notificationListener.onNotificationCancelled(notificationId); + } + } + } + + private Map createPlaybackActions(Context context) { + Map actions = new HashMap<>(); + Intent playIntent = new Intent(ACTION_PLAY).setPackage(context.getPackageName()); + actions.put( + ACTION_PLAY, + new NotificationCompat.Action( + R.drawable.exo_notification_play, + context.getString(R.string.exo_controls_play_description), + PendingIntent.getBroadcast(context, 0, playIntent, PendingIntent.FLAG_CANCEL_CURRENT))); + Intent pauseIntent = new Intent(ACTION_PAUSE).setPackage(context.getPackageName()); + actions.put( + ACTION_PAUSE, + new NotificationCompat.Action( + R.drawable.exo_notification_pause, + context.getString(R.string.exo_controls_pause_description), + PendingIntent.getBroadcast( + context, 0, pauseIntent, PendingIntent.FLAG_CANCEL_CURRENT))); + Intent stopIntent = new Intent(ACTION_STOP).setPackage(context.getPackageName()); + actions.put( + ACTION_STOP, + new NotificationCompat.Action( + R.drawable.exo_notification_stop, + context.getString(R.string.exo_controls_stop_description), + PendingIntent.getBroadcast(context, 0, stopIntent, PendingIntent.FLAG_CANCEL_CURRENT))); + Intent rewindIntent = new Intent(ACTION_REWIND).setPackage(context.getPackageName()); + actions.put( + ACTION_REWIND, + new NotificationCompat.Action( + R.drawable.exo_notification_rewind, + context.getString(R.string.exo_controls_rewind_description), + PendingIntent.getBroadcast( + context, 0, rewindIntent, PendingIntent.FLAG_CANCEL_CURRENT))); + Intent fastForwardIntent = new Intent(ACTION_FAST_FORWARD).setPackage(context.getPackageName()); + actions.put( + ACTION_FAST_FORWARD, + new NotificationCompat.Action( + R.drawable.exo_notification_fastforward, + context.getString(R.string.exo_controls_fastforward_description), + PendingIntent.getBroadcast( + context, 0, fastForwardIntent, PendingIntent.FLAG_CANCEL_CURRENT))); + Intent previousIntent = new Intent(ACTION_PREVIOUS).setPackage(context.getPackageName()); + actions.put( + ACTION_PREVIOUS, + new NotificationCompat.Action( + R.drawable.exo_notification_previous, + context.getString(R.string.exo_controls_previous_description), + PendingIntent.getBroadcast( + context, 0, previousIntent, PendingIntent.FLAG_CANCEL_CURRENT))); + Intent nextIntent = new Intent(ACTION_NEXT).setPackage(context.getPackageName()); + actions.put( + ACTION_NEXT, + new NotificationCompat.Action( + R.drawable.exo_notification_next, + context.getString(R.string.exo_controls_next_description), + PendingIntent.getBroadcast(context, 0, nextIntent, PendingIntent.FLAG_CANCEL_CURRENT))); + return actions; + } + + /** + * Creates the notification given the current player state. + * + * @param player The player for which state to build a notification. + * @param largeIcon The large icon to be used. + * @return The {@link Notification} which has been built. + */ + protected Notification createNotification(Player player, @Nullable Bitmap largeIcon) { + boolean isPlayingAd = player.isPlayingAd(); + NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId); + List actionNames = getActions(player); + for (int i = 0; i < actionNames.size(); i++) { + String actionName = actionNames.get(i); + NotificationCompat.Action action = + playbackActions.containsKey(actionName) + ? playbackActions.get(actionName) + : customActions.get(actionName); + if (action != null) { + builder.addAction(action); + } + } + // Create a media style notification. + MediaStyle mediaStyle = new MediaStyle(); + builder.setStyle(mediaStyle); + if (mediaSessionToken != null) { + mediaStyle.setMediaSession(mediaSessionToken); + } + mediaStyle.setShowActionsInCompactView(getActionIndicesForCompactView(player)); + // Configure stop action (eg. when user dismisses the notification when !isOngoing). + boolean useStopAction = stopAction != null && !isPlayingAd; + mediaStyle.setShowCancelButton(useStopAction); + if (useStopAction) { + PendingIntent stopIntent = stopAction.second.actionIntent; + builder.setDeleteIntent(stopIntent); + mediaStyle.setCancelButtonIntent(stopIntent); + } + // Set notification properties from getters. + builder + .setBadgeIconType(badgeIconType) + .setOngoing(ongoing) + .setColor(color) + .setColorized(colorized) + .setSmallIcon(smallIconResourceId) + .setVisibility(visibility) + .setDefaults(defaults); + if (useChronometer + && !player.isCurrentWindowDynamic() + && player.getPlayWhenReady() + && player.getPlaybackState() == Player.STATE_READY) { + builder + .setWhen(System.currentTimeMillis() - player.getContentPosition()) + .setShowWhen(true) + .setUsesChronometer(true); + } else { + builder.setShowWhen(false).setUsesChronometer(false); + } + // Set media specific notification properties from MediaDescriptionAdapter. + builder.setContentTitle(mediaDescriptionAdapter.getCurrentContentTitle(player)); + builder.setContentText(mediaDescriptionAdapter.getCurrentContentText(player)); + if (largeIcon == null) { + largeIcon = + mediaDescriptionAdapter.getCurrentLargeIcon( + player, new BitmapCallback(++currentNotificationTag)); + } + if (largeIcon != null) { + builder.setLargeIcon(largeIcon); + } + PendingIntent contentIntent = mediaDescriptionAdapter.createCurrentContentIntent(player); + if (contentIntent != null) { + builder.setContentIntent(contentIntent); + } + return builder.build(); + } + + /** + * Gets the names and order of the actions to be included in the notification at the current + * player state. + * + *

The playback and custom actions are combined and placed in the following order if not + * omitted: + * + *

+   *   +------------------------------------------------------------------------+
+   *   | prev | << | play/pause | >> | next | custom actions | stop |
+   *   +------------------------------------------------------------------------+
+   * 
+ * + *

This method can be safely overridden. However, the names must be of the playback actions + * {@link #ACTION_PAUSE}, {@link #ACTION_PLAY}, {@link #ACTION_FAST_FORWARD}, {@link + * #ACTION_REWIND}, {@link #ACTION_NEXT} or {@link #ACTION_PREVIOUS}, or a key contained in the + * map returned by {@link CustomActionReceiver#createCustomActions(Context)}. Otherwise the action + * name is ignored. + */ + protected List getActions(Player player) { + List stringActions = new ArrayList<>(); + if (!player.isPlayingAd()) { + if (useNavigationActions) { + stringActions.add(ACTION_PREVIOUS); + } + if (rewindMs > 0) { + stringActions.add(ACTION_REWIND); + } + if (player.getPlayWhenReady()) { + stringActions.add(ACTION_PAUSE); + } else if (!player.getPlayWhenReady()) { + stringActions.add(ACTION_PLAY); + } + if (fastForwardMs > 0) { + stringActions.add(ACTION_FAST_FORWARD); + } + if (useNavigationActions && player.getNextWindowIndex() != C.INDEX_UNSET) { + stringActions.add(ACTION_NEXT); + } + if (!customActions.isEmpty()) { + stringActions.addAll(customActionReceiver.getCustomActions(player)); + } + if (stopAction != null) { + stringActions.add(stopAction.first); + } + } + return stringActions; + } + + /** + * Gets an array with the indices of the buttons to be shown in compact mode. + * + *

This method can be overridden. The indices must refer to the list of actions returned by + * {@link #getActions(Player)}. + * + * @param player The player for which state to build a notification. + */ + protected int[] getActionIndicesForCompactView(Player player) { + int actionIndex = useNavigationActions ? 1 : 0; + actionIndex += fastForwardMs > 0 ? 1 : 0; + return new int[] {actionIndex}; + } + + private class PlayerListener extends Player.DefaultEventListener { + + @Override + public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { + if ((wasPlayWhenReady != playWhenReady && playbackState != Player.STATE_IDLE) + || lastPlaybackState != playbackState) { + startOrUpdateNotification(); + } + wasPlayWhenReady = playWhenReady; + lastPlaybackState = playbackState; + } + + @Override + public void onTimelineChanged(Timeline timeline, Object manifest, int reason) { + if (player.getPlaybackState() == Player.STATE_IDLE) { + return; + } + startOrUpdateNotification(); + } + + @Override + public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { + if (player.getPlaybackState() == Player.STATE_IDLE) { + return; + } + startOrUpdateNotification(); + } + + @Override + public void onPositionDiscontinuity(int reason) { + startOrUpdateNotification(); + } + + @Override + public void onRepeatModeChanged(int repeatMode) { + if (player.getPlaybackState() == Player.STATE_IDLE) { + return; + } + startOrUpdateNotification(); + } + } + + private class NotificationBroadcastReceiver extends BroadcastReceiver { + + private final Timeline.Window window; + + /** Creates the broadcast receiver. */ + public NotificationBroadcastReceiver() { + window = new Timeline.Window(); + } + + @Override + public void onReceive(Context context, Intent intent) { + if (!isNotificationStarted) { + return; + } + String action = intent.getAction(); + if (ACTION_PLAY.equals(action) || ACTION_PAUSE.equals(action)) { + controlDispatcher.dispatchSetPlayWhenReady(player, ACTION_PLAY.equals(action)); + } else if (ACTION_FAST_FORWARD.equals(action) || ACTION_REWIND.equals(action)) { + long increment = ACTION_FAST_FORWARD.equals(action) ? fastForwardMs : -rewindMs; + controlDispatcher.dispatchSeekTo( + player, player.getCurrentWindowIndex(), player.getCurrentPosition() + increment); + } else if (ACTION_NEXT.equals(action)) { + int nextWindowIndex = player.getNextWindowIndex(); + if (nextWindowIndex != C.INDEX_UNSET) { + controlDispatcher.dispatchSeekTo(player, nextWindowIndex, C.TIME_UNSET); + } + } else if (ACTION_PREVIOUS.equals(action)) { + player.getCurrentTimeline().getWindow(player.getCurrentWindowIndex(), window); + int previousWindowIndex = player.getPreviousWindowIndex(); + if (previousWindowIndex != C.INDEX_UNSET + && (player.getCurrentPosition() <= MAX_POSITION_FOR_SEEK_TO_PREVIOUS + || (window.isDynamic && !window.isSeekable))) { + controlDispatcher.dispatchSeekTo(player, previousWindowIndex, C.TIME_UNSET); + } else { + controlDispatcher.dispatchSeekTo(player, player.getCurrentWindowIndex(), C.TIME_UNSET); + } + } else if (ACTION_STOP.equals(action)) { + controlDispatcher.dispatchStop(player, true); + stopNotification(); + } else if (customActions.containsKey(action)) { + customActionReceiver.onCustomAction(player, action, intent); + } + } + } +} diff --git a/library/ui/src/main/res/drawable-anydpi-v21/exo_controls_fastforward.xml b/library/ui/src/main/res/drawable-anydpi-v21/exo_icon_fastforward.xml similarity index 100% rename from library/ui/src/main/res/drawable-anydpi-v21/exo_controls_fastforward.xml rename to library/ui/src/main/res/drawable-anydpi-v21/exo_icon_fastforward.xml diff --git a/library/ui/src/main/res/drawable-anydpi-v21/exo_controls_next.xml b/library/ui/src/main/res/drawable-anydpi-v21/exo_icon_next.xml similarity index 100% rename from library/ui/src/main/res/drawable-anydpi-v21/exo_controls_next.xml rename to library/ui/src/main/res/drawable-anydpi-v21/exo_icon_next.xml diff --git a/library/ui/src/main/res/drawable-anydpi-v21/exo_controls_pause.xml b/library/ui/src/main/res/drawable-anydpi-v21/exo_icon_pause.xml similarity index 100% rename from library/ui/src/main/res/drawable-anydpi-v21/exo_controls_pause.xml rename to library/ui/src/main/res/drawable-anydpi-v21/exo_icon_pause.xml diff --git a/library/ui/src/main/res/drawable-anydpi-v21/exo_controls_play.xml b/library/ui/src/main/res/drawable-anydpi-v21/exo_icon_play.xml similarity index 100% rename from library/ui/src/main/res/drawable-anydpi-v21/exo_controls_play.xml rename to library/ui/src/main/res/drawable-anydpi-v21/exo_icon_play.xml diff --git a/library/ui/src/main/res/drawable-anydpi-v21/exo_controls_previous.xml b/library/ui/src/main/res/drawable-anydpi-v21/exo_icon_previous.xml similarity index 100% rename from library/ui/src/main/res/drawable-anydpi-v21/exo_controls_previous.xml rename to library/ui/src/main/res/drawable-anydpi-v21/exo_icon_previous.xml diff --git a/library/ui/src/main/res/drawable-anydpi-v21/exo_controls_rewind.xml b/library/ui/src/main/res/drawable-anydpi-v21/exo_icon_rewind.xml similarity index 100% rename from library/ui/src/main/res/drawable-anydpi-v21/exo_controls_rewind.xml rename to library/ui/src/main/res/drawable-anydpi-v21/exo_icon_rewind.xml diff --git a/library/ui/src/main/res/drawable-anydpi-v21/exo_icon_stop.xml b/library/ui/src/main/res/drawable-anydpi-v21/exo_icon_stop.xml new file mode 100644 index 0000000000..2e1e40cbb5 --- /dev/null +++ b/library/ui/src/main/res/drawable-anydpi-v21/exo_icon_stop.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/library/ui/src/main/res/drawable-hdpi/exo_controls_fastforward.png b/library/ui/src/main/res/drawable-hdpi/exo_controls_fastforward.png deleted file mode 100644 index 843df84091..0000000000 Binary files a/library/ui/src/main/res/drawable-hdpi/exo_controls_fastforward.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-hdpi/exo_controls_next.png b/library/ui/src/main/res/drawable-hdpi/exo_controls_next.png deleted file mode 100644 index c37541472e..0000000000 Binary files a/library/ui/src/main/res/drawable-hdpi/exo_controls_next.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-hdpi/exo_controls_pause.png b/library/ui/src/main/res/drawable-hdpi/exo_controls_pause.png deleted file mode 100644 index 0a23452746..0000000000 Binary files a/library/ui/src/main/res/drawable-hdpi/exo_controls_pause.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-hdpi/exo_controls_play.png b/library/ui/src/main/res/drawable-hdpi/exo_controls_play.png deleted file mode 100644 index e98e2b9cbe..0000000000 Binary files a/library/ui/src/main/res/drawable-hdpi/exo_controls_play.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-hdpi/exo_controls_previous.png b/library/ui/src/main/res/drawable-hdpi/exo_controls_previous.png deleted file mode 100644 index 3eae5c883b..0000000000 Binary files a/library/ui/src/main/res/drawable-hdpi/exo_controls_previous.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-hdpi/exo_controls_rewind.png b/library/ui/src/main/res/drawable-hdpi/exo_controls_rewind.png deleted file mode 100644 index 36537d3b73..0000000000 Binary files a/library/ui/src/main/res/drawable-hdpi/exo_controls_rewind.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-hdpi/exo_icon_fastforward.png b/library/ui/src/main/res/drawable-hdpi/exo_icon_fastforward.png new file mode 100644 index 0000000000..5699614c6b Binary files /dev/null and b/library/ui/src/main/res/drawable-hdpi/exo_icon_fastforward.png differ diff --git a/library/ui/src/main/res/drawable-hdpi/exo_icon_next.png b/library/ui/src/main/res/drawable-hdpi/exo_icon_next.png new file mode 100644 index 0000000000..303e896187 Binary files /dev/null and b/library/ui/src/main/res/drawable-hdpi/exo_icon_next.png differ diff --git a/library/ui/src/main/res/drawable-hdpi/exo_icon_pause.png b/library/ui/src/main/res/drawable-hdpi/exo_icon_pause.png new file mode 100644 index 0000000000..f49aed7571 Binary files /dev/null and b/library/ui/src/main/res/drawable-hdpi/exo_icon_pause.png differ diff --git a/library/ui/src/main/res/drawable-hdpi/exo_icon_play.png b/library/ui/src/main/res/drawable-hdpi/exo_icon_play.png new file mode 100644 index 0000000000..5a3e037ae9 Binary files /dev/null and b/library/ui/src/main/res/drawable-hdpi/exo_icon_play.png differ diff --git a/library/ui/src/main/res/drawable-hdpi/exo_icon_previous.png b/library/ui/src/main/res/drawable-hdpi/exo_icon_previous.png new file mode 100644 index 0000000000..2c3b3af982 Binary files /dev/null and b/library/ui/src/main/res/drawable-hdpi/exo_icon_previous.png differ diff --git a/library/ui/src/main/res/drawable-hdpi/exo_icon_rewind.png b/library/ui/src/main/res/drawable-hdpi/exo_icon_rewind.png new file mode 100644 index 0000000000..d9e279231a Binary files /dev/null and b/library/ui/src/main/res/drawable-hdpi/exo_icon_rewind.png differ diff --git a/library/ui/src/main/res/drawable-hdpi/exo_icon_stop.png b/library/ui/src/main/res/drawable-hdpi/exo_icon_stop.png new file mode 100644 index 0000000000..3ad2c9c4e3 Binary files /dev/null and b/library/ui/src/main/res/drawable-hdpi/exo_icon_stop.png differ diff --git a/library/ui/src/main/res/drawable-hdpi/exo_notification_small_icon.png b/library/ui/src/main/res/drawable-hdpi/exo_notification_small_icon.png new file mode 100644 index 0000000000..ecf3df3cb1 Binary files /dev/null and b/library/ui/src/main/res/drawable-hdpi/exo_notification_small_icon.png differ diff --git a/library/ui/src/main/res/drawable-ldpi/exo_controls_fastforward.png b/library/ui/src/main/res/drawable-ldpi/exo_controls_fastforward.png deleted file mode 100644 index 19b9e6015c..0000000000 Binary files a/library/ui/src/main/res/drawable-ldpi/exo_controls_fastforward.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-ldpi/exo_controls_next.png b/library/ui/src/main/res/drawable-ldpi/exo_controls_next.png deleted file mode 100644 index d4872037aa..0000000000 Binary files a/library/ui/src/main/res/drawable-ldpi/exo_controls_next.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-ldpi/exo_controls_pause.png b/library/ui/src/main/res/drawable-ldpi/exo_controls_pause.png deleted file mode 100644 index 616ec42f39..0000000000 Binary files a/library/ui/src/main/res/drawable-ldpi/exo_controls_pause.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-ldpi/exo_controls_play.png b/library/ui/src/main/res/drawable-ldpi/exo_controls_play.png deleted file mode 100644 index 5d1c702892..0000000000 Binary files a/library/ui/src/main/res/drawable-ldpi/exo_controls_play.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-ldpi/exo_controls_previous.png b/library/ui/src/main/res/drawable-ldpi/exo_controls_previous.png deleted file mode 100644 index 930534d312..0000000000 Binary files a/library/ui/src/main/res/drawable-ldpi/exo_controls_previous.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-ldpi/exo_controls_rewind.png b/library/ui/src/main/res/drawable-ldpi/exo_controls_rewind.png deleted file mode 100644 index 83d71782f6..0000000000 Binary files a/library/ui/src/main/res/drawable-ldpi/exo_controls_rewind.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-ldpi/exo_icon_fastforward.png b/library/ui/src/main/res/drawable-ldpi/exo_icon_fastforward.png new file mode 100644 index 0000000000..e63921abe6 Binary files /dev/null and b/library/ui/src/main/res/drawable-ldpi/exo_icon_fastforward.png differ diff --git a/library/ui/src/main/res/drawable-ldpi/exo_icon_next.png b/library/ui/src/main/res/drawable-ldpi/exo_icon_next.png new file mode 100644 index 0000000000..78f9bed762 Binary files /dev/null and b/library/ui/src/main/res/drawable-ldpi/exo_icon_next.png differ diff --git a/library/ui/src/main/res/drawable-ldpi/exo_icon_pause.png b/library/ui/src/main/res/drawable-ldpi/exo_icon_pause.png new file mode 100644 index 0000000000..1818039e51 Binary files /dev/null and b/library/ui/src/main/res/drawable-ldpi/exo_icon_pause.png differ diff --git a/library/ui/src/main/res/drawable-ldpi/exo_icon_play.png b/library/ui/src/main/res/drawable-ldpi/exo_icon_play.png new file mode 100644 index 0000000000..f0b0570d0b Binary files /dev/null and b/library/ui/src/main/res/drawable-ldpi/exo_icon_play.png differ diff --git a/library/ui/src/main/res/drawable-ldpi/exo_icon_previous.png b/library/ui/src/main/res/drawable-ldpi/exo_icon_previous.png new file mode 100644 index 0000000000..4d2eccfe9a Binary files /dev/null and b/library/ui/src/main/res/drawable-ldpi/exo_icon_previous.png differ diff --git a/library/ui/src/main/res/drawable-ldpi/exo_icon_rewind.png b/library/ui/src/main/res/drawable-ldpi/exo_icon_rewind.png new file mode 100644 index 0000000000..8cd1daa810 Binary files /dev/null and b/library/ui/src/main/res/drawable-ldpi/exo_icon_rewind.png differ diff --git a/library/ui/src/main/res/drawable-ldpi/exo_icon_stop.png b/library/ui/src/main/res/drawable-ldpi/exo_icon_stop.png new file mode 100644 index 0000000000..836f4dbb55 Binary files /dev/null and b/library/ui/src/main/res/drawable-ldpi/exo_icon_stop.png differ diff --git a/library/ui/src/main/res/drawable-ldpi/exo_notification_small_icon.png b/library/ui/src/main/res/drawable-ldpi/exo_notification_small_icon.png new file mode 100644 index 0000000000..e5104fcd62 Binary files /dev/null and b/library/ui/src/main/res/drawable-ldpi/exo_notification_small_icon.png differ diff --git a/library/ui/src/main/res/drawable-mdpi/exo_controls_fastforward.png b/library/ui/src/main/res/drawable-mdpi/exo_controls_fastforward.png deleted file mode 100644 index ee3efe1d69..0000000000 Binary files a/library/ui/src/main/res/drawable-mdpi/exo_controls_fastforward.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-mdpi/exo_controls_next.png b/library/ui/src/main/res/drawable-mdpi/exo_controls_next.png deleted file mode 100644 index 9d4d7469ed..0000000000 Binary files a/library/ui/src/main/res/drawable-mdpi/exo_controls_next.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-mdpi/exo_controls_pause.png b/library/ui/src/main/res/drawable-mdpi/exo_controls_pause.png deleted file mode 100644 index f54c942201..0000000000 Binary files a/library/ui/src/main/res/drawable-mdpi/exo_controls_pause.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-mdpi/exo_controls_play.png b/library/ui/src/main/res/drawable-mdpi/exo_controls_play.png deleted file mode 100644 index dd0c142859..0000000000 Binary files a/library/ui/src/main/res/drawable-mdpi/exo_controls_play.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-mdpi/exo_controls_previous.png b/library/ui/src/main/res/drawable-mdpi/exo_controls_previous.png deleted file mode 100644 index 950e213d2f..0000000000 Binary files a/library/ui/src/main/res/drawable-mdpi/exo_controls_previous.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-mdpi/exo_controls_rewind.png b/library/ui/src/main/res/drawable-mdpi/exo_controls_rewind.png deleted file mode 100644 index e75efae189..0000000000 Binary files a/library/ui/src/main/res/drawable-mdpi/exo_controls_rewind.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-mdpi/exo_icon_fastforward.png b/library/ui/src/main/res/drawable-mdpi/exo_icon_fastforward.png new file mode 100644 index 0000000000..1b42a5315f Binary files /dev/null and b/library/ui/src/main/res/drawable-mdpi/exo_icon_fastforward.png differ diff --git a/library/ui/src/main/res/drawable-mdpi/exo_icon_next.png b/library/ui/src/main/res/drawable-mdpi/exo_icon_next.png new file mode 100644 index 0000000000..a93aae0f34 Binary files /dev/null and b/library/ui/src/main/res/drawable-mdpi/exo_icon_next.png differ diff --git a/library/ui/src/main/res/drawable-mdpi/exo_icon_pause.png b/library/ui/src/main/res/drawable-mdpi/exo_icon_pause.png new file mode 100644 index 0000000000..3e150b5a45 Binary files /dev/null and b/library/ui/src/main/res/drawable-mdpi/exo_icon_pause.png differ diff --git a/library/ui/src/main/res/drawable-mdpi/exo_icon_play.png b/library/ui/src/main/res/drawable-mdpi/exo_icon_play.png new file mode 100644 index 0000000000..692d8c2ad9 Binary files /dev/null and b/library/ui/src/main/res/drawable-mdpi/exo_icon_play.png differ diff --git a/library/ui/src/main/res/drawable-mdpi/exo_icon_previous.png b/library/ui/src/main/res/drawable-mdpi/exo_icon_previous.png new file mode 100644 index 0000000000..ea83907d8e Binary files /dev/null and b/library/ui/src/main/res/drawable-mdpi/exo_icon_previous.png differ diff --git a/library/ui/src/main/res/drawable-mdpi/exo_icon_rewind.png b/library/ui/src/main/res/drawable-mdpi/exo_icon_rewind.png new file mode 100644 index 0000000000..231bcee4ca Binary files /dev/null and b/library/ui/src/main/res/drawable-mdpi/exo_icon_rewind.png differ diff --git a/library/ui/src/main/res/drawable-mdpi/exo_icon_stop.png b/library/ui/src/main/res/drawable-mdpi/exo_icon_stop.png new file mode 100644 index 0000000000..2aeffbb6cf Binary files /dev/null and b/library/ui/src/main/res/drawable-mdpi/exo_icon_stop.png differ diff --git a/library/ui/src/main/res/drawable-mdpi/exo_notification_small_icon.png b/library/ui/src/main/res/drawable-mdpi/exo_notification_small_icon.png new file mode 100644 index 0000000000..7242e1f5c8 Binary files /dev/null and b/library/ui/src/main/res/drawable-mdpi/exo_notification_small_icon.png differ diff --git a/library/ui/src/main/res/drawable-xhdpi/exo_controls_fastforward.png b/library/ui/src/main/res/drawable-xhdpi/exo_controls_fastforward.png deleted file mode 100644 index ead712cfe9..0000000000 Binary files a/library/ui/src/main/res/drawable-xhdpi/exo_controls_fastforward.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-xhdpi/exo_controls_next.png b/library/ui/src/main/res/drawable-xhdpi/exo_controls_next.png deleted file mode 100644 index bc1ebf83c5..0000000000 Binary files a/library/ui/src/main/res/drawable-xhdpi/exo_controls_next.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-xhdpi/exo_controls_play.png b/library/ui/src/main/res/drawable-xhdpi/exo_controls_play.png deleted file mode 100644 index f2f934413e..0000000000 Binary files a/library/ui/src/main/res/drawable-xhdpi/exo_controls_play.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-xhdpi/exo_controls_previous.png b/library/ui/src/main/res/drawable-xhdpi/exo_controls_previous.png deleted file mode 100644 index d197eff873..0000000000 Binary files a/library/ui/src/main/res/drawable-xhdpi/exo_controls_previous.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-xhdpi/exo_controls_rewind.png b/library/ui/src/main/res/drawable-xhdpi/exo_controls_rewind.png deleted file mode 100644 index 3340ef9bd2..0000000000 Binary files a/library/ui/src/main/res/drawable-xhdpi/exo_controls_rewind.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-xhdpi/exo_icon_fastforward.png b/library/ui/src/main/res/drawable-xhdpi/exo_icon_fastforward.png new file mode 100644 index 0000000000..ab7e1fd334 Binary files /dev/null and b/library/ui/src/main/res/drawable-xhdpi/exo_icon_fastforward.png differ diff --git a/library/ui/src/main/res/drawable-xhdpi/exo_icon_next.png b/library/ui/src/main/res/drawable-xhdpi/exo_icon_next.png new file mode 100644 index 0000000000..f3552d7216 Binary files /dev/null and b/library/ui/src/main/res/drawable-xhdpi/exo_icon_next.png differ diff --git a/library/ui/src/main/res/drawable-xhdpi/exo_controls_pause.png b/library/ui/src/main/res/drawable-xhdpi/exo_icon_pause.png similarity index 100% rename from library/ui/src/main/res/drawable-xhdpi/exo_controls_pause.png rename to library/ui/src/main/res/drawable-xhdpi/exo_icon_pause.png diff --git a/library/ui/src/main/res/drawable-xhdpi/exo_icon_play.png b/library/ui/src/main/res/drawable-xhdpi/exo_icon_play.png new file mode 100644 index 0000000000..381eabdccf Binary files /dev/null and b/library/ui/src/main/res/drawable-xhdpi/exo_icon_play.png differ diff --git a/library/ui/src/main/res/drawable-xhdpi/exo_icon_previous.png b/library/ui/src/main/res/drawable-xhdpi/exo_icon_previous.png new file mode 100644 index 0000000000..0a2ddd5e90 Binary files /dev/null and b/library/ui/src/main/res/drawable-xhdpi/exo_icon_previous.png differ diff --git a/library/ui/src/main/res/drawable-xhdpi/exo_icon_rewind.png b/library/ui/src/main/res/drawable-xhdpi/exo_icon_rewind.png new file mode 100644 index 0000000000..a798fee30e Binary files /dev/null and b/library/ui/src/main/res/drawable-xhdpi/exo_icon_rewind.png differ diff --git a/library/ui/src/main/res/drawable-xhdpi/exo_icon_stop.png b/library/ui/src/main/res/drawable-xhdpi/exo_icon_stop.png new file mode 100644 index 0000000000..8727a93480 Binary files /dev/null and b/library/ui/src/main/res/drawable-xhdpi/exo_icon_stop.png differ diff --git a/library/ui/src/main/res/drawable-xhdpi/exo_notification_small_icon.png b/library/ui/src/main/res/drawable-xhdpi/exo_notification_small_icon.png new file mode 100644 index 0000000000..dd31d608d4 Binary files /dev/null and b/library/ui/src/main/res/drawable-xhdpi/exo_notification_small_icon.png differ diff --git a/library/ui/src/main/res/drawable-xxhdpi/exo_controls_fastforward.png b/library/ui/src/main/res/drawable-xxhdpi/exo_controls_fastforward.png deleted file mode 100644 index e1c6cae292..0000000000 Binary files a/library/ui/src/main/res/drawable-xxhdpi/exo_controls_fastforward.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-xxhdpi/exo_controls_next.png b/library/ui/src/main/res/drawable-xxhdpi/exo_controls_next.png deleted file mode 100644 index 232f09e910..0000000000 Binary files a/library/ui/src/main/res/drawable-xxhdpi/exo_controls_next.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-xxhdpi/exo_controls_pause.png b/library/ui/src/main/res/drawable-xxhdpi/exo_controls_pause.png deleted file mode 100644 index 50a545db4d..0000000000 Binary files a/library/ui/src/main/res/drawable-xxhdpi/exo_controls_pause.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-xxhdpi/exo_controls_play.png b/library/ui/src/main/res/drawable-xxhdpi/exo_controls_play.png deleted file mode 100644 index 08508c5015..0000000000 Binary files a/library/ui/src/main/res/drawable-xxhdpi/exo_controls_play.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-xxhdpi/exo_controls_previous.png b/library/ui/src/main/res/drawable-xxhdpi/exo_controls_previous.png deleted file mode 100644 index f71acc4875..0000000000 Binary files a/library/ui/src/main/res/drawable-xxhdpi/exo_controls_previous.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-xxhdpi/exo_controls_rewind.png b/library/ui/src/main/res/drawable-xxhdpi/exo_controls_rewind.png deleted file mode 100644 index db0555f9e5..0000000000 Binary files a/library/ui/src/main/res/drawable-xxhdpi/exo_controls_rewind.png and /dev/null differ diff --git a/library/ui/src/main/res/drawable-xxhdpi/exo_icon_fastforward.png b/library/ui/src/main/res/drawable-xxhdpi/exo_icon_fastforward.png new file mode 100644 index 0000000000..1e8db0ec23 Binary files /dev/null and b/library/ui/src/main/res/drawable-xxhdpi/exo_icon_fastforward.png differ diff --git a/library/ui/src/main/res/drawable-xxhdpi/exo_icon_next.png b/library/ui/src/main/res/drawable-xxhdpi/exo_icon_next.png new file mode 100644 index 0000000000..131a531b37 Binary files /dev/null and b/library/ui/src/main/res/drawable-xxhdpi/exo_icon_next.png differ diff --git a/library/ui/src/main/res/drawable-xxhdpi/exo_icon_pause.png b/library/ui/src/main/res/drawable-xxhdpi/exo_icon_pause.png new file mode 100644 index 0000000000..ac8d4fcad5 Binary files /dev/null and b/library/ui/src/main/res/drawable-xxhdpi/exo_icon_pause.png differ diff --git a/library/ui/src/main/res/drawable-xxhdpi/exo_icon_play.png b/library/ui/src/main/res/drawable-xxhdpi/exo_icon_play.png new file mode 100644 index 0000000000..365b3dfee5 Binary files /dev/null and b/library/ui/src/main/res/drawable-xxhdpi/exo_icon_play.png differ diff --git a/library/ui/src/main/res/drawable-xxhdpi/exo_icon_previous.png b/library/ui/src/main/res/drawable-xxhdpi/exo_icon_previous.png new file mode 100644 index 0000000000..884cbdd407 Binary files /dev/null and b/library/ui/src/main/res/drawable-xxhdpi/exo_icon_previous.png differ diff --git a/library/ui/src/main/res/drawable-xxhdpi/exo_icon_rewind.png b/library/ui/src/main/res/drawable-xxhdpi/exo_icon_rewind.png new file mode 100644 index 0000000000..4bab545714 Binary files /dev/null and b/library/ui/src/main/res/drawable-xxhdpi/exo_icon_rewind.png differ diff --git a/library/ui/src/main/res/drawable-xxhdpi/exo_icon_stop.png b/library/ui/src/main/res/drawable-xxhdpi/exo_icon_stop.png new file mode 100644 index 0000000000..5239336671 Binary files /dev/null and b/library/ui/src/main/res/drawable-xxhdpi/exo_icon_stop.png differ diff --git a/library/ui/src/main/res/drawable-xxhdpi/exo_notification_small_icon.png b/library/ui/src/main/res/drawable-xxhdpi/exo_notification_small_icon.png new file mode 100644 index 0000000000..56ed071b2f Binary files /dev/null and b/library/ui/src/main/res/drawable-xxhdpi/exo_notification_small_icon.png differ diff --git a/library/ui/src/main/res/drawable-xxxhdpi/exo_notification_small_icon.png b/library/ui/src/main/res/drawable-xxxhdpi/exo_notification_small_icon.png new file mode 100644 index 0000000000..eabf12f38a Binary files /dev/null and b/library/ui/src/main/res/drawable-xxxhdpi/exo_notification_small_icon.png differ diff --git a/library/ui/src/main/res/values/drawables.xml b/library/ui/src/main/res/values/drawables.xml new file mode 100644 index 0000000000..b528c9cc9a --- /dev/null +++ b/library/ui/src/main/res/values/drawables.xml @@ -0,0 +1,16 @@ + + + @drawable/exo_icon_play + @drawable/exo_icon_pause + @drawable/exo_icon_next + @drawable/exo_icon_previous + @drawable/exo_icon_fastforward + @drawable/exo_icon_rewind + @drawable/exo_icon_play + @drawable/exo_icon_pause + @drawable/exo_icon_next + @drawable/exo_icon_previous + @drawable/exo_icon_fastforward + @drawable/exo_icon_rewind + @drawable/exo_icon_stop +