/* * Copyright (C) 2010 Josh Guilfoyle <jasta@devtcg.org> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. */ package org.devtcg.five.receiver; import org.devtcg.five.Constants; import org.devtcg.five.service.IPlaylistService; import org.devtcg.five.service.PlaylistService; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.telephony.TelephonyManager; import android.util.Log; import android.view.KeyEvent; public class MediaButton extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { KeyEvent event = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); Log.i(Constants.TAG, "MediaButton: intent=" + intent + ", event=" + event); /* * Only process certain keycodes... */ event = filterKeyEvent(event); if (event == null) return; /* * If the dubious headset hook / media play/pause button is pressed, * check the phone state and abort processing if it's active in any way. * This allows the phone app to receive the MediaButton event and * provide a more appropriate action for the user. Note that the main * Music app does not need to do this as they declare a priority for * this receiver of 0, whereas the phone app declares priority 1. */ if (event.getKeyCode() == KeyEvent.KEYCODE_HEADSETHOOK) { TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); if (tm.getCallState() != TelephonyManager.CALL_STATE_IDLE) return; } /* * Only react on ACTION_UP, but consume (via abortBroadcast()) any * actions for these key codes. */ if (event.getAction() == KeyEvent.ACTION_UP) { IPlaylistService service = getServiceInstance(context); /* * If the service is currently running, invoke commands directly to * it via the Binder. Otherwise, start it and deliver the keyevent * commands. After the service starts, it will call back into our * static handleMediaButtonEvent method. */ if (service != null) { try { handleMediaButtonEvent(service, event); } catch (RemoteException e) {} } else { context.startService(new Intent(Intent.ACTION_MEDIA_BUTTON, null, context, PlaylistService.class).putExtra(Intent.EXTRA_KEY_EVENT, event)); } } /* * Don't let the default music app (or any other) consume this event. */ abortBroadcast(); } private IPlaylistService getServiceInstance(Context context) { IBinder binder = peekService(context, new Intent(context, PlaylistService.class)); if (binder != null) return IPlaylistService.Stub.asInterface(binder); else return null; } /** * Narrow down the KeyEvent to only those that we intend to handle. */ private static KeyEvent filterKeyEvent(KeyEvent event) { if (event == null) return null; switch (event.getKeyCode()) { case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: case KeyEvent.KEYCODE_HEADSETHOOK: case KeyEvent.KEYCODE_MEDIA_NEXT: case KeyEvent.KEYCODE_MEDIA_PREVIOUS: case KeyEvent.KEYCODE_MEDIA_STOP: return event; default: return null; } } /** * Handle the media button keycode event provided. * * @throws IllegalArgumentException * When the provided event cannot be handled. These events are * expected to have already been filtered by the time this * method invokes. */ public static void handleMediaButtonEvent(IPlaylistService service, KeyEvent event) throws RemoteException { if (event == null) throw new IllegalArgumentException("event must not be null"); switch (event.getKeyCode()) { case KeyEvent.KEYCODE_HEADSETHOOK: case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: if (!service.isPlaying()) service.play(); else { if (service.isPaused()) service.unpause(); else service.pause(); } break; case KeyEvent.KEYCODE_MEDIA_NEXT: service.next(); break; case KeyEvent.KEYCODE_MEDIA_PREVIOUS: service.previous(); break; case KeyEvent.KEYCODE_MEDIA_STOP: service.stop(); break; default: throw new IllegalArgumentException("cannot handle media button event=" + event); } } }