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}