15
15
*/
16
16
package androidx .media3 .ui ;
17
17
18
+ import static androidx .media3 .common .Player .COMMAND_GET_CURRENT_MEDIA_ITEM ;
19
+ import static androidx .media3 .common .Player .COMMAND_GET_TIMELINE ;
20
+ import static androidx .media3 .common .Player .COMMAND_GET_TRACKS ;
21
+ import static androidx .media3 .common .Player .COMMAND_PLAY_PAUSE ;
22
+ import static androidx .media3 .common .Player .COMMAND_PREPARE ;
18
23
import static androidx .media3 .common .Player .COMMAND_SEEK_BACK ;
19
24
import static androidx .media3 .common .Player .COMMAND_SEEK_FORWARD ;
20
25
import static androidx .media3 .common .Player .COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM ;
26
+ import static androidx .media3 .common .Player .COMMAND_SEEK_TO_DEFAULT_POSITION ;
27
+ import static androidx .media3 .common .Player .COMMAND_SEEK_TO_MEDIA_ITEM ;
21
28
import static androidx .media3 .common .Player .COMMAND_SEEK_TO_NEXT ;
22
29
import static androidx .media3 .common .Player .COMMAND_SEEK_TO_PREVIOUS ;
30
+ import static androidx .media3 .common .Player .COMMAND_SET_REPEAT_MODE ;
31
+ import static androidx .media3 .common .Player .COMMAND_SET_SHUFFLE_MODE ;
32
+ import static androidx .media3 .common .Player .COMMAND_SET_SPEED_AND_PITCH ;
33
+ import static androidx .media3 .common .Player .COMMAND_SET_TRACK_SELECTION_PARAMETERS ;
23
34
import static androidx .media3 .common .Player .EVENT_AVAILABLE_COMMANDS_CHANGED ;
24
35
import static androidx .media3 .common .Player .EVENT_IS_PLAYING_CHANGED ;
25
36
import static androidx .media3 .common .Player .EVENT_PLAYBACK_PARAMETERS_CHANGED ;
35
46
import static androidx .media3 .common .util .Assertions .checkNotNull ;
36
47
import static androidx .media3 .common .util .Util .castNonNull ;
37
48
import static androidx .media3 .common .util .Util .getDrawable ;
49
+ import static androidx .media3 .common .util .Util .msToUs ;
38
50
39
51
import android .annotation .SuppressLint ;
40
52
import android .content .Context ;
@@ -798,7 +810,7 @@ public void setShowTimeoutMs(int showTimeoutMs) {
798
810
*/
799
811
public void setRepeatToggleModes (@ RepeatModeUtil .RepeatToggleModes int repeatToggleModes ) {
800
812
this .repeatToggleModes = repeatToggleModes ;
801
- if (player != null ) {
813
+ if (player != null && player . isCommandAvailable ( COMMAND_SET_REPEAT_MODE ) ) {
802
814
@ Player .RepeatMode int currentMode = player .getRepeatMode ();
803
815
if (repeatToggleModes == RepeatModeUtil .REPEAT_TOGGLE_MODE_NONE
804
816
&& currentMode != Player .REPEAT_MODE_OFF ) {
@@ -1062,7 +1074,7 @@ private void updateRepeatModeButton() {
1062
1074
}
1063
1075
1064
1076
@ Nullable Player player = this .player ;
1065
- if (player == null ) {
1077
+ if (player == null || ! player . isCommandAvailable ( COMMAND_SET_REPEAT_MODE ) ) {
1066
1078
updateButton (/* enabled= */ false , repeatToggleButton );
1067
1079
repeatToggleButton .setImageDrawable (repeatOffButtonDrawable );
1068
1080
repeatToggleButton .setContentDescription (repeatOffButtonContentDescription );
@@ -1096,7 +1108,7 @@ private void updateShuffleButton() {
1096
1108
@ Nullable Player player = this .player ;
1097
1109
if (!controlViewLayoutManager .getShowButton (shuffleButton )) {
1098
1110
updateButton (/* enabled= */ false , shuffleButton );
1099
- } else if (player == null ) {
1111
+ } else if (player == null || ! player . isCommandAvailable ( COMMAND_SET_SHUFFLE_MODE ) ) {
1100
1112
updateButton (/* enabled= */ false , shuffleButton );
1101
1113
shuffleButton .setImageDrawable (shuffleOffButtonDrawable );
1102
1114
shuffleButton .setContentDescription (shuffleOffContentDescription );
@@ -1120,8 +1132,8 @@ private void initTrackSelectionAdapter() {
1120
1132
textTrackSelectionAdapter .clear ();
1121
1133
audioTrackSelectionAdapter .clear ();
1122
1134
if (player == null
1123
- || !player .isCommandAvailable (Player . COMMAND_GET_TRACKS )
1124
- || !player .isCommandAvailable (Player . COMMAND_SET_TRACK_SELECTION_PARAMETERS )) {
1135
+ || !player .isCommandAvailable (COMMAND_GET_TRACKS )
1136
+ || !player .isCommandAvailable (COMMAND_SET_TRACK_SELECTION_PARAMETERS )) {
1125
1137
return ;
1126
1138
}
1127
1139
Tracks tracks = player .getCurrentTracks ();
@@ -1162,12 +1174,14 @@ private void updateTimeline() {
1162
1174
if (player == null ) {
1163
1175
return ;
1164
1176
}
1165
- multiWindowTimeBar =
1166
- showMultiWindowTimeBar && canShowMultiWindowTimeBar (player .getCurrentTimeline (), window );
1177
+ multiWindowTimeBar = showMultiWindowTimeBar && canShowMultiWindowTimeBar (player , window );
1167
1178
currentWindowOffset = 0 ;
1168
1179
long durationUs = 0 ;
1169
1180
int adGroupCount = 0 ;
1170
- Timeline timeline = player .getCurrentTimeline ();
1181
+ Timeline timeline =
1182
+ player .isCommandAvailable (COMMAND_GET_TIMELINE )
1183
+ ? player .getCurrentTimeline ()
1184
+ : Timeline .EMPTY ;
1171
1185
if (!timeline .isEmpty ()) {
1172
1186
int currentWindowIndex = player .getCurrentMediaItemIndex ();
1173
1187
int firstWindowIndex = multiWindowTimeBar ? 0 : currentWindowIndex ;
@@ -1209,6 +1223,11 @@ private void updateTimeline() {
1209
1223
}
1210
1224
durationUs += window .durationUs ;
1211
1225
}
1226
+ } else if (player .isCommandAvailable (COMMAND_GET_CURRENT_MEDIA_ITEM )) {
1227
+ long playerDurationMs = player .getContentDuration ();
1228
+ if (playerDurationMs != C .TIME_UNSET ) {
1229
+ durationUs = msToUs (playerDurationMs );
1230
+ }
1212
1231
}
1213
1232
long durationMs = Util .usToMs (durationUs );
1214
1233
if (durationView != null ) {
@@ -1236,7 +1255,7 @@ private void updateProgress() {
1236
1255
@ Nullable Player player = this .player ;
1237
1256
long position = 0 ;
1238
1257
long bufferedPosition = 0 ;
1239
- if (player != null ) {
1258
+ if (player != null && player . isCommandAvailable ( COMMAND_GET_CURRENT_MEDIA_ITEM ) ) {
1240
1259
position = currentWindowOffset + player .getContentPosition ();
1241
1260
bufferedPosition = currentWindowOffset + player .getContentBufferedPosition ();
1242
1261
}
@@ -1314,7 +1333,7 @@ private void displaySettingsWindow(RecyclerView.Adapter<?> adapter, View anchorV
1314
1333
}
1315
1334
1316
1335
private void setPlaybackSpeed (float speed ) {
1317
- if (player == null ) {
1336
+ if (player == null || ! player . isCommandAvailable ( COMMAND_SET_SPEED_AND_PITCH ) ) {
1318
1337
return ;
1319
1338
}
1320
1339
player .setPlaybackParameters (player .getPlaybackParameters ().withSpeed (speed ));
@@ -1335,11 +1354,12 @@ private void updateButton(boolean enabled, @Nullable View view) {
1335
1354
}
1336
1355
1337
1356
private void seekToTimeBarPosition (Player player , long positionMs ) {
1338
- int windowIndex ;
1339
- Timeline timeline = player .getCurrentTimeline ();
1340
- if (multiWindowTimeBar && !timeline .isEmpty ()) {
1357
+ if (multiWindowTimeBar
1358
+ && player .isCommandAvailable (COMMAND_GET_TIMELINE )
1359
+ && player .isCommandAvailable (COMMAND_SEEK_TO_MEDIA_ITEM )) {
1360
+ Timeline timeline = player .getCurrentTimeline ();
1341
1361
int windowCount = timeline .getWindowCount ();
1342
- windowIndex = 0 ;
1362
+ int windowIndex = 0 ;
1343
1363
while (true ) {
1344
1364
long windowDurationMs = timeline .getWindow (windowIndex , window ).getDurationMs ();
1345
1365
if (positionMs < windowDurationMs ) {
@@ -1352,17 +1372,13 @@ private void seekToTimeBarPosition(Player player, long positionMs) {
1352
1372
positionMs -= windowDurationMs ;
1353
1373
windowIndex ++;
1354
1374
}
1355
- } else {
1356
- windowIndex = player .getCurrentMediaItemIndex ();
1375
+ player .seekTo (windowIndex , positionMs );
1376
+ } else if (player .isCommandAvailable (COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM )) {
1377
+ player .seekTo (positionMs );
1357
1378
}
1358
- seekTo (player , windowIndex , positionMs );
1359
1379
updateProgress ();
1360
1380
}
1361
1381
1362
- private void seekTo (Player player , int windowIndex , long positionMs ) {
1363
- player .seekTo (windowIndex , positionMs );
1364
- }
1365
-
1366
1382
private void onFullScreenButtonClicked (View v ) {
1367
1383
if (onFullScreenModeChangedListener == null ) {
1368
1384
return ;
@@ -1440,10 +1456,12 @@ public boolean dispatchMediaKeyEvent(KeyEvent event) {
1440
1456
}
1441
1457
if (event .getAction () == KeyEvent .ACTION_DOWN ) {
1442
1458
if (keyCode == KeyEvent .KEYCODE_MEDIA_FAST_FORWARD ) {
1443
- if (player .getPlaybackState () != Player .STATE_ENDED ) {
1459
+ if (player .getPlaybackState () != Player .STATE_ENDED
1460
+ && player .isCommandAvailable (COMMAND_SEEK_FORWARD )) {
1444
1461
player .seekForward ();
1445
1462
}
1446
- } else if (keyCode == KeyEvent .KEYCODE_MEDIA_REWIND ) {
1463
+ } else if (keyCode == KeyEvent .KEYCODE_MEDIA_REWIND
1464
+ && player .isCommandAvailable (COMMAND_SEEK_BACK )) {
1447
1465
player .seekBack ();
1448
1466
} else if (event .getRepeatCount () == 0 ) {
1449
1467
switch (keyCode ) {
@@ -1458,10 +1476,14 @@ public boolean dispatchMediaKeyEvent(KeyEvent event) {
1458
1476
dispatchPause (player );
1459
1477
break ;
1460
1478
case KeyEvent .KEYCODE_MEDIA_NEXT :
1461
- player .seekToNext ();
1479
+ if (player .isCommandAvailable (COMMAND_SEEK_TO_NEXT )) {
1480
+ player .seekToNext ();
1481
+ }
1462
1482
break ;
1463
1483
case KeyEvent .KEYCODE_MEDIA_PREVIOUS :
1464
- player .seekToPrevious ();
1484
+ if (player .isCommandAvailable (COMMAND_SEEK_TO_PREVIOUS )) {
1485
+ player .seekToPrevious ();
1486
+ }
1465
1487
break ;
1466
1488
default :
1467
1489
break ;
@@ -1501,7 +1523,10 @@ private void onLayoutChange(
1501
1523
}
1502
1524
1503
1525
private boolean shouldEnablePlayPauseButton () {
1504
- return player != null && !player .getCurrentTimeline ().isEmpty ();
1526
+ return player != null
1527
+ && player .isCommandAvailable (COMMAND_PLAY_PAUSE )
1528
+ && (!player .isCommandAvailable (COMMAND_GET_TIMELINE )
1529
+ || !player .getCurrentTimeline ().isEmpty ());
1505
1530
}
1506
1531
1507
1532
private boolean shouldShowPauseButton () {
@@ -1522,16 +1547,21 @@ private void dispatchPlayPause(Player player) {
1522
1547
1523
1548
private void dispatchPlay (Player player ) {
1524
1549
@ State int state = player .getPlaybackState ();
1525
- if (state == Player .STATE_IDLE ) {
1550
+ if (state == Player .STATE_IDLE && player . isCommandAvailable ( COMMAND_PREPARE ) ) {
1526
1551
player .prepare ();
1527
- } else if (state == Player .STATE_ENDED ) {
1528
- seekTo (player , player .getCurrentMediaItemIndex (), C .TIME_UNSET );
1552
+ } else if (state == Player .STATE_ENDED
1553
+ && player .isCommandAvailable (COMMAND_SEEK_TO_DEFAULT_POSITION )) {
1554
+ player .seekToDefaultPosition ();
1555
+ }
1556
+ if (player .isCommandAvailable (COMMAND_PLAY_PAUSE )) {
1557
+ player .play ();
1529
1558
}
1530
- player .play ();
1531
1559
}
1532
1560
1533
1561
private void dispatchPause (Player player ) {
1534
- player .pause ();
1562
+ if (player .isCommandAvailable (COMMAND_PLAY_PAUSE )) {
1563
+ player .pause ();
1564
+ }
1535
1565
}
1536
1566
1537
1567
@ SuppressLint ("InlinedApi" )
@@ -1547,13 +1577,18 @@ private static boolean isHandledMediaKey(int keyCode) {
1547
1577
}
1548
1578
1549
1579
/**
1550
- * Returns whether the specified {@code timeline } can be shown on a multi-window time bar.
1580
+ * Returns whether the specified {@code player } can be shown on a multi-window time bar.
1551
1581
*
1552
- * @param timeline The {@link Timeline } to check.
1582
+ * @param player The {@link Player } to check.
1553
1583
* @param window A scratch {@link Timeline.Window} instance.
1554
1584
* @return Whether the specified timeline can be shown on a multi-window time bar.
1555
1585
*/
1556
- private static boolean canShowMultiWindowTimeBar (Timeline timeline , Timeline .Window window ) {
1586
+ private static boolean canShowMultiWindowTimeBar (Player player , Timeline .Window window ) {
1587
+ if (!player .isCommandAvailable (COMMAND_GET_TIMELINE )
1588
+ || !player .isCommandAvailable (COMMAND_SEEK_TO_MEDIA_ITEM )) {
1589
+ return false ;
1590
+ }
1591
+ Timeline timeline = player .getCurrentTimeline ();
1557
1592
if (timeline .getWindowCount () > MAX_WINDOWS_FOR_MULTI_WINDOW_TIME_BAR ) {
1558
1593
return false ;
1559
1594
}
@@ -1674,22 +1709,33 @@ public void onClick(View view) {
1674
1709
}
1675
1710
controlViewLayoutManager .resetHideCallbacks ();
1676
1711
if (nextButton == view ) {
1677
- player .seekToNext ();
1712
+ if (player .isCommandAvailable (COMMAND_SEEK_TO_NEXT )) {
1713
+ player .seekToNext ();
1714
+ }
1678
1715
} else if (previousButton == view ) {
1679
- player .seekToPrevious ();
1716
+ if (player .isCommandAvailable (COMMAND_SEEK_TO_PREVIOUS )) {
1717
+ player .seekToPrevious ();
1718
+ }
1680
1719
} else if (fastForwardButton == view ) {
1681
- if (player .getPlaybackState () != Player .STATE_ENDED ) {
1720
+ if (player .getPlaybackState () != Player .STATE_ENDED
1721
+ && player .isCommandAvailable (COMMAND_SEEK_FORWARD )) {
1682
1722
player .seekForward ();
1683
1723
}
1684
1724
} else if (rewindButton == view ) {
1685
- player .seekBack ();
1725
+ if (player .isCommandAvailable (COMMAND_SEEK_BACK )) {
1726
+ player .seekBack ();
1727
+ }
1686
1728
} else if (playPauseButton == view ) {
1687
1729
dispatchPlayPause (player );
1688
1730
} else if (repeatToggleButton == view ) {
1689
- player .setRepeatMode (
1690
- RepeatModeUtil .getNextRepeatMode (player .getRepeatMode (), repeatToggleModes ));
1731
+ if (player .isCommandAvailable (COMMAND_SET_REPEAT_MODE )) {
1732
+ player .setRepeatMode (
1733
+ RepeatModeUtil .getNextRepeatMode (player .getRepeatMode (), repeatToggleModes ));
1734
+ }
1691
1735
} else if (shuffleButton == view ) {
1692
- player .setShuffleModeEnabled (!player .getShuffleModeEnabled ());
1736
+ if (player .isCommandAvailable (COMMAND_SET_SHUFFLE_MODE )) {
1737
+ player .setShuffleModeEnabled (!player .getShuffleModeEnabled ());
1738
+ }
1693
1739
} else if (settingsButton == view ) {
1694
1740
controlViewLayoutManager .removeHideCallbacks ();
1695
1741
displaySettingsWindow (settingsAdapter , settingsButton );
@@ -1892,7 +1938,8 @@ public void onBindViewHolderAtZeroPosition(SubSettingViewHolder holder) {
1892
1938
holder .checkView .setVisibility (isTrackSelectionOff ? VISIBLE : INVISIBLE );
1893
1939
holder .itemView .setOnClickListener (
1894
1940
v -> {
1895
- if (player != null ) {
1941
+ if (player != null
1942
+ && player .isCommandAvailable (COMMAND_SET_TRACK_SELECTION_PARAMETERS )) {
1896
1943
TrackSelectionParameters trackSelectionParameters =
1897
1944
player .getTrackSelectionParameters ();
1898
1945
player .setTrackSelectionParameters (
@@ -1933,7 +1980,8 @@ public void onBindViewHolderAtZeroPosition(SubSettingViewHolder holder) {
1933
1980
holder .checkView .setVisibility (hasSelectionOverride ? INVISIBLE : VISIBLE );
1934
1981
holder .itemView .setOnClickListener (
1935
1982
v -> {
1936
- if (player == null ) {
1983
+ if (player == null
1984
+ || !player .isCommandAvailable (COMMAND_SET_TRACK_SELECTION_PARAMETERS )) {
1937
1985
return ;
1938
1986
}
1939
1987
TrackSelectionParameters trackSelectionParameters =
@@ -2036,6 +2084,9 @@ public void onBindViewHolder(SubSettingViewHolder holder, int position) {
2036
2084
holder .checkView .setVisibility (explicitlySelected ? VISIBLE : INVISIBLE );
2037
2085
holder .itemView .setOnClickListener (
2038
2086
v -> {
2087
+ if (!player .isCommandAvailable (COMMAND_SET_TRACK_SELECTION_PARAMETERS )) {
2088
+ return ;
2089
+ }
2039
2090
TrackSelectionParameters trackSelectionParameters =
2040
2091
player .getTrackSelectionParameters ();
2041
2092
player .setTrackSelectionParameters (
0 commit comments