From afd11de57dcc7ec01c9c42401ae28234e717b4d3 Mon Sep 17 00:00:00 2001 From: bachinger Date: Thu, 8 Mar 2018 10:31:27 -0800 Subject: [PATCH] add a PlayerNotificationManager to post media style notifications. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=188352116 --- .gitignore | 4 + RELEASENOTES.md | 1 + .../android/exoplayer2/ControlDispatcher.java | 8 + .../exoplayer2/DefaultControlDispatcher.java | 5 + library/ui/build.gradle | 1 + .../ui/PlayerNotificationManager.java | 931 ++++++++++++++++++ ...stforward.xml => exo_icon_fastforward.xml} | 0 ...xo_controls_next.xml => exo_icon_next.xml} | 0 ..._controls_pause.xml => exo_icon_pause.xml} | 0 ...xo_controls_play.xml => exo_icon_play.xml} | 0 ...ols_previous.xml => exo_icon_previous.xml} | 0 ...ontrols_rewind.xml => exo_icon_rewind.xml} | 0 .../res/drawable-anydpi-v21/exo_icon_stop.xml | 13 + .../exo_controls_fastforward.png | Bin 354 -> 0 bytes .../res/drawable-hdpi/exo_controls_next.png | Bin 323 -> 0 bytes .../res/drawable-hdpi/exo_controls_pause.png | Bin 108 -> 0 bytes .../res/drawable-hdpi/exo_controls_play.png | Bin 286 -> 0 bytes .../drawable-hdpi/exo_controls_previous.png | Bin 292 -> 0 bytes .../res/drawable-hdpi/exo_controls_rewind.png | Bin 347 -> 0 bytes .../drawable-hdpi/exo_icon_fastforward.png | Bin 0 -> 283 bytes .../main/res/drawable-hdpi/exo_icon_next.png | Bin 0 -> 264 bytes .../main/res/drawable-hdpi/exo_icon_pause.png | Bin 0 -> 90 bytes .../main/res/drawable-hdpi/exo_icon_play.png | Bin 0 -> 233 bytes .../res/drawable-hdpi/exo_icon_previous.png | Bin 0 -> 255 bytes .../res/drawable-hdpi/exo_icon_rewind.png | Bin 0 -> 332 bytes .../main/res/drawable-hdpi/exo_icon_stop.png | Bin 0 -> 90 bytes .../exo_notification_small_icon.png | Bin 0 -> 995 bytes .../exo_controls_fastforward.png | Bin 192 -> 0 bytes .../res/drawable-ldpi/exo_controls_next.png | Bin 167 -> 0 bytes .../res/drawable-ldpi/exo_controls_pause.png | Bin 91 -> 0 bytes .../res/drawable-ldpi/exo_controls_play.png | Bin 182 -> 0 bytes .../drawable-ldpi/exo_controls_previous.png | Bin 187 -> 0 bytes .../res/drawable-ldpi/exo_controls_rewind.png | Bin 214 -> 0 bytes .../drawable-ldpi/exo_icon_fastforward.png | Bin 0 -> 173 bytes .../main/res/drawable-ldpi/exo_icon_next.png | Bin 0 -> 152 bytes .../main/res/drawable-ldpi/exo_icon_pause.png | Bin 0 -> 88 bytes .../main/res/drawable-ldpi/exo_icon_play.png | Bin 0 -> 167 bytes .../res/drawable-ldpi/exo_icon_previous.png | Bin 0 -> 161 bytes .../res/drawable-ldpi/exo_icon_rewind.png | Bin 0 -> 186 bytes .../main/res/drawable-ldpi/exo_icon_stop.png | Bin 0 -> 88 bytes .../exo_notification_small_icon.png | Bin 0 -> 457 bytes .../exo_controls_fastforward.png | Bin 255 -> 0 bytes .../res/drawable-mdpi/exo_controls_next.png | Bin 276 -> 0 bytes .../res/drawable-mdpi/exo_controls_pause.png | Bin 153 -> 0 bytes .../res/drawable-mdpi/exo_controls_play.png | Bin 228 -> 0 bytes .../drawable-mdpi/exo_controls_previous.png | Bin 227 -> 0 bytes .../res/drawable-mdpi/exo_controls_rewind.png | Bin 273 -> 0 bytes .../drawable-mdpi/exo_icon_fastforward.png | Bin 0 -> 217 bytes .../main/res/drawable-mdpi/exo_icon_next.png | Bin 0 -> 209 bytes .../main/res/drawable-mdpi/exo_icon_pause.png | Bin 0 -> 140 bytes .../main/res/drawable-mdpi/exo_icon_play.png | Bin 0 -> 200 bytes .../res/drawable-mdpi/exo_icon_previous.png | Bin 0 -> 180 bytes .../res/drawable-mdpi/exo_icon_rewind.png | Bin 0 -> 217 bytes .../main/res/drawable-mdpi/exo_icon_stop.png | Bin 0 -> 97 bytes .../exo_notification_small_icon.png | Bin 0 -> 672 bytes .../exo_controls_fastforward.png | Bin 392 -> 0 bytes .../res/drawable-xhdpi/exo_controls_next.png | Bin 334 -> 0 bytes .../res/drawable-xhdpi/exo_controls_play.png | Bin 343 -> 0 bytes .../drawable-xhdpi/exo_controls_previous.png | Bin 339 -> 0 bytes .../drawable-xhdpi/exo_controls_rewind.png | Bin 400 -> 0 bytes .../drawable-xhdpi/exo_icon_fastforward.png | Bin 0 -> 386 bytes .../main/res/drawable-xhdpi/exo_icon_next.png | Bin 0 -> 269 bytes ..._controls_pause.png => exo_icon_pause.png} | Bin .../main/res/drawable-xhdpi/exo_icon_play.png | Bin 0 -> 270 bytes .../res/drawable-xhdpi/exo_icon_previous.png | Bin 0 -> 328 bytes .../res/drawable-xhdpi/exo_icon_rewind.png | Bin 0 -> 391 bytes .../main/res/drawable-xhdpi/exo_icon_stop.png | Bin 0 -> 91 bytes .../exo_notification_small_icon.png | Bin 0 -> 1322 bytes .../exo_controls_fastforward.png | Bin 584 -> 0 bytes .../res/drawable-xxhdpi/exo_controls_next.png | Bin 391 -> 0 bytes .../drawable-xxhdpi/exo_controls_pause.png | Bin 113 -> 0 bytes .../res/drawable-xxhdpi/exo_controls_play.png | Bin 384 -> 0 bytes .../drawable-xxhdpi/exo_controls_previous.png | Bin 464 -> 0 bytes .../drawable-xxhdpi/exo_controls_rewind.png | Bin 571 -> 0 bytes .../drawable-xxhdpi/exo_icon_fastforward.png | Bin 0 -> 575 bytes .../res/drawable-xxhdpi/exo_icon_next.png | Bin 0 -> 385 bytes .../res/drawable-xxhdpi/exo_icon_pause.png | Bin 0 -> 111 bytes .../res/drawable-xxhdpi/exo_icon_play.png | Bin 0 -> 378 bytes .../res/drawable-xxhdpi/exo_icon_previous.png | Bin 0 -> 458 bytes .../res/drawable-xxhdpi/exo_icon_rewind.png | Bin 0 -> 561 bytes .../res/drawable-xxhdpi/exo_icon_stop.png | Bin 0 -> 95 bytes .../exo_notification_small_icon.png | Bin 0 -> 2020 bytes .../exo_notification_small_icon.png | Bin 0 -> 2683 bytes library/ui/src/main/res/values/drawables.xml | 16 + 84 files changed, 979 insertions(+) create mode 100644 library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerNotificationManager.java rename library/ui/src/main/res/drawable-anydpi-v21/{exo_controls_fastforward.xml => exo_icon_fastforward.xml} (100%) rename library/ui/src/main/res/drawable-anydpi-v21/{exo_controls_next.xml => exo_icon_next.xml} (100%) rename library/ui/src/main/res/drawable-anydpi-v21/{exo_controls_pause.xml => exo_icon_pause.xml} (100%) rename library/ui/src/main/res/drawable-anydpi-v21/{exo_controls_play.xml => exo_icon_play.xml} (100%) rename library/ui/src/main/res/drawable-anydpi-v21/{exo_controls_previous.xml => exo_icon_previous.xml} (100%) rename library/ui/src/main/res/drawable-anydpi-v21/{exo_controls_rewind.xml => exo_icon_rewind.xml} (100%) create mode 100644 library/ui/src/main/res/drawable-anydpi-v21/exo_icon_stop.xml delete mode 100644 library/ui/src/main/res/drawable-hdpi/exo_controls_fastforward.png delete mode 100644 library/ui/src/main/res/drawable-hdpi/exo_controls_next.png delete mode 100644 library/ui/src/main/res/drawable-hdpi/exo_controls_pause.png delete mode 100644 library/ui/src/main/res/drawable-hdpi/exo_controls_play.png delete mode 100644 library/ui/src/main/res/drawable-hdpi/exo_controls_previous.png delete mode 100644 library/ui/src/main/res/drawable-hdpi/exo_controls_rewind.png create mode 100644 library/ui/src/main/res/drawable-hdpi/exo_icon_fastforward.png create mode 100644 library/ui/src/main/res/drawable-hdpi/exo_icon_next.png create mode 100644 library/ui/src/main/res/drawable-hdpi/exo_icon_pause.png create mode 100644 library/ui/src/main/res/drawable-hdpi/exo_icon_play.png create mode 100644 library/ui/src/main/res/drawable-hdpi/exo_icon_previous.png create mode 100644 library/ui/src/main/res/drawable-hdpi/exo_icon_rewind.png create mode 100644 library/ui/src/main/res/drawable-hdpi/exo_icon_stop.png create mode 100644 library/ui/src/main/res/drawable-hdpi/exo_notification_small_icon.png delete mode 100644 library/ui/src/main/res/drawable-ldpi/exo_controls_fastforward.png delete mode 100644 library/ui/src/main/res/drawable-ldpi/exo_controls_next.png delete mode 100644 library/ui/src/main/res/drawable-ldpi/exo_controls_pause.png delete mode 100644 library/ui/src/main/res/drawable-ldpi/exo_controls_play.png delete mode 100644 library/ui/src/main/res/drawable-ldpi/exo_controls_previous.png delete mode 100644 library/ui/src/main/res/drawable-ldpi/exo_controls_rewind.png create mode 100644 library/ui/src/main/res/drawable-ldpi/exo_icon_fastforward.png create mode 100644 library/ui/src/main/res/drawable-ldpi/exo_icon_next.png create mode 100644 library/ui/src/main/res/drawable-ldpi/exo_icon_pause.png create mode 100644 library/ui/src/main/res/drawable-ldpi/exo_icon_play.png create mode 100644 library/ui/src/main/res/drawable-ldpi/exo_icon_previous.png create mode 100644 library/ui/src/main/res/drawable-ldpi/exo_icon_rewind.png create mode 100644 library/ui/src/main/res/drawable-ldpi/exo_icon_stop.png create mode 100644 library/ui/src/main/res/drawable-ldpi/exo_notification_small_icon.png delete mode 100644 library/ui/src/main/res/drawable-mdpi/exo_controls_fastforward.png delete mode 100644 library/ui/src/main/res/drawable-mdpi/exo_controls_next.png delete mode 100644 library/ui/src/main/res/drawable-mdpi/exo_controls_pause.png delete mode 100644 library/ui/src/main/res/drawable-mdpi/exo_controls_play.png delete mode 100644 library/ui/src/main/res/drawable-mdpi/exo_controls_previous.png delete mode 100644 library/ui/src/main/res/drawable-mdpi/exo_controls_rewind.png create mode 100644 library/ui/src/main/res/drawable-mdpi/exo_icon_fastforward.png create mode 100644 library/ui/src/main/res/drawable-mdpi/exo_icon_next.png create mode 100644 library/ui/src/main/res/drawable-mdpi/exo_icon_pause.png create mode 100644 library/ui/src/main/res/drawable-mdpi/exo_icon_play.png create mode 100644 library/ui/src/main/res/drawable-mdpi/exo_icon_previous.png create mode 100644 library/ui/src/main/res/drawable-mdpi/exo_icon_rewind.png create mode 100644 library/ui/src/main/res/drawable-mdpi/exo_icon_stop.png create mode 100644 library/ui/src/main/res/drawable-mdpi/exo_notification_small_icon.png delete mode 100644 library/ui/src/main/res/drawable-xhdpi/exo_controls_fastforward.png delete mode 100644 library/ui/src/main/res/drawable-xhdpi/exo_controls_next.png delete mode 100644 library/ui/src/main/res/drawable-xhdpi/exo_controls_play.png delete mode 100644 library/ui/src/main/res/drawable-xhdpi/exo_controls_previous.png delete mode 100644 library/ui/src/main/res/drawable-xhdpi/exo_controls_rewind.png create mode 100644 library/ui/src/main/res/drawable-xhdpi/exo_icon_fastforward.png create mode 100644 library/ui/src/main/res/drawable-xhdpi/exo_icon_next.png rename library/ui/src/main/res/drawable-xhdpi/{exo_controls_pause.png => exo_icon_pause.png} (100%) create mode 100644 library/ui/src/main/res/drawable-xhdpi/exo_icon_play.png create mode 100644 library/ui/src/main/res/drawable-xhdpi/exo_icon_previous.png create mode 100644 library/ui/src/main/res/drawable-xhdpi/exo_icon_rewind.png create mode 100644 library/ui/src/main/res/drawable-xhdpi/exo_icon_stop.png create mode 100644 library/ui/src/main/res/drawable-xhdpi/exo_notification_small_icon.png delete mode 100644 library/ui/src/main/res/drawable-xxhdpi/exo_controls_fastforward.png delete mode 100644 library/ui/src/main/res/drawable-xxhdpi/exo_controls_next.png delete mode 100644 library/ui/src/main/res/drawable-xxhdpi/exo_controls_pause.png delete mode 100644 library/ui/src/main/res/drawable-xxhdpi/exo_controls_play.png delete mode 100644 library/ui/src/main/res/drawable-xxhdpi/exo_controls_previous.png delete mode 100644 library/ui/src/main/res/drawable-xxhdpi/exo_controls_rewind.png create mode 100644 library/ui/src/main/res/drawable-xxhdpi/exo_icon_fastforward.png create mode 100644 library/ui/src/main/res/drawable-xxhdpi/exo_icon_next.png create mode 100644 library/ui/src/main/res/drawable-xxhdpi/exo_icon_pause.png create mode 100644 library/ui/src/main/res/drawable-xxhdpi/exo_icon_play.png create mode 100644 library/ui/src/main/res/drawable-xxhdpi/exo_icon_previous.png create mode 100644 library/ui/src/main/res/drawable-xxhdpi/exo_icon_rewind.png create mode 100644 library/ui/src/main/res/drawable-xxhdpi/exo_icon_stop.png create mode 100644 library/ui/src/main/res/drawable-xxhdpi/exo_notification_small_icon.png create mode 100644 library/ui/src/main/res/drawable-xxxhdpi/exo_notification_small_icon.png create mode 100644 library/ui/src/main/res/values/drawables.xml 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 843df84091bb7e43f85088e2985cb17712eb4b12..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 354 zcmV-o0iFJdP)~5QPzl6$m6^1rmWoAW4u24uIeQ2qc0dZ~z1cKp+tuU<=Tl1wDU|KjmESlLKwAWuDy0?Ju3$lDWgaLQ6r z0eDtE9z+44UOeG~t54wP|l{5gJor@<%0p%hYWnGgfPpkqdf0AlUPwWE9 zNzwy&j_*7c1yUB0GQcyx_gEHC_L3ITV_iT$r#{kSS>WME4DhUjerS&a^(q`cUxg@7 z>;fMjBM(p0`Wnp~2fpykpksx@fldP%%7ZT8JQ6#2el3T&ua}Yx&(M0D_F4;3p3nl` zi}@SRkL60~|CGOa+SW_`B50Tbdk7(f5JJd}D`jHlVpJacasU7T07*qoM6N<$f{G`F Aj{pDw 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 c37541472ebb20e052a5c4e68f82852d4a489ec0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 323 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-s;Q>A&uI>ds|7if^nwEqEt&k}R z@(X5g;PIDueVZlw_jHBT=RXUWJnsHpeOP02w9fW;RW^Pzpt^cb7srr_xVM)qg$@}A zuwG>3G3w^Ik@o)g{K>04IW(I1PF(rP?e^C6Z?e~t}8mc^KVEOXY<)RyS%oA nDVdY8h==XT9hM(YvbWwlo}!Q;Ydn(!=oAJ|S3j3^P6E)*( 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 0a23452746c0f33622ff4d909e2213b98ce990b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 108 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDH3?y^UWFG-iYymzYu0Z<#|Nl#G&c6#}F_r}R z1v5B2yO9RuhnjGkCiCxvXAbpmr5JpHO}HTOB5@xyCL>^BiEHX8zeV3eCC}QlkKsiLyr`#VO({s-JnDN;}FLM6npO-wE_EgQu&X%Q~loCID~aZ8HD> 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 3eae5c883bf2c1d8b950e35307ca7e0bf0fc664b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 292 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-st^qzFuI>ds|0xFHS4_484HPR0 z@(X4#xE%a&;{M-UA61GMy32^HPd(Xe_1wSeC_hj|g{O;SNJZS+OB=}UWAyQc>(V?C+-zHvv;vr9<^9_ b@df)-Q^{poR@z^IZeZ|q^>bP0l+XkKj~kZ6 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 36537d3b7320d0e2b352527ae374bc8c6f3a1d64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 347 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sfdM`tuI>ds|EUU+r44zwK8TlpZ=V`AX~Kb@xBLUpBqutG}z@yPb#Hr0Npiu4KG(s4v}T&!I9w zksXaJ*N)%W2b^$lV+00^T7r*g}8FYn}2J|fI5tI_Y)qkwje@(&0_aOxv z<9#Jszxs2`G_3UgsrOxvSk=Qd V9_(4)c?IZH22WQ%mvv4FO#p)T!5#nr 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 0000000000000000000000000000000000000000..5699614c6b57ccdb83eef3e147759a6d2712e144 GIT binary patch literal 283 zcmV+$0p$LPP)@E!Q3-SxXAgBC-{DLsZF9?Uha5xACgTd3y;d}3;!T0j- z?vd~Q-zY9FE^dh9LSh(wK*3xv3qJrY2gUdSfG1hdyvY0j)NBN^^#cG`Cc1F~@FfqL zlH3nK$B9HXMgZW=OgByd8g@bRW9J6|9;88YC-nnRu~Lj5XxJ;p4ZN8J&8Ig`pyN!U z87EMZ3ufs)(<_spx%I{g)NCE223}->nR>^rn(+b`62XkUFSBALnDxjT h4^qPndU0`aaev93S;ei1XQluE002ovPDHLkV1hR$a_Im7 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..303e896187f42c67f2c74aa4bf290ca13160a98f GIT binary patch literal 264 zcmV+j0r&oiP)(qMR63y zLG{1r0tg_000Ic0WdQ_GN;#`;st-^`YXSJLS1TsK`=}!q;N8@M3Gm)(#|3y7HDdz2 zr`j+9-dCMe#{_r}wPXUkLG8H!@2ciZfcH{cGGI^#D8OH65}^7?B>}2GH5kBW1qo0c zb~M1Bmov9~Dz O0000zopr0A|D{vfA zh@bF2S&r@Jp*c+o%MF$r#Lv+GvF|~$0E>K_wN<?57n5$vTzu@@Bh2_7xrUTv1;OXk;vd$@?2>_+$SFr#9 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..2c3b3af982afd2bb1bb23da18c54c4d691addff3 GIT binary patch literal 255 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpUtW1cRKAr*{C1^OQtwUDmuGU$n15Ye(BPG`PJ-BufCFnTG&WcsNntuB xF_TTAxke`;Y=X@JjmFrVrAuKB5a$2Ecv$6xXWhk|Wk4S>c)I$ztaD0e0sxgKS)Kp@ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d9e279231a831a95bc4f64d68142db1b7d6e7cf0 GIT binary patch literal 332 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezr3(FbaFRIEGX(CKc#^kh~;up-;BS zMZh&tB=2^P+v>Y{$NLV(y-CRbmcZUD+kCh0D&Orhlg(?ci9a!X{3nj*`+>FX;Q$e%-vo&927OF6?+eCUEn9O}Z)Gk-y!AX_}jOx-etGSp88q4>mG^@)u zMC+7I_;N<=*Jer2sx;xhkNaF~CwJOexlfvR(7!P&sto3)7+{rzYfV^ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..ecf3df3cb17d34f1737fd54fc4fbeba558018d36 GIT binary patch literal 995 zcmV<9104K`P)_=93O49-$`l z5>_|dT;VFjGQ=FAIaeS_SY6&6yH60yBy(B77f7oMIR(ZOL(W{?FY*UUSlwlax`Qqv zxy4++NEpapT@zJAy@^Q1AnKR?KmpZ#gQ%m36xBCMHRM&y1I}c`{p721~SJvyT z?BFcw5B-b!V2piF_}FK^G4>|h7(ehG9beIy#8oNZBI-yYxfxMc(HKWnj>GjvBI!1R ze{sS8g_asI`&&VPla2R{YYBkJSC(x2UkI+R#URenaAcVC$X z{EDb6$Pei24%^xf(&`d15eCd3gmv<#HTMGVi7fGJQ&4!XEd6R<1!%SwxAf zIDsXIx{oVx^0|rhD=lO}D}UHKC2j_o!4%#nJxzRMe=SqN4-X(VQtDrNA0x(lNXTo5 z@mESI){ZSpc#*`k^Dkmt#D#=#2}=;;U$hZGf!T<0H!o5k2}NFE4`Q52jzB6JNo6I& z$>X8GC{`nNamu8iz<)@c>v)sPXo{Jr%Xv#D)s~ebCGEV5^l6oE7|iXQPMMCHWlraI z2J;r@Q!xD>&d( zc!KflL}J{*I3B0WL8ObD7{!N7WieHD)~vFaseHglZeag77zQaA1*2dT008y_=SL%k Rgw+55002ovPDHLkV1k*n&J+Lu literal 0 HcmV?d00001 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 19b9e6015c7cfe517037032e34ef1fd2de721475..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 192 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gj?Vc`@3FFZCs1}JDH z@L6DS8&IL6qH~W_kS>F3nw?5rI|NsA8 zhciwsRt5^knFK8Ok63Nl!oaeRxzJ2kx1sT@gN~y&x5EQdf$Gbbm?UQK9kJQ6CBa(Y z^o2uA0WHrjYjtc;i_D5-I8`FE#ba524kRmlTeg(dV1rbLYDOj_#~$`aDN#`j%)c8WnnBX#3hUUC#G)b@n5Q@1XqqOr zhwV{8RHT7?2Wy6)%l5__jbao3NO#hahox3Je0A~ do~wX~q2W~A`8Aye6Mzn3@O1TaS?83{1OTu`L7V^p 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 930534d3127ceef161e086437aab19a725009e09..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 187 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjO`a}}Ar-fh6C_v{H>5qV7ZF(4 z$KLV$@)4#5TX+TTs~=NTiDVE-V|%Z zT*VkP1r_4{%kwX|!nEd~(^Z8gw;b!DBSE%&7bdIu>NK308E)g3vbV?ft1c)I$ztaD0e0sshPLKpx5 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 83d71782f65462fbc78127f99fa99dd10cbee9ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`Gjb3I)gLn>}1CrGd^ZqR%1|Nnm* z2b=jpraMfc7(Ui2{Ofn`o9owS^-t^h%r9;`u`(yQx*s7=}aaufe`1SBa5=zR%ISv-ZrD6LEYMBqE7&? z;mmA9yR#08j80h{8#N?4?hBvDc-V49V3P}5$M@h>X(ByLpBNY{e!iaao+04@(4h>T Lu6{1-oD!MFS$kP61P7X*140z_ILDsVIz zoDhFqB|7Wp#`t#iL_xtacZQC8aWJ>b#o({G-EV6Iy6rfcMp00i_>zopr0Iu*m AlmGw# literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..1818039e5161e9d588c1cc6581b38476bebf9663 GIT binary patch literal 88 zcmeAS@N?(olHy`uVBq!ia0vp^5+KY7Bp6QcFoXgrrjj7PU literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..4d2eccfe9a4b53cfac15a6dae8f6a60d5f870718 GIT binary patch literal 161 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_+i3{MxwkP61P7X*0@2S~JDWIuW^ z!D5c}nY+(s?oKx-mpEg*yz*7da;J?2Ui+uK`&?HyY4+{Ds^)E{PcCQPF`J|D;At<8 zJ3RtS{@jWMADd;aNPJXj-X6;L>ViPXxqXH!Y|bdIi1{0x?k=6UV~K2xz^z9>I~hD( L{an^LB{Ts5)GcA#d&Ae;(i_X9g)Aj-#!{F)a=d#Wzp$PyJ_)vfV literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..836f4dbb55e53e7e0eacb7f5859a431a3fff832c GIT binary patch literal 88 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1c~2L|5R22v2@*FS*ejnj`d`R! kr>rMS;;O^aBj!LI9u`U;{{1(c094B0>FVdQ&MBb@0HW6!m;e9( literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e5104fcd62cfd3892e6339e84466463bfbc6db40 GIT binary patch literal 457 zcmV;)0XF`LP)kdg0004yNklEXOoj`TbCRJWC%;snK zeYxwK-T3{ZGc)p!Ns%E-ZW~!Lq}Yp7Mno*w5EHXuLBx;~{zRG{u_wBOB=EpYf*J{ z;K!j<)D2U>8>+X3Z6#qQmZyO25nH)1|;Qxyg>^9eTMgc97xMx00000NkvXXu0mjft&PIl literal 0 HcmV?d00001 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 ee3efe1d694bb6cfa69dfd7451e4d1a2904c77a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 255 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJW1cRKAr-fh6C_v{Cy4Yg={)%V z|NmKsH4fTek4Ws0@33bsG|{~xoUOpKhAoLPYnA#{MuiU&9kMHy&XZwpIFPUKjx9+{ z>FWREOXtO~A2?8{a7`g_FQ2I_1JnIRi`g+>uM{xwds|EUK$mVS={nk-oo zMzKY@y3JY5_^D&pQ=bmwa@5MXu) z@^pItmdKI;Vst0BZD- A)c^nh 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 f54c942201a83c6b866d651cb6dda51ab06d5cc9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv(Ey(iSN8&+{}^EVDaKns1#Bfj ze!&d!4iW+?+N{j~fFin{E{-7@=aUl_NCyN3B_#` zyzvFT4)#Z99dwK*@G&%=cbLO_M2Gn)gMTnjll#U zebYe)NHa7(caX6G@)=*HMHgTe~DWM4f@A*$c 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 950e213d2f550cf8022cc7ee063e509e6903d393..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 227 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJRh}-6Ar-fh6C_v{Cy4YgF+H$n zI-?#a&%d&2#WRKlQPK+g_%FGHvol=Xz))x`@V_l{(QM|1Pg)K&zwD(JbTc;;y>$5V z(_YLWn4RIMOGCxqdYgtcK88gMUIs4~z z1~5loTbylnB<#n9y)q8^b?iqNJwz`&0!mMsm!ME*pTO7fYBJ-azl(Wi1sZUmdKI;Vst07C3gL;wH) 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 e75efae1893e7c53fce2d4c8809bbbe12e577923..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 273 zcmV+s0q*{ZP)`R z%w)4WEs7urf)7yDIa2=0w~Cd>9TCEByne3G6glVI|tLyqc0l7I60o&l_5><&Qr%*Cs`1zG>w zrkdg0001>NklYyCj#Y zRQiuAt>+@1%=C`yf!Ii`+#7(2PPsP#3j-nHM0W;aAGX4G-T-_#EB6LscbcR>Xsm&F zVCBxhz(#n;Ut!`XymKCLq}HArm^lgWosaaUP5LW(ZeZa;$h(Le2jTl_wNmK^5Q&s= TKo_aB00000NkvXXu0mjf*0Eeg literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a93aae0f3484bcb35450eb05d0cd643a272147d2 GIT binary patch literal 209 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJ8J;eVAr*{ougUT_C5o^;c#tFz zw_M?jDo567*~vQ3Yb@IxXIws7^YAO{LE*T()%PA8Sbl%r+dq5Kx7=B~`2BxIRt*Qn z0*BA%mhWJyVPcS7=E-2Q!Iz2QT*hRE7aP5p82TjC84C{Ea5kua%W!H~D8uQ{@4Jdc zd`qXA%(S2c9_uqs-5DIq;+fw?EIIU|<>dB9JMLh#Eox~+4A6}Xp00i_ I>zopr0341^lmGw# literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..3e150b5a45ac6e321d1cd54b625e412a6d762e30 GIT binary patch literal 140 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=ffJU{4pvkP6160{st+TcX*;7!DYS zxP)scFqcfone=zLhqy)8qEKk4U{hlt4Ar*{oFAEAV1q!e|Xl6g? zD8S-qmuMGh_j8W_j9@VImuO2AU^{qF-0uF=Sv&7PpMCRh`Rw?!0=Z%@=A12J$d6WK zK5$iu{labORE8bVPZ=zvo-@odvt-zMMB=D|!_ZlZ^;7h{Zy4K6YMrADbQ*)FtDnm{r-UW|DUM2I literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..ea83907d8edc58358daeb5dbb93695f8e97c3e95 GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4U)t)YnAr*{oF9`}TIWn|9Y*nzZ z;c1@pxu)oxZ1Jh*3iB1_*POHDX*T%57e4LY%YSzAdn%V+&t_eA=}bZtqeYc4li9j| zEe(4moemuB7ci)D;IQ~9z_cxI0`ubKG8t#Y`zj1hH&!i_SfSq6FzcW#OV+u4f&rU9 eYwe9&%sSCUaZ>S;pWQ%bFnGH9xvXkdg0001>Nkl9aBP zdzZmaY4)+|$;u6s==bcw(U%;`o-7rphnB9N{cJF*(3_HES%3Th#_ZW;ZjNWDA4NCM^^fn8Z97gYRaBlW}-6xYjb4Hd{ TSHd>;00000NkvXXu0mjffqz{g literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..2aeffbb6cfc16a1b12eb98bf9e93e647a19c50b4 GIT binary patch literal 97 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4U8lEnWAs(H{2@Jw%1#3HFnGH9xvX~F^jVFo}-QbYlqEs$$sN9&7NAX}b!o!i>`zVXe}%+F$M=>6ma>BV_W(Zgh}K&RsTJV>akK0Sru+zgYww zvBpZ2GpfQAj8yT$?x5KknX2N*fW+%^xFu5pY#tcB)~q1X z@^Hn92fydexcT4i@{=WsXErogKMntLwbk8b_SrOP_g7WAYW}h80aLG(GCX`b<2Azu zT_CF(DAcfg`8sBc@K;t0GVj*6uYb+(ta}%q!WE!whSe*ph9r+4Gb9<*{B6`5R{2z> zF+42WIN4qJ{oCMqq8e>mp0mvA+rif|YbW0s@nTsEnL=5M@Ml&Zz8on{crSeKS*^xx zrx^A_Z#!N)gc@CMJbC<|b*-!h+cT>RB}cygnzduj*|g2I4C0r(-V6Fk^8x+D;OXk; Jvd$@?2>^VR(}n;5 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 bc1ebf83c58c0f6b983817f62a24213cf25c2c27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 334 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU=;OqaSW-r_4cNskYk`od!eHM zOQ3*`gNTcyt{r}#)c+<@MXM4-f zhpq$~2m~TiQg7Z__N)26WP14@XNK*YUuv)yq|fdwYDmbv9K!dY&&>HL!@)z#*2x@r zo4e~2SL}&66gRYYcTuay8?+egzqa?iKrg>VC>bmL1i7;T#`6T@CAEG|d_tc@}WR?%2|^fWQwsU}LWWBG2$FJPU}Nu&HMOfj3rr z77)2$bI$?-UuZ~Y$daY09(oIDZoxL2MW-i%tQk8CNrS`oyp7~Kvyy|3DA?w z%mU;mvw#5E$t);9W-|XE;D#+c3ix1KuTS8N%{+btU)aWL29Map<016GRvu5`aQ^Pq z{V_@;Kvvoc3Xqw$LIUKbEwcdmY0D@;N7^z7(3Q4?0(7P=kpR7E8z?}3+6D-)k+vQJ pY^ANEfZANz2qA?qY_z{C1* z%d}ffN-Gw;|0n*{`1iWB*_H?Pd(P|aTBLDl|7zYWcIF^a#%HNQjC$ss4B6t0r}P99 zes?jvRTO;iQ;aEYGDpBBmxgL_##@Sl3ZaY#;^i6s{o?QtWxD3a_Jdb3LDl%ce+kBY zDl9*yaX!eE=ZLt;`CzRq#uN5$XW0pK OCWEJ|pUXO@geCy!sjGbe 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 3340ef9bd227be28c3641fc1a111522d9d0af362..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 400 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!LIQk3T-^(N{!<$?d%wI4G+(+T z$S;_|kmEGV1`&r(zgw*zPI6BVmD#t5ulUMy6^-P8+MT9AWfweM978JN-cGUPYjzM| zsZHRQ;Mw=V@&Et%eCjWBGvzmkPSbM8s46!#k?K9RN&olRnu(u18}~K+T%2U#?{!{g zT~OPTnSY}t7o}fwl$@0Qj$hOH)0ws5K*8(5o(tTMd5SG6-*sAn`=a&z#VLwa=`TZh z%s-bra+}b8S-(A>qM(9jaIq`R&m+>pL1$zqkn6*~lvgG#geJ#Uw zueTCpdT+KR1BregEt3 z>RGH`QqBcF*t2Kyfw~Cw#-h)DOsBJCS)bjq;_R7RE+k>}TIIsmWBcC(%(`aHF{S*i zEX$`u)|^Xj?zX?#&T{UwHRqf5@(p#H<+_h1CD^m1@XI-iuX_pf41=eupUXO@geCwx CaLZLRzZav!q1P_$`iTlt=|YTLQ_0gq2GO01aM{KD_4^wFqO zat_CD?d;Q^vai`?`AYT+n?A@cRd&A~xz6)FW5%Uw2CLmM48FhqGk}~k<(@Z3bbtA) RzH*Q^Jzf1=);T3K0RTb7Zax42 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..381eabdccf795abbc00d86fbd2cb04449abb7356 GIT binary patch literal 270 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I0wfs{c7_7UtDY{7Ar*{ouL=q{IZC)bOfcj) zq;N+4#M4AS{?8}X=Nl|HSgvqFou}D=L!L!_cV2qdztZ@6%RhE|lKxDOfo+=!`vg@#?g@k}rVdM8gc9F9s;AUDr6kZC*B14)6><@ JWt~$(695KMV)Bn`{ESg^Ze$jFU zRt*P6u7Cz6kp)2JfycpM*0Znw-t;CX%5yfD&r#rFkgj>m7RXR>(B46W!LG1XgCXZ5 zw*YH{>^UIOD&L^Nu;&p+A;XVDyo(q%9F+$tDgY|ddBbSL&?YmV+2F|!nFht++}b<#TTVLx{oC)z4*}Q$iB} D_1|@w literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a798fee30ee704c190e03acb258790ae9ff5bfd6 GIT binary patch literal 391 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!LIQk3T-^(N{!<$?d%wI4G+(A9 z$S;^7-7r~X!+!_&=MOn1T`B%8voG`$-y&<4R+ZBl@0vZcfa;EUx;TbZFuuKVT=1}g zz|nx?@8_(&Q$K%S^*krWBZADXdOM?>76xdnG5;1_vElaf4L6z}Yx49zulLux?xJXW zGgZuVyunwembrY}PlymJiEb3%7nq`OA4&caOUb*Gt=9 zJpJhv{W}gB_J1$8c=nTBw6DtG3&-)j%R3bJt~}58cJE}FOS8WpU%1y_xc$?zo%Rx! zW}iK~aPRq&_Ak#WiZb4xeLLaa8hgICwFfL^UhK7e^>kl=hDcq?a|vIruje9wK4S26 L^>bP0l+XkKTix2T literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..8727a934805fbeee09e9426ea9712efd061aeba6 GIT binary patch literal 91 zcmeAS@N?(olHy`uVBq!ia0vp^4j{}3Bp8~6e2Re-Q%R6tFatx`NklE;LX9}f=Atg*Ak9#7?94;aDaDka5$gmNdKtjwhhLSN&_!@~I zU(kpQ7{fIrqFiAFk&3g*PsP=0eG9WMV5rhuHN6~H->Q+`_5}=8(wFqFaJ65Jbh0mC zs8Uz-@~+OQk=phJ3{|S9rq1B%wi=1s7cf*Qf&T1VRj84eeE~z2VrruNep_IaVS#&I zq1_%Tl+-KVKPU0(s7_*i8SRWe#_HHFqu;2JKA|tL+i)ZM5?8C#$SnM^RY#krrkCC8 zhq+Psvs1mo6#tLY$MMHj9qqLLUnXiID$Vi3QXQ>rkS{cFSPf0Y4@-5lsroBru%GpQ z!4FGyv_FFVyxCWcrTmo0P#vu`qS6CTlH5>(@8g-FI@(9-D6SF5qit&R3i)`Ys}ocq z`v19&c=k4;G6%18b%I)esJww^Ih3g3%T&V~dvyS6auxl*eI1C7c! ze97Yi`dainURbLG@DieOjwC+KL{yHDMGAt9!Ofn->M>6}OG;T%(lb5G^DMt#f7jXWpDl6QVfyONCe??d>Y3Y7#J8s zJY5_^D&pQwKb?2TK*U9MQUaF=r?1atr}!^V|NlRvw`Yd$(w$QI?<3`3>g4pk`C#Ao z*aC?Bq<#DR?#c0-D|c0R&pvrEdy!q=ou880Oh4b5k76Y3*kztw*42IAQQ~^>bP0 Hl+XkKRE;tn 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 232f09e910954a5a56b32d2cb807f2e68f8de781..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 391 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wSy#_0(?ST-3xsFQwF}~PUr`kCR!5Y z7tG*y)Qt7M!Y{seD`%c}?5@(zm5SXa?clKjsNlY*i(^Pd+}o>XxeggHuq6JTyzJt( zZP#l5hksRi<0~@f_BqQBjvTX3KM(TKTsq~FSzWo``#QEQZwwYx9ccJcbD$xLo%vF3 z8{`>6vf8#!6LBX zI49c`KOpO1!znK2w<(QvP3unRPh~m9U>m%imAT@*z=3XGrmqdpZtYj&`f;C~p`V$d z;vUC=%gqe)FVdQ&MBb@0Q^V4E&u=k 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 50a545db4d67f51ccc127aa98582a2d5236e127f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 113 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeK3?y%aJ*@^(YymzYuI>ds|NsA=KV^|KP=v80 z$S;_|;n|HeAV<>E#WBR_ALEHZkC5TjW01WFdewB_m zpN&S#A|!*?^k{}16VXoYnre9T)QqH5!op(Rt?bj}tv}c;oS*SOslKRi@&Y&Ci%$dR zhP?2L$lEM(JEzsGdg0l)jLqV=-e6}st}o!u{dPy~NUjrvcs8ro@R-D7ev`ELYA)%6 Rr9gi&c)I$ztaD0e0ssTizK8$- 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 f71acc4875e1ee7375b4023d800abd6b1292e717..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 464 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wSy#z0(?ST-3xsF(-#!%=eQ5FU9}|0 zFPP!IK{1ES>EA3i_dlnLJYPI{zq`if>)y>g?E=9n>o14*?|%G!e#s*KRX`0dJY5_^ zD&pQ=wJc%^6mh=D?)&*RbHay~vpgC9qg57qNxBvp-{qa-v+DTLDM4PEs&`i}kBX0c z@HAA#;E&nF17%th5B#3(k+5yDM}nz1r;nYAki`2|rdVOlFW#LjX37%}R4FPpEN^GD zvE_O2(viWRpShySBcW7`)2B~}siG=i!R`*0Uz3k8%##y&A;oj$$84sWeuIFo#_f%> zY7-l#^Rc}u=3+MeW6Jd*{*c38eT4(EZUzB=h1jxQNU>GSQ8@6pg;7qO>w`sN!|_fg z8$XE$g^L*ag;^`+7#w))!YH@AD#A+>=<;2Rb!XSeIO>$-0R7D1>FVdQ&MBb@0G@yI AVgLXD 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 db0555f9e5c5f89cf29edafc7c5f277556f3cb74..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 571 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wSy$81AIbU-3xsF4-n8x+3yN;hDJ${ zUob=Q{m(M*4Sut5h%`U9nS7cjcz?S4VwLrmH8x-Op5K3bce23a@8K5hd){5)sP1bJzu zex8u|$?iP!?4_9w#fJS7cQ&kE{)6%CEt3bSG8KD_c_U(uuXp5qeV?iS_c@ce-x7|; z-<~o_C=+&n+17iHx!=9`a>eB599HvUX0hcgXFPu4!kQ!Tx8C?3zTAA=J0l@1WYxo$ zfmXI!hPGbI-Bp*lFO`}nxAgL2q05U)szm(z7xo@{SqNnL^e?}>_|KPy%d?c{#ufx6 zpLzFlhV=5-hlb|&7B(m69eBjox6yI8p^Wq4yz2#Lc=wxC+^KPNzF%O`)|UIN<_{O| z4Zoj14x9L->#~^|_!Zo7m|RnLreUU x-~XHn5bxna#>s3aK743!bbf5IM-v<<_wCJor+pLc2m?j~gQu&X%Q~loCIETRKIZ@c 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 0000000000000000000000000000000000000000..1e8db0ec23de5d7fb177e9971bfde76ee15d9e5f GIT binary patch literal 575 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wSy$81AIbU-3xsF4-n8x+3yN;hGt2S zUob;A%m4E_8sD?GOKg7a?>(L8w#oJ1)#Zm3K1aV_EwKEtL;P`z*)sVY&Nd6z`!O&u zdU?7yhEy=Vo$lJl5-4(*Yxc~UFaL?loH2N!xwrTFv>1t(>c=FKdkmkIU-Bt;dg3Zs zpjFQDDNFa!i$?WVyzfjNdNl62%4Z>K`@v=Va%&ZaU(D}#*n=2)j!BlQ%@kp{JoCi| zh8g-hHpgTzZip&Q-#m>eVYl#H2Zkk*rEUdV7>;#X7cm?@ENj_N_^40rfJLD@UqZzW zWp)FbBQN>CFtoK>KK9{Hs92%Q1{68iaFUUSzc4J@vf&}8MODJSev7IH2F=d*HcK1` z_mDNcV9z$E@!!mORYwj+bui{gmR@oAD^no#=|-qNz(Ijp3;@#R9lX&nhw9h<#V!m>{qf&-|%2;9@t@#>+nl~Phzr#La`wG8ef?SwH?gALS+o{{G=!RR^*HDFLa1= zmMOURXu-T&3HA$k9z9#Y^NdTQ*=_+(7V{KGy#O{t##0B_J(`a?N}iDpV7uO6v_gFW z&o|~%3VD2|64o^syL;wH) literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..ac8d4fcad56a7e2b7e0a2a379e5e28e98f7d6216 GIT binary patch literal 111 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeK3?y%aJ*@^(YymzYuI>ds|NsA=KV^|KP=v80 z$S;_|;n|HeAV=KO#WAFUF`1z`@WX$54u17>8tGGvxfqxZ+*iEm^>GVO34^DrpUXO@ GgeCwoL?BK8 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..365b3dfee5bcc47da1f04d84a67c3169dcc73e7f GIT binary patch literal 378 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wSy$=1AIbU-3xsFQvkerXGs8!5-kbx z3uc%s@c&7`df9jBN438;v);4_pXZP(caPB$WTmHzV@L(#+iUDxOo1Xt15Cf&`?f#q zWs9E2lD(T7xs4xw6mL8rk=$cAZMAFnS2ekYw|a63^&RXFD!SM$;@7=pxbyBKgUL}P z&y5j*I$2lqfAwx2N^tF{an^LB{Ts5v<|n9 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..884cbdd4077318cd73bce790f3faa0260ba5630f GIT binary patch literal 458 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wSy#z0(?ST-3xsF(-#!%=eQ5FU9BX@ zFPPy!&wQKvEX}`FPN#nsS?~UwW3s{JVwwHH?-xJr|GxXW_hyZDf$-x&%T@q&JoI#N z45?szdu3}PGlK~8MSWxA_RRl+hXcho)y~apydL{E#!GYQl%PxLUzbbyie+qdWtyAk zuW~`&L&ZR*ljWFjC(AP9i3e&FCmtwL6^Z!n!ZdrW3)Aks9t#R5NE~>*kYRm0(;hjl z4^qnsO$SWVTV~`!L7gK=C7F&LM{Loj@6$ j4|BADj`q_0dxJZABX_X=iu@>`XBj+Q{an^LB{Ts55pVU6 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..4bab5457141f9f14771f1006db37c82a2b9ad2d1 GIT binary patch literal 561 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wSy$81AIbU-3xsF4-n8x+3yN;hGt2S zUoZpDe2e@4ZCILrs~kU_{5f4@z5CE@6!Y3U*JcEIO z(ZduFGiSc5cTBeVJl{O*XlSOlyyqkpAUavUG9>i?+C@I~0zK`neUiP8`?t#&&t!Xia?Jt5&#oG<7EZ866W$nAKH}LlC{#0`&&GyDEhN0_|UtCh`yU!B+Xww_+t?l z`ygSCz-hzDGas*(u`%p(H@A>dKT_bc{P_>Q_Qx$V`i`G8l=)y|z3u?x`pJ)#WqL3F f_5%lq=O@FlwLLh1E0au)z4*}Q$iB};MzC; literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..52393366712cb37ce62aef45fd4542ce45cc6ac5 GIT binary patch literal 95 zcmeAS@N?(olHy`uVBq!ia0vp^2_Vb}Bp6OT_L>T$m`Z~Df*BafCZDwc@+3T6978G? plNF?Un*Jv^WS`SWpW>`1!OD<-Rqn&L4h?CLPES`qmvv4FO#td&7>@t| literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..56ed071b2f7aa4e69c2d69626c984ff827c11dd0 GIT binary patch literal 2020 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 + + @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 +