SampleMediaRouteProvider.java revision b885388f83d1651f330039930483c06ef21b7e6e
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 .build(); 149 setDescriptor(providerDescriptor); 150 } 151 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 159 public SampleRouteController(String routeId) { 160 mRouteId = routeId; 161 Log.d(TAG, mRouteId + ": Controller created"); 162 } 163 164 @Override 165 public void onRelease() { 166 Log.d(TAG, mRouteId + ": Controller released"); 167 } 168 169 @Override 170 public void onSelect() { 171 Log.d(TAG, mRouteId + ": Selected"); 172 } 173 174 @Override 175 public void onUnselect() { 176 Log.d(TAG, mRouteId + ": Unselected"); 177 } 178 179 @Override 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 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 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 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 Uri uri = intent.getData(); 212 double contentPosition = intent.getDoubleExtra( 213 MediaControlIntent.EXTRA_ITEM_CONTENT_POSITION, 0); 214 Bundle metadata = intent.getBundleExtra(MediaControlIntent.EXTRA_ITEM_METADATA); 215 Bundle headers = intent.getBundleExtra( 216 MediaControlIntent.EXTRA_ITEM_HTTP_HEADERS); 217 218 Log.d(TAG, mRouteId + ": Received play request, uri=" + uri 219 + ", contentPosition=" + contentPosition 220 + ", metadata=" + metadata 221 + ", headers=" + headers); 222 223 if (uri.toString().contains("hats")) { 224 // Simulate generating an error whenever the uri contains the word 'hats'. 225 Toast.makeText(getContext(), "Route rejected play request: uri=" + uri 226 + ", no hats allowed!", Toast.LENGTH_LONG).show(); 227 if (callback != null) { 228 callback.onError("Simulated error. No hats allowed!", null); 229 } 230 } else { 231 Toast.makeText(getContext(), "Route received play request: uri=" + uri, 232 Toast.LENGTH_LONG).show(); 233 String streamId = generateStreamId(); 234 if (callback != null) { 235 MediaItemStatus status = new MediaItemStatus.Builder( 236 MediaItemStatus.PLAYBACK_STATE_PLAYING) 237 .setContentPosition(contentPosition) 238 .build(); 239 240 Bundle result = new Bundle(); 241 result.putString(MediaControlIntent.EXTRA_ITEM_ID, streamId); 242 result.putBundle(MediaControlIntent.EXTRA_ITEM_STATUS, status.asBundle()); 243 callback.onResult(result); 244 } 245 } 246 return true; 247 } 248 249 if (intent.getAction().equals(ACTION_GET_STATISTICS) 250 && intent.hasCategory(CATEGORY_SAMPLE_ROUTE)) { 251 Bundle data = new Bundle(); 252 data.putInt(DATA_PLAYBACK_COUNT, mPlaybackCount); 253 if (callback != null) { 254 callback.onResult(data); 255 } 256 return true; 257 } 258 return false; 259 } 260 } 261}