• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 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 
17 package com.example.android.supportv7.media;
18 
19 import com.example.android.supportv7.R;
20 
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.content.IntentFilter.MalformedMimeTypeException;
25 import android.content.res.Resources;
26 import android.media.MediaRouter;
27 import android.net.Uri;
28 import android.os.Bundle;
29 import android.support.v7.media.MediaControlIntent;
30 import android.support.v7.media.MediaItemStatus;
31 import android.support.v7.media.MediaRouteProvider;
32 import android.support.v7.media.MediaRouter.ControlRequestCallback;
33 import android.support.v7.media.MediaRouteProviderDescriptor;
34 import android.support.v7.media.MediaRouteDescriptor;
35 import android.util.Log;
36 import android.widget.Toast;
37 
38 import java.util.ArrayList;
39 import java.util.UUID;
40 
41 /**
42  * Demonstrates how to create a custom media route provider.
43  *
44  * @see SampleMediaRouteProviderService
45  */
46 final class SampleMediaRouteProvider extends MediaRouteProvider {
47     private static final String TAG = "SampleMediaRouteProvider";
48 
49     private static final String FIXED_VOLUME_ROUTE_ID = "fixed";
50     private static final String VARIABLE_VOLUME_ROUTE_ID = "variable";
51     private static final int VOLUME_MAX = 10;
52 
53     /**
54      * A custom media control intent category for special requests that are
55      * supported by this provider's routes.
56      */
57     public static final String CATEGORY_SAMPLE_ROUTE =
58             "com.example.android.supportv7.media.CATEGORY_SAMPLE_ROUTE";
59 
60     /**
61      * A custom media control intent action for special requests that are
62      * supported by this provider's routes.
63      * <p>
64      * This particular request is designed to return a bundle of not very
65      * interesting statistics for demonstration purposes.
66      * </p>
67      *
68      * @see #DATA_PLAYBACK_COUNT
69      */
70     public static final String ACTION_GET_STATISTICS =
71             "com.example.android.supportv7.media.ACTION_GET_STATISTICS";
72 
73     /**
74      * {@link #ACTION_GET_STATISTICS} result data: Number of times the
75      * playback action was invoked.
76      */
77     public static final String DATA_PLAYBACK_COUNT =
78             "com.example.android.supportv7.media.EXTRA_PLAYBACK_COUNT";
79 
80     private static final ArrayList<IntentFilter> CONTROL_FILTERS;
81     static {
82         IntentFilter f1 = new IntentFilter();
83         f1.addCategory(CATEGORY_SAMPLE_ROUTE);
84         f1.addAction(ACTION_GET_STATISTICS);
85 
86         IntentFilter f2 = new IntentFilter();
87         f2.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
88         f2.addAction(MediaControlIntent.ACTION_PLAY);
89         f2.addDataScheme("http");
90         f2.addDataScheme("https");
addDataTypeUnchecked(f2, "video/*")91         addDataTypeUnchecked(f2, "video/*");
92 
93         CONTROL_FILTERS = new ArrayList<IntentFilter>();
94         CONTROL_FILTERS.add(f1);
95         CONTROL_FILTERS.add(f2);
96     }
97 
addDataTypeUnchecked(IntentFilter filter, String type)98     private static void addDataTypeUnchecked(IntentFilter filter, String type) {
99         try {
100             filter.addDataType(type);
101         } catch (MalformedMimeTypeException ex) {
102             throw new RuntimeException(ex);
103         }
104     }
105 
106     private int mVolume = 5;
107     private int mPlaybackCount;
108 
SampleMediaRouteProvider(Context context)109     public SampleMediaRouteProvider(Context context) {
110         super(context);
111 
112         publishRoutes();
113     }
114 
115     @Override
onCreateRouteController(String routeId)116     public RouteController onCreateRouteController(String routeId) {
117         return new SampleRouteController(routeId);
118     }
119 
publishRoutes()120     private void publishRoutes() {
121         Resources r = getContext().getResources();
122 
123         MediaRouteDescriptor routeDescriptor1 = new MediaRouteDescriptor.Builder(
124                 FIXED_VOLUME_ROUTE_ID,
125                 r.getString(R.string.fixed_volume_route_name))
126                 .setDescription(r.getString(R.string.sample_route_description))
127                 .addControlFilters(CONTROL_FILTERS)
128                 .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
129                 .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED)
130                 .setVolume(VOLUME_MAX)
131                 .build();
132 
133         MediaRouteDescriptor routeDescriptor2 = new MediaRouteDescriptor.Builder(
134                 VARIABLE_VOLUME_ROUTE_ID,
135                 r.getString(R.string.variable_volume_route_name))
136                 .setDescription(r.getString(R.string.sample_route_description))
137                 .addControlFilters(CONTROL_FILTERS)
138                 .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE)
139                 .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
140                 .setVolumeMax(VOLUME_MAX)
141                 .setVolume(mVolume)
142                 .build();
143 
144         MediaRouteProviderDescriptor providerDescriptor =
145                 new MediaRouteProviderDescriptor.Builder()
146                 .addRoute(routeDescriptor1)
147                 .addRoute(routeDescriptor2)
148                 .build();
149         setDescriptor(providerDescriptor);
150     }
151 
generateStreamId()152     private String generateStreamId() {
153         return UUID.randomUUID().toString();
154     }
155 
156     private final class SampleRouteController extends MediaRouteProvider.RouteController {
157         private final String mRouteId;
158 
SampleRouteController(String routeId)159         public SampleRouteController(String routeId) {
160             mRouteId = routeId;
161             Log.d(TAG, mRouteId + ": Controller created");
162         }
163 
164         @Override
onRelease()165         public void onRelease() {
166             Log.d(TAG, mRouteId + ": Controller released");
167         }
168 
169         @Override
onSelect()170         public void onSelect() {
171             Log.d(TAG, mRouteId + ": Selected");
172         }
173 
174         @Override
onUnselect()175         public void onUnselect() {
176             Log.d(TAG, mRouteId + ": Unselected");
177         }
178 
179         @Override
onSetVolume(int volume)180         public void onSetVolume(int volume) {
181             Log.d(TAG, mRouteId + ": Set volume to " + volume);
182             if (mRouteId.equals(VARIABLE_VOLUME_ROUTE_ID)) {
183                 setVolumeInternal(volume);
184             }
185         }
186 
187         @Override
onUpdateVolume(int delta)188         public void onUpdateVolume(int delta) {
189             Log.d(TAG, mRouteId + ": Update volume by " + delta);
190             if (mRouteId.equals(VARIABLE_VOLUME_ROUTE_ID)) {
191                 setVolumeInternal(mVolume + delta);
192             }
193         }
194 
setVolumeInternal(int volume)195         private void setVolumeInternal(int volume) {
196             if (volume >= 0 && volume <= VOLUME_MAX) {
197                 mVolume = volume;
198                 Log.d(TAG, mRouteId + ": New volume is " + mVolume);
199                 publishRoutes();
200             }
201         }
202 
203         @Override
onControlRequest(Intent intent, ControlRequestCallback callback)204         public boolean onControlRequest(Intent intent, ControlRequestCallback callback) {
205             Log.d(TAG, mRouteId + ": Received control request " + intent);
206             if (intent.getAction().equals(MediaControlIntent.ACTION_PLAY)
207                     && intent.hasCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
208                     && intent.getData() != null) {
209                 mPlaybackCount +=1;
210 
211                 // TODO: Handle queue ids.
212                 Uri uri = intent.getData();
213                 long contentPositionMillis = intent.getLongExtra(
214                         MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, 0);
215                 Bundle metadata = intent.getBundleExtra(MediaControlIntent.EXTRA_ITEM_METADATA);
216                 Bundle headers = intent.getBundleExtra(
217                         MediaControlIntent.EXTRA_ITEM_HTTP_HEADERS);
218 
219                 Log.d(TAG, mRouteId + ": Received play request, uri=" + uri
220                         + ", contentPositionMillis=" + contentPositionMillis
221                         + ", metadata=" + metadata
222                         + ", headers=" + headers);
223 
224                 if (uri.toString().contains("hats")) {
225                     // Simulate generating an error whenever the uri contains the word 'hats'.
226                     Toast.makeText(getContext(), "Route rejected play request: uri=" + uri
227                             + ", no hats allowed!", Toast.LENGTH_LONG).show();
228                     if (callback != null) {
229                         callback.onError("Simulated error.  No hats allowed!", null);
230                     }
231                 } else {
232                     Toast.makeText(getContext(), "Route received play request: uri=" + uri,
233                             Toast.LENGTH_LONG).show();
234                     String streamId = generateStreamId();
235                     if (callback != null) {
236                         MediaItemStatus status = new MediaItemStatus.Builder(
237                                 MediaItemStatus.PLAYBACK_STATE_PLAYING)
238                                 .setContentPosition(contentPositionMillis)
239                                 .build();
240 
241                         Bundle result = new Bundle();
242                         result.putString(MediaControlIntent.EXTRA_ITEM_ID, streamId);
243                         result.putBundle(MediaControlIntent.EXTRA_ITEM_STATUS, status.asBundle());
244                         callback.onResult(result);
245                     }
246                 }
247                 return true;
248             }
249 
250             if (intent.getAction().equals(ACTION_GET_STATISTICS)
251                     && intent.hasCategory(CATEGORY_SAMPLE_ROUTE)) {
252                 Bundle data = new Bundle();
253                 data.putInt(DATA_PLAYBACK_COUNT, mPlaybackCount);
254                 if (callback != null) {
255                     callback.onResult(data);
256                 }
257                 return true;
258             }
259             return false;
260         }
261     }
262 }