SampleMediaRouteProvider.java revision fcb6a9d1caaf43ae41f859ebb58a4483b12475a1
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
17package com.example.android.supportv7.media;
18
19import com.example.android.supportv7.R;
20
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
24import android.content.IntentFilter.MalformedMimeTypeException;
25import android.content.res.Resources;
26import android.media.MediaRouter;
27import android.net.Uri;
28import android.os.Bundle;
29import android.support.v7.media.MediaControlIntent;
30import android.support.v7.media.MediaItemStatus;
31import android.support.v7.media.MediaRouteProvider;
32import android.support.v7.media.MediaRouter.ControlRequestCallback;
33import android.support.v7.media.MediaRouteProviderDescriptor;
34import android.support.v7.media.MediaRouteDescriptor;
35import android.util.Log;
36import android.widget.Toast;
37
38import java.util.ArrayList;
39import java.util.UUID;
40
41/**
42 * Demonstrates how to create a custom media route provider.
43 *
44 * @see SampleMediaRouteProviderService
45 */
46final 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");
91        addDataTypeUnchecked(f2, "video/*");
92
93        CONTROL_FILTERS = new ArrayList<IntentFilter>();
94        CONTROL_FILTERS.add(f1);
95        CONTROL_FILTERS.add(f2);
96    }
97
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
109    public SampleMediaRouteProvider(Context context) {
110        super(context);
111
112        publishRoutes();
113    }
114
115    @Override
116    public RouteController onCreateRouteController(String routeId) {
117        return new SampleRouteController(routeId);
118    }
119
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                .addDiscoverableControlFilters(CONTROL_FILTERS)
149                .build();
150        setDescriptor(providerDescriptor);
151    }
152
153    private String generateStreamId() {
154        return UUID.randomUUID().toString();
155    }
156
157    private final class SampleRouteController extends MediaRouteProvider.RouteController {
158        private final String mRouteId;
159
160        public SampleRouteController(String routeId) {
161            mRouteId = routeId;
162            Log.d(TAG, mRouteId + ": Controller created");
163        }
164
165        @Override
166        public void onRelease() {
167            Log.d(TAG, mRouteId + ": Controller released");
168        }
169
170        @Override
171        public void onSelect() {
172            Log.d(TAG, mRouteId + ": Selected");
173        }
174
175        @Override
176        public void onUnselect() {
177            Log.d(TAG, mRouteId + ": Unselected");
178        }
179
180        @Override
181        public void onSetVolume(int volume) {
182            Log.d(TAG, mRouteId + ": Set volume to " + volume);
183            if (mRouteId.equals(VARIABLE_VOLUME_ROUTE_ID)) {
184                setVolumeInternal(volume);
185            }
186        }
187
188        @Override
189        public void onUpdateVolume(int delta) {
190            Log.d(TAG, mRouteId + ": Update volume by " + delta);
191            if (mRouteId.equals(VARIABLE_VOLUME_ROUTE_ID)) {
192                setVolumeInternal(mVolume + delta);
193            }
194        }
195
196        private void setVolumeInternal(int volume) {
197            if (volume >= 0 && volume <= VOLUME_MAX) {
198                mVolume = volume;
199                Log.d(TAG, mRouteId + ": New volume is " + mVolume);
200                publishRoutes();
201            }
202        }
203
204        @Override
205        public boolean onControlRequest(Intent intent, ControlRequestCallback callback) {
206            Log.d(TAG, mRouteId + ": Received control request " + intent);
207            if (intent.getAction().equals(MediaControlIntent.ACTION_PLAY)
208                    && intent.hasCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
209                    && intent.getData() != null) {
210                mPlaybackCount +=1;
211
212                Uri uri = intent.getData();
213                int queueBehavior = intent.getIntExtra(
214                        MediaControlIntent.EXTRA_ITEM_QUEUE_BEHAVIOR,
215                        MediaControlIntent.ITEM_QUEUE_BEHAVIOR_PLAY_NOW);
216                double contentPosition = intent.getDoubleExtra(
217                        MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, 0);
218                Bundle metadata = intent.getBundleExtra(MediaControlIntent.EXTRA_ITEM_METADATA);
219                Bundle headers = intent.getBundleExtra(
220                        MediaControlIntent.EXTRA_ITEM_HTTP_HEADERS);
221
222                Log.d(TAG, mRouteId + ": Received play request, uri=" + uri
223                        + ", queueBehavior=" + queueBehavior
224                        + ", contentPosition=" + contentPosition
225                        + ", metadata=" + metadata
226                        + ", headers=" + headers);
227
228                if (uri.toString().contains("hats")) {
229                    // Simulate generating an error whenever the uri contains the word 'hats'.
230                    Toast.makeText(getContext(), "Route rejected play request: uri=" + uri
231                            + ", no hats allowed!", Toast.LENGTH_LONG).show();
232                    if (callback != null) {
233                        callback.onError("Simulated error.  No hats allowed!", null);
234                    }
235                } else {
236                    Toast.makeText(getContext(), "Route received play request: uri=" + uri,
237                            Toast.LENGTH_LONG).show();
238                    String streamId = generateStreamId();
239                    if (callback != null) {
240                        MediaItemStatus status = new MediaItemStatus.Builder(
241                                MediaItemStatus.PLAYBACK_STATE_PLAYING)
242                                .setContentPosition(contentPosition)
243                                .build();
244
245                        Bundle result = new Bundle();
246                        result.putString(MediaControlIntent.EXTRA_ITEM_ID, streamId);
247                        result.putBundle(MediaControlIntent.EXTRA_ITEM_STATUS, status.asBundle());
248                        callback.onResult(result);
249                    }
250                }
251                return true;
252            }
253
254            if (intent.getAction().equals(ACTION_GET_STATISTICS)
255                    && intent.hasCategory(CATEGORY_SAMPLE_ROUTE)) {
256                Bundle data = new Bundle();
257                data.putInt(DATA_PLAYBACK_COUNT, mPlaybackCount);
258                if (callback != null) {
259                    callback.onResult(data);
260                }
261                return true;
262            }
263            return false;
264        }
265    }
266}