/* * Copyright 2008-2013, ETH Zürich, Samuel Welten, Michael Kuhn, Tobias Langner, * Sandro Affentranger, Lukas Bossard, Michael Grob, Rahul Jain, * Dominic Langenegger, Sonia Mayor Alonso, Roger Odermatt, Tobias Schlueter, * Yannick Stucki, Sebastian Wendland, Samuel Zehnder, Samuel Zihlmann, * Samuel Zweifel * * This file is part of Jukefox. * * Jukefox 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 3 of the License, or any later version. Jukefox 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. * * You should have received a copy of the GNU General Public License along with * Jukefox. If not, see <http://www.gnu.org/licenses/>. */ package ch.ethz.dcg.pancho3.controller; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; import android.telephony.TelephonyManager; import android.view.KeyEvent; import ch.ethz.dcg.jukefox.commons.utils.AndroidUtils; import ch.ethz.dcg.jukefox.commons.utils.Log; import ch.ethz.dcg.jukefox.controller.Controller; import ch.ethz.dcg.jukefox.model.player.PlayerState; import ch.ethz.dcg.pancho3.controller.player.PlayerService; import ch.ethz.dcg.pancho3.model.JukefoxApplication; import ch.ethz.dcg.pancho3.view.lockscreen.LockScreen; public class JukefoxIntentReceiver extends BroadcastReceiver { /* Support for Android below 2.2, because better Headset Handling is only available from 2.2 * See http://android-developers.blogspot.ch/2010/06/allowing-applications-to-play-nicer.html*/ static { initializeRemoteControlRegistrationMethods(); } /* Support for Android below 2.2, because better Headset Handling is only available from 2.2*/ private static Method mRegisterMediaButtonEventReceiver; private static Method mUnregisterMediaButtonEventReceiver; private final static String TAG = JukefoxIntentReceiver.class.getSimpleName(); // public static long ignoreEventsUntil = 0; private long lastEventTime = 0; public JukefoxIntentReceiver() { super(); } @Override public void onReceive(Context context, Intent intent) { String intentAction = intent.getAction(); long currentTime = System.currentTimeMillis(); Log.v(TAG, "intentAction: " + intentAction + ", time: " + currentTime); if (ignoreEventsAndCancel(currentTime, intent, intentAction)) { return; } if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intentAction)) { JukefoxApplication.setIgnoreEventsTime(currentTime + 2000); handleAudioBecomingNoisy(context, currentTime); } else if (Intent.ACTION_MEDIA_BUTTON.equals(intentAction)) { handleMediaButtons(context, intent); } else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(intentAction)) { handlePhoneState(context, currentTime, intent); } else if (Intent.ACTION_HEADSET_PLUG.equals(intentAction)) { handleHeadsetPlug(context, currentTime, intent); } else if (Intent.ACTION_MEDIA_SCANNER_FINISHED.equals(intentAction)) { handleMediaScannerFinished(context); } else if (Intent.ACTION_SCREEN_ON.equals(intentAction)) { Log.v(TAG, "Screen on"); JukefoxApplication.setScreenOn(true); } else if (Intent.ACTION_SCREEN_OFF.equals(intentAction)) { Log.v(TAG, "Screen off"); JukefoxApplication.setScreenOn(false); /* * unregister and register sensors to make them available even if * the screen is off (see * http://code.google.com/p/android/issues/detail?id=3708) */ JukefoxApplication.reregisterSensors(); Intent intent2 = new Intent(context, LockScreen.class); intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent2); } else if (PlayerService.ACTION_PLAY_DATE_RANGE.equals(intentAction)) { Intent intent2 = new Intent(context, PlayerService.class); intent2.setAction(PlayerService.ACTION_PLAY_DATE_RANGE); intent2.putExtras(intent.getExtras()); context.startService(intent2); } else if (PlayerService.ACTION_PLAY_TAG.equals(intentAction)) { Intent intent2 = new Intent(context, PlayerService.class); intent2.setAction(PlayerService.ACTION_PLAY_TAG); intent2.putExtras(intent.getExtras()); context.startService(intent2); } else if (PlayerService.ACTION_STOP_MUSIC.equals(intentAction)) { Intent intent2 = new Intent(context, PlayerService.class); intent2.setAction(PlayerService.ACTION_STOP_MUSIC); if (intent.getExtras() != null) { intent2.putExtras(intent.getExtras()); } context.startService(intent2); } else if (Intent.ACTION_MEDIA_EJECT.equals(intentAction)) { Intent intent2 = new Intent(context, PlayerService.class); intent2.setAction(PlayerService.ACTION_STOP_MUSIC); if (intent.getExtras() != null) { intent2.putExtras(intent.getExtras()); } context.startService(intent2); } else if (Intent.ACTION_MEDIA_MOUNTED.equals(intentAction)) { Intent intent2 = new Intent(context, PlayerService.class); intent2.setAction(PlayerService.ACTION_MEDIA_MOUNTED); if (intent.getExtras() != null) { intent2.putExtras(intent.getExtras()); } context.startService(intent2); } } private void handleAudioBecomingNoisy(Context context, long currentTime) { JukefoxApplication.ignoreEventsUntil = currentTime + 2000; Intent intent = new Intent(context, PlayerService.class); intent.setAction(PlayerService.ACTION_AUDIOBECOMING_NOISY); context.startService(intent); } private void handleMediaScannerFinished(Context context) { if (AndroidUtils.isSdCardOk()) { Intent intent = new Intent(context, PlayerService.class); intent.setAction(PlayerService.ACTION_MEDIA_SCANNER_FINISHED); context.startService(intent); } } private void handleHeadsetPlug(Context context, long currentTime, Intent intent) { int state = intent.getIntExtra("state", 0); Intent serviceIntent = new Intent(context, PlayerService.class); if (state > 0) { Log.v(TAG, "handling headset plugged"); serviceIntent.setAction(PlayerService.ACTION_HEADSET_PLUGGED); } else { Log.v(TAG, "handling headset unplugged"); serviceIntent.setAction(PlayerService.ACTION_HEADSET_UNPLUGGED); } context.startService(serviceIntent); } private void handlePhoneState(Context context, long currentTime, Intent intent) { String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE); Intent serviceIntent = new Intent(context, PlayerService.class); if (state.equals(TelephonyManager.EXTRA_STATE_RINGING) || state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) { serviceIntent.setAction(PlayerService.ACTION_CALL_START); } else if (state.equals(TelephonyManager.EXTRA_STATE_IDLE)) { serviceIntent.setAction(PlayerService.ACTION_CALL_END); } context.startService(serviceIntent); } private void handleMediaButtons(Context context, Intent intent) { if (JukefoxApplication.ignoreMediaButtons()) { return; } KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); if (event == null) { return; } if (event.getEventTime() - lastEventTime < 100) { lastEventTime = event.getEventTime(); abortBroadcast(); return; } lastEventTime = event.getEventTime(); int action = event.getAction(); if (action == KeyEvent.ACTION_DOWN) { Log.v("Key Down event time:", "" + event.getEventTime()); handleMediaKeyDown(context, event); } abortBroadcast(); } private void handleMediaKeyDown(Context context, KeyEvent event) { int keyCode = event.getKeyCode(); long eventTime = event.getEventTime(); Log.v("Receiver", "got keycode " + keyCode); try { switch (keyCode) { case KeyEvent.KEYCODE_MEDIA_STOP: sendStopIntent(context); break; case KeyEvent.KEYCODE_HEADSETHOOK: case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: sendPlayPauseIntent(context, eventTime); break; case KeyEvent.KEYCODE_MEDIA_NEXT: sendNextIntent(context); break; case KeyEvent.KEYCODE_MEDIA_PREVIOUS: sendPreviousIntent(context); break; } } catch (Exception e) { Log.w(TAG, e); } } private boolean ignoreEventsAndCancel(long currentTime, Intent intent, String intentAction) { if (currentTime < JukefoxApplication.ignoreEventsUntil) { try { Log.v(TAG, intent.getAction()); if (!Intent.ACTION_HEADSET_PLUG.equals(intentAction)) { abortBroadcast(); } } catch (Exception e) { Log.w(TAG, e); } // abortBroadcast(); return true; } return false; } private void sendPlayPauseIntent(Context context, long eventTime) { Intent playButtonIntent = new Intent(context, PlayerService.class); playButtonIntent.setAction(PlayerService.ACTION_HEADSET_PLAY_PAUSE); playButtonIntent.putExtra(Controller.INTENT_EXTRA_EVENT_TIME, eventTime); context.startService(playButtonIntent); } private void sendStopIntent(Context context) { Intent pauseButtonIntent = new Intent(context, PlayerService.class); pauseButtonIntent.setAction(PlayerService.ACTION_STOP_MUSIC); context.startService(pauseButtonIntent); } private void sendNextIntent(Context context) { Intent nextButtonIntent = new Intent(context, PlayerService.class); nextButtonIntent.setAction(PlayerService.ACTION_NEXT); context.startService(nextButtonIntent); } private void sendPreviousIntent(Context context) { Intent previousButtonIntent = new Intent(context, PlayerService.class); previousButtonIntent.setAction(PlayerService.ACTION_PREVIOUS); context.startService(previousButtonIntent); } public static synchronized void updateJukefoxIntentReceiver(JukefoxApplication application) { AudioManager audioManager = (AudioManager) application.getSystemService(Context.AUDIO_SERVICE); ComponentName eventReceiver = new ComponentName(application.getPackageName(), JukefoxIntentReceiver.class.getName()); registerRemoteControl(audioManager, eventReceiver); if (application.getIntentReceiver() != null) { try { application.unregisterReceiver(application.getIntentReceiver()); } catch (IllegalArgumentException e) { Log.w(TAG, e); } application.setIntentReceiver(null); } IntentFilter intf = new IntentFilter(); intf.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY); intf.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); intf.addAction(Intent.ACTION_MEDIA_BUTTON); intf.addAction(Intent.ACTION_HEADSET_PLUG); // if (isPlaybackFunctionalityInitialized) Log.v(TAG, "PlayerState: " + // playManager.getPlayerState()); // if (isPlaybackFunctionalityInitialized && // playManager.getPlayerState() == PlayerState.PLAY && // settings.isLockScreenControls()) { if (JukefoxApplication.getPlayerController().isReady() && JukefoxApplication.getPlayerController() .getPlayerState() == PlayerState.PLAY) { intf.addAction(Intent.ACTION_SCREEN_ON); intf.addAction(Intent.ACTION_SCREEN_OFF); } intf.addAction(PlayerService.ACTION_PLAY_DATE_RANGE); intf.addAction(PlayerService.ACTION_PLAY_TAG); intf.addAction(PlayerService.ACTION_STOP_MUSIC); intf.setPriority(1000); JukefoxIntentReceiver intentReceiver = new JukefoxIntentReceiver(); application.setIntentReceiver(intentReceiver); application.registerReceiver(intentReceiver, intf); intf = new IntentFilter(); intf.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED); intf.addAction(Intent.ACTION_MEDIA_EJECT); intf.addAction(Intent.ACTION_MEDIA_MOUNTED); intf.addDataScheme("file"); application.registerReceiver(intentReceiver, intf); } /* Support for Android below 2.2, because better Headset Handling is only available from 2.2*/ private static void initializeRemoteControlRegistrationMethods() { try { if (mRegisterMediaButtonEventReceiver == null) { mRegisterMediaButtonEventReceiver = AudioManager.class.getMethod( "registerMediaButtonEventReceiver", new Class[] { ComponentName.class }); } if (mUnregisterMediaButtonEventReceiver == null) { mUnregisterMediaButtonEventReceiver = AudioManager.class.getMethod( "unregisterMediaButtonEventReceiver", new Class[] { ComponentName.class }); } /* success, this device will take advantage of better remote */ /* control event handling */ } catch (NoSuchMethodException nsme) { /* failure, still using the legacy behavior, but this app */ /* is future-proof! */ } } /* Support for Android below 2.2, because better Headset Handling is only available from 2.2*/ private static void registerRemoteControl(AudioManager mAudioManager, ComponentName intentReceiver) { try { if (mRegisterMediaButtonEventReceiver == null) { return; } mRegisterMediaButtonEventReceiver.invoke(mAudioManager, intentReceiver); } catch (InvocationTargetException ite) { /* unpack original exception when possible */ Throwable cause = ite.getCause(); if (cause instanceof RuntimeException) { throw (RuntimeException) cause; } else if (cause instanceof Error) { throw (Error) cause; } else { /* unexpected checked exception; wrap and re-throw */ throw new RuntimeException(ite); } } catch (IllegalAccessException ie) { Log.e("MyApp", "unexpected " + ie); } } /* Support for Android below 2.2, because better Headset Handling is only available from 2.2*/ private static void unregisterRemoteControl(AudioManager mAudioManager, ComponentName intentReceiver) { try { if (mUnregisterMediaButtonEventReceiver == null) { return; } mUnregisterMediaButtonEventReceiver.invoke(mAudioManager, intentReceiver); } catch (InvocationTargetException ite) { /* unpack original exception when possible */ Throwable cause = ite.getCause(); if (cause instanceof RuntimeException) { throw (RuntimeException) cause; } else if (cause instanceof Error) { throw (Error) cause; } else { /* unexpected checked exception; wrap and re-throw */ throw new RuntimeException(ite); } } catch (IllegalAccessException ie) { System.err.println("unexpected " + ie); } } }