MediaButtonReceiver.java revision d3c5347b3ec0025ec906e2053eaa9b97287c46a5
1/* 2 * Copyright (C) 2015 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 android.support.v4.media.session; 18 19import android.app.Service; 20import android.content.BroadcastReceiver; 21import android.content.ComponentName; 22import android.content.Context; 23import android.content.Intent; 24import android.content.pm.PackageManager; 25import android.content.pm.ResolveInfo; 26import android.support.v4.media.MediaBrowserServiceCompat; 27import android.support.v4.media.session.MediaControllerCompat; 28import android.support.v4.media.session.MediaSessionCompat; 29import android.view.KeyEvent; 30 31import java.util.List; 32 33/** 34 * A media button receiver receives and helps translate hardware media playback buttons, 35 * such as those found on wired and wireless headsets, into the appropriate callbacks 36 * in your app. 37 * <p /> 38 * You can add this MediaButtonReceiver to your app by adding it directly to your 39 * AndroidManifest.xml: 40 * <pre> 41 * <receiver android:name="android.support.v4.media.session.MediaButtonReceiver" > 42 * <intent-filter> 43 * <action android:name="android.intent.action.MEDIA_BUTTON" /> 44 * </intent-filter> 45 * </receiver> 46 * </pre> 47 * This class assumes you have a {@link Service} in your app that controls 48 * media playback via a {@link MediaSessionCompat} - all {@link Intent}s received by 49 * the MediaButtonReceiver will be forwarded to that service. 50 * <p /> 51 * First priority is given to a {@link Service} 52 * that includes an intent filter that handles {@link Intent#ACTION_MEDIA_BUTTON}: 53 * <pre> 54 * <service android:name="com.example.android.MediaPlaybackService" > 55 * <intent-filter> 56 * <action android:name="android.intent.action.MEDIA_BUTTON" /> 57 * </intent-filter> 58 * </service> 59 * </pre> 60 * 61 * If such a {@link Service} is not found, MediaButtonReceiver will attempt to 62 * find a media browser service implementation. 63 * If neither is available or more than one valid service/media browser service is found, an 64 * {@link IllegalStateException} will be thrown. 65 * <p /> 66 * Events can then be handled in {@link Service#onStartCommand(Intent, int, int)} by calling 67 * {@link MediaButtonReceiver#handleIntent(MediaSessionCompat, Intent)}, passing in 68 * your current {@link MediaSessionCompat}: 69 * <pre> 70 * private MediaSessionCompat mMediaSessionCompat = ...; 71 * 72 * public int onStartCommand(Intent intent, int flags, int startId) { 73 * MediaButtonReceiver.handleIntent(mMediaSessionCompat, intent); 74 * return super.onStartCommand(intent, flags, startId); 75 * } 76 * </pre> 77 * 78 * This ensures that the correct callbacks to {@link MediaSessionCompat.Callback} 79 * will be triggered based on the incoming {@link KeyEvent}. 80 */ 81public class MediaButtonReceiver extends BroadcastReceiver { 82 @Override 83 public void onReceive(Context context, Intent intent) { 84 Intent queryIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); 85 queryIntent.setPackage(context.getPackageName()); 86 PackageManager pm = context.getPackageManager(); 87 List<ResolveInfo> resolveInfos = pm.queryIntentServices(queryIntent, 0); 88 if (resolveInfos.isEmpty()) { 89 // Fall back to looking for any available media browser service 90 queryIntent.setAction(MediaBrowserServiceCompat.SERVICE_INTERFACE); 91 resolveInfos = pm.queryIntentServices(queryIntent, 0); 92 } 93 if (resolveInfos.isEmpty()) { 94 throw new IllegalStateException("Could not find any Service that handles " + 95 Intent.ACTION_MEDIA_BUTTON + " or a media browser service implementation"); 96 } else if (resolveInfos.size() != 1) { 97 throw new IllegalStateException("Expected 1 Service that handles " + 98 queryIntent.getAction() + ", found " + resolveInfos.size() ); 99 } 100 ResolveInfo resolveInfo = resolveInfos.get(0); 101 ComponentName componentName = new ComponentName(resolveInfo.serviceInfo.packageName, 102 resolveInfo.serviceInfo.name); 103 intent.setComponent(componentName); 104 context.startService(intent); 105 } 106 107 /** 108 * Extracts any available {@link KeyEvent} from an {@link Intent#ACTION_MEDIA_BUTTON} 109 * intent, passing it onto the {@link MediaSessionCompat} using 110 * {@link MediaControllerCompat#dispatchMediaButtonEvent(KeyEvent)}, which in turn 111 * will trigger callbacks to the {@link MediaSessionCompat.Callback} registered via 112 * {@link MediaSessionCompat#setCallback(MediaSessionCompat.Callback)}. 113 * <p /> 114 * The returned {@link KeyEvent} is non-null if any {@link KeyEvent} is found and can 115 * be used if any additional processing is needed beyond what is done in the 116 * {@link MediaSessionCompat.Callback}. An example of is to prevent redelivery of a 117 * {@link KeyEvent#KEYCODE_MEDIA_PLAY_PAUSE} Intent in the case of the Service being 118 * restarted (which, by default, will redeliver the last received Intent). 119 * <pre> 120 * KeyEvent keyEvent = MediaButtonReceiver.handleIntent(mediaSession, intent); 121 * if (keyEvent != null && keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) { 122 * Intent emptyIntent = new Intent(intent); 123 * emptyIntent.setAction(""); 124 * startService(emptyIntent); 125 * } 126 * </pre> 127 * @param mediaSessionCompat A {@link MediaSessionCompat} that has a 128 * {@link MediaSessionCompat.Callback} set. 129 * @param intent The intent to parse. 130 * @return The extracted {@link KeyEvent} if found, or null. 131 */ 132 public static KeyEvent handleIntent(MediaSessionCompat mediaSessionCompat, Intent intent) { 133 if (mediaSessionCompat == null || intent == null 134 || !Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction()) 135 || !intent.hasExtra(Intent.EXTRA_KEY_EVENT)) { 136 return null; 137 } 138 KeyEvent ke = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); 139 MediaControllerCompat mediaController = mediaSessionCompat.getController(); 140 mediaController.dispatchMediaButtonEvent(ke); 141 return ke; 142 } 143} 144 145