Skip to content

Commit 748f01f

Browse files
Googlercopybara-github
authored andcommitted
Add support for setting up the media route button as a menu item
The media3-cast module offers a MediaRouteButtonFactory that sets up the media route button as a menu item on an action bar menu. PiperOrigin-RevId: 819373612
1 parent f52e9b8 commit 748f01f

File tree

4 files changed

+126
-4
lines changed

4 files changed

+126
-4
lines changed

RELEASENOTES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@
336336
* Enable remote to local transfers in `DefaultCastOptionsProvider`.
337337
* Add support for Cast in the Session demo.
338338
* Add support for displaying a media route button on a Composable UI.
339+
* Add support for displaying a media route button on an action bar menu.
339340
* Test Utilities:
340341
* Add maximum time diff for the auto-advancing behavior of `FakeClock`. It
341342
defaults to 1 second, but is configurable via `FakeClock.Builder`.

demos/session/src/main/java/androidx/media3/demo/session/MainActivity.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,12 @@ import android.widget.Toast
3333
import androidx.activity.OnBackPressedCallback
3434
import androidx.appcompat.app.AppCompatActivity
3535
import androidx.core.content.ContextCompat
36+
import androidx.media3.cast.MediaRouteButtonFactory
3637
import androidx.media3.common.MediaItem
38+
import androidx.media3.common.util.UnstableApi
3739
import androidx.media3.session.LibraryResult
3840
import androidx.media3.session.MediaBrowser
3941
import androidx.media3.session.SessionToken
40-
import com.google.android.gms.cast.framework.CastButtonFactory
4142
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
4243
import com.google.common.util.concurrent.ListenableFuture
4344

@@ -97,10 +98,11 @@ class MainActivity : AppCompatActivity() {
9798
}
9899
}
99100

101+
@OptIn(UnstableApi::class) // MediaRouteButtonFactory is unstable API.
100102
override fun onCreateOptionsMenu(menu: Menu): Boolean {
101103
super.onCreateOptionsMenu(menu)
102104
getMenuInflater().inflate(R.menu.menu, menu)
103-
CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.cast_menu_item)
105+
val unused = MediaRouteButtonFactory.setUpMediaRouteButton(this, menu, R.id.cast_menu_item)
104106
return true
105107
}
106108

demos/session/src/main/java/androidx/media3/demo/session/PlayableFolderActivity.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,13 @@ import android.widget.ListView
3131
import android.widget.TextView
3232
import androidx.appcompat.app.AppCompatActivity
3333
import androidx.core.content.ContextCompat
34+
import androidx.media3.cast.MediaRouteButtonFactory
3435
import androidx.media3.common.C
3536
import androidx.media3.common.MediaItem
3637
import androidx.media3.common.Player
38+
import androidx.media3.common.util.UnstableApi
3739
import androidx.media3.session.MediaBrowser
3840
import androidx.media3.session.SessionToken
39-
import com.google.android.gms.cast.framework.CastButtonFactory
4041
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
4142
import com.google.android.material.snackbar.BaseTransientBottomBar
4243
import com.google.android.material.snackbar.Snackbar
@@ -111,10 +112,11 @@ class PlayableFolderActivity : AppCompatActivity() {
111112
}
112113
}
113114

115+
@OptIn(UnstableApi::class)
114116
override fun onCreateOptionsMenu(menu: Menu): Boolean {
115117
super.onCreateOptionsMenu(menu)
116118
getMenuInflater().inflate(R.menu.menu, menu)
117-
CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.cast_menu_item)
119+
val unused = MediaRouteButtonFactory.setUpMediaRouteButton(this, menu, R.id.cast_menu_item)
118120
return true
119121
}
120122

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright 2025 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package androidx.media3.cast;
17+
18+
import android.content.Context;
19+
import android.view.Menu;
20+
import android.view.MenuItem;
21+
import androidx.concurrent.futures.CallbackToFutureAdapter;
22+
import androidx.media3.common.util.BackgroundExecutor;
23+
import androidx.media3.common.util.UnstableApi;
24+
import com.google.android.gms.cast.framework.CastButtonFactory;
25+
import com.google.common.util.concurrent.ListenableFuture;
26+
27+
/** A factory class to set up a media route button. */
28+
@UnstableApi
29+
public final class MediaRouteButtonFactory {
30+
31+
/**
32+
* Sets up a media route button in the action bar menu with an asynchronous callback, which will
33+
* not block the caller thread. Returns a {@link ListenableFuture} with the menu item of the media
34+
* route button
35+
*
36+
* <p>The application should define a menu resource to include the {@link
37+
* androidx.mediarouter.app.MediaRouteActionProvider} as the action provider of the menu item.
38+
*
39+
* <pre>{@code
40+
* <menu xmlns:android="http://schemas.android.com/apk/res/android"
41+
* xmlns:app="http://schemas.android.com/apk/res-auto">
42+
* <item android:id="@+id/media_route_menu_item"
43+
* android:title="@string/media_route_menu_title"
44+
* app:showAsAction="always"
45+
* app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"/>
46+
* </menu>
47+
* }</pre>
48+
*
49+
* Then the application can inflate the /res/menu/sample_media_route_button_menu.xml and set up
50+
* the media route button in the action bar menu as follows.
51+
*
52+
* <pre>{@code
53+
* public class MyActivity extends AppCompatActivity {
54+
* ...
55+
* @Override
56+
* public boolean onCreateOptionsMenu(Menu menu) {
57+
* ...
58+
* getMenuInflater().inflate(R.menu.sample_media_route_button_menu, menu);
59+
* ListenableFuture<MenuItem> menuItemFuture =
60+
* MediaRouteButtonFactory.setUpMediaRouteButton(this, menu, R.id.media_route_menu_item);
61+
* Futures.addCallback(
62+
* menuItemFuture,
63+
* new FutureCallback<MenuItem>() {
64+
* @Override
65+
* public void onSuccess(MenuItem menuItem) {
66+
* // Do something with the menu item.
67+
* }
68+
*
69+
* @Override
70+
* public void onFailure(Throwable t) {
71+
* // Handle the failure.
72+
* }
73+
* },
74+
* executor);
75+
* ...
76+
* }
77+
* }
78+
* }</pre>
79+
*
80+
* <p>If setting up the media route button succeeds, the future will resolve to the menu item of
81+
* the media route button. The application can do further operations with the menu item, such as
82+
* showing an introductory overlay to highlight the media route button to users.
83+
*
84+
* <p>If setting up the media route button fails, the future may fail with an exception. Consumers
85+
* should handle the failure gracefully, for example by not showing the media route button.
86+
*
87+
* <p>The callback of the returned {@link ListenableFuture} may be called on a different thread
88+
* than the caller's thread. If the caller wants to update the UI in the callbacks, it is
89+
* responsible for forwarding the callback to the UI thread.
90+
*
91+
* <p>Clicking on the media route button opens a dialog that allows the user to select a remote
92+
* device for transferring media.
93+
*
94+
* <p>See {@link androidx.mediarouter.app.MediaRouteActionProvider} for more details.
95+
*
96+
* @param context The {@link Context} for creating the media route button.
97+
* @param menu The {@link Menu} to which the menu item with the button will be added.
98+
* @param menuResourceId The resource ID of the menu item for the media route button.
99+
* @return A {@link ListenableFuture} that resolves to the created {@link MenuItem} when the media
100+
* route button is set up successfully. The future may fail with {link
101+
* IllegalArgumentException} if the menu doesn't contain a menu item with the given {@code
102+
* menuResourceId} identifier or the menu item doesn't have a {@link
103+
* androidx.mediarouter.app.MediaRouteActionProvider} as its action provider, and may fail
104+
* with {@link IllegalStateException} if this method is not called on the main thread.
105+
*/
106+
public static ListenableFuture<MenuItem> setUpMediaRouteButton(
107+
Context context, Menu menu, int menuResourceId) {
108+
return CallbackToFutureAdapter.getFuture(
109+
completer ->
110+
CastButtonFactory.setUpMediaRouteButton(
111+
context, BackgroundExecutor.get(), menu, menuResourceId)
112+
.addOnSuccessListener(completer::set)
113+
.addOnFailureListener(completer::setException));
114+
}
115+
116+
private MediaRouteButtonFactory() {}
117+
}

0 commit comments

Comments
 (0)