diff options
author | Chris "Koying" Browet <cbro@semperpax.com> | 2015-05-31 14:15:04 +0200 |
---|---|---|
committer | Chris "Koying" Browet <cbro@semperpax.com> | 2015-06-14 10:30:24 +0200 |
commit | 8b3ee73c2cd16ce6d276b2505d5e2039c2ee4f92 (patch) | |
tree | 1a28a3236fdcdc73b5cd495fc0b678f544802520 | |
parent | 790843dfbb346dd7ca1e31e7fca200116a8877fc (diff) |
FIX: [droid] proper audio focus management
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | tools/android/packaging/xbmc/src/org/xbmc/kodi/XBMCOnAudioFocusChangeListener.java.in | 16 | ||||
-rw-r--r-- | xbmc/Application.cpp | 15 | ||||
-rw-r--r-- | xbmc/android/activity/XBMCApp.cpp | 76 | ||||
-rw-r--r-- | xbmc/android/activity/XBMCApp.h | 15 | ||||
-rw-r--r-- | xbmc/android/activity/android_main.cpp | 12 | ||||
-rw-r--r-- | xbmc/android/jni/Activity.cpp | 8 | ||||
-rw-r--r-- | xbmc/android/jni/Activity.h | 2 | ||||
-rw-r--r-- | xbmc/android/jni/AudioManager.cpp | 60 | ||||
-rw-r--r-- | xbmc/android/jni/AudioManager.h | 25 |
10 files changed, 227 insertions, 3 deletions
diff --git a/configure.ac b/configure.ac index 8c1e41221d..4e55ad6b28 100644 --- a/configure.ac +++ b/configure.ac @@ -2530,6 +2530,7 @@ OUTPUT_FILES="Makefile \ tools/android/packaging/xbmc/src/org/xbmc/kodi/XBMCBroadcastReceiver.java \ tools/android/packaging/xbmc/src/org/xbmc/kodi/XBMCOnFrameAvailableListener.java \ tools/android/packaging/xbmc/src/org/xbmc/kodi/XBMCSettingsContentObserver.java \ + tools/android/packaging/xbmc/src/org/xbmc/kodi/XBMCOnAudioFocusChangeListener.java \ tools/android/packaging/xbmc/strings.xml \ addons/xbmc.addon/addon.xml" diff --git a/tools/android/packaging/xbmc/src/org/xbmc/kodi/XBMCOnAudioFocusChangeListener.java.in b/tools/android/packaging/xbmc/src/org/xbmc/kodi/XBMCOnAudioFocusChangeListener.java.in new file mode 100644 index 0000000000..df33b17810 --- /dev/null +++ b/tools/android/packaging/xbmc/src/org/xbmc/kodi/XBMCOnAudioFocusChangeListener.java.in @@ -0,0 +1,16 @@ +package org.xbmc.@APP_NAME_LC@; + +import android.media.AudioManager.OnAudioFocusChangeListener; +import android.util.Log; + +public class XBMCOnAudioFocusChangeListener implements OnAudioFocusChangeListener +{ + native void _onAudioFocusChange(int focusChange); + + @Override + public void onAudioFocusChange(int focusChange) + { + _onAudioFocusChange(focusChange); + + } +} diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp index 291f3a04af..1de65fda6f 100644 --- a/xbmc/Application.cpp +++ b/xbmc/Application.cpp @@ -3310,6 +3310,9 @@ void CApplication::OnPlayBackEnded() #ifdef HAS_PYTHON g_pythonParser.OnPlayBackEnded(); #endif +#ifdef TARGET_ANDROID + CXBMCApp::OnPlayBackEnded(); +#endif CVariant data(CVariant::VariantTypeObject); data["end"] = true; @@ -3332,6 +3335,9 @@ void CApplication::OnPlayBackStarted() // (does nothing if python is not loaded) g_pythonParser.OnPlayBackStarted(); #endif +#ifdef TARGET_ANDROID + CXBMCApp::OnPlayBackStarted(); +#endif CGUIMessage msg(GUI_MSG_PLAYBACK_STARTED, 0, 0); g_windowManager.SendThreadMessage(msg); @@ -3366,6 +3372,9 @@ void CApplication::OnPlayBackStopped() #ifdef HAS_PYTHON g_pythonParser.OnPlayBackStopped(); #endif +#ifdef TARGET_ANDROID + CXBMCApp::OnPlayBackStopped(); +#endif CVariant data(CVariant::VariantTypeObject); data["end"] = false; @@ -3380,6 +3389,9 @@ void CApplication::OnPlayBackPaused() #ifdef HAS_PYTHON g_pythonParser.OnPlayBackPaused(); #endif +#ifdef TARGET_ANDROID + CXBMCApp::OnPlayBackPaused(); +#endif CVariant param; param["player"]["speed"] = 0; @@ -3392,6 +3404,9 @@ void CApplication::OnPlayBackResumed() #ifdef HAS_PYTHON g_pythonParser.OnPlayBackResumed(); #endif +#ifdef TARGET_ANDROID + CXBMCApp::OnPlayBackResumed(); +#endif CVariant param; param["player"]["speed"] = 1; diff --git a/xbmc/android/activity/XBMCApp.cpp b/xbmc/android/activity/XBMCApp.cpp index 5b07d525f6..1143a62a3f 100644 --- a/xbmc/android/activity/XBMCApp.cpp +++ b/xbmc/android/activity/XBMCApp.cpp @@ -53,7 +53,6 @@ #include "android/jni/Intent.h" #include "android/jni/PackageManager.h" #include "android/jni/Context.h" -#include "android/jni/AudioManager.h" #include "android/jni/PowerManager.h" #include "android/jni/WakeLock.h" #include "android/jni/Environment.h" @@ -87,6 +86,8 @@ void* thread_run(void* obj) (static_cast<T*>(obj)->*fn)(); return NULL; } + +CXBMCApp* CXBMCApp::m_xbmcappinstance = NULL; CEvent CXBMCApp::m_windowCreated; ANativeActivity *CXBMCApp::m_activity = NULL; CJNIWakeLock *CXBMCApp::m_wakeLock = NULL; @@ -101,6 +102,7 @@ CXBMCApp::CXBMCApp(ANativeActivity* nativeActivity) : CJNIApplicationMainActivity(nativeActivity) , CJNIBroadcastReceiver("org/xbmc/kodi/XBMCBroadcastReceiver") { + m_xbmcappinstance = this; m_activity = nativeActivity; m_firstrun = true; m_exiting=false; @@ -114,6 +116,7 @@ CXBMCApp::CXBMCApp(ANativeActivity* nativeActivity) CXBMCApp::~CXBMCApp() { + m_xbmcappinstance = NULL; delete m_wakeLock; } @@ -299,6 +302,45 @@ bool CXBMCApp::EnableWakeLock(bool on) return true; } +bool CXBMCApp::AcquireAudioFocus() +{ + if (!m_xbmcappinstance) + return false; + + CJNIAudioManager audioManager(getSystemService("audio")); + + // Request audio focus for playback + int result = audioManager.requestAudioFocus(*m_xbmcappinstance, + // Use the music stream. + CJNIAudioManager::STREAM_MUSIC, + // Request permanent focus. + CJNIAudioManager::AUDIOFOCUS_GAIN); + + if (result != CJNIAudioManager::AUDIOFOCUS_REQUEST_GRANTED) + { + CXBMCApp::android_printf("Audio Focus request failed"); + return false; + } + return true; +} + +bool CXBMCApp::ReleaseAudioFocus() +{ + if (!m_xbmcappinstance) + return false; + + CJNIAudioManager audioManager(getSystemService("audio")); + + // Release audio focus after playback + int result = audioManager.abandonAudioFocus(*m_xbmcappinstance); + if (result != CJNIAudioManager::AUDIOFOCUS_REQUEST_GRANTED) + { + CXBMCApp::android_printf("Audio Focus abandon failed"); + return false; + } + return true; +} + bool CXBMCApp::HasFocus() { return m_hasFocus; @@ -405,6 +447,31 @@ int CXBMCApp::GetDPI() return dpi; } +void CXBMCApp::OnPlayBackStarted() +{ + AcquireAudioFocus(); +} + +void CXBMCApp::OnPlayBackPaused() +{ + ReleaseAudioFocus(); +} + +void CXBMCApp::OnPlayBackResumed() +{ + AcquireAudioFocus(); +} + +void CXBMCApp::OnPlayBackStopped() +{ + ReleaseAudioFocus(); +} + +void CXBMCApp::OnPlayBackEnded() +{ + ReleaseAudioFocus(); +} + std::vector<androidPackage> CXBMCApp::GetApplications() { CSingleLock lock(m_applicationsMutex); @@ -652,6 +719,13 @@ void CXBMCApp::onVolumeChanged(int volume) CApplicationMessenger::Get().SendAction(CAction(ACTION_VOLUME_SET, (float)volume), WINDOW_INVALID, false); } +void CXBMCApp::onAudioFocusChange(int focusChange) +{ + CXBMCApp::android_printf("Audio Focus changed: %d", focusChange); + if (focusChange == CJNIAudioManager::AUDIOFOCUS_LOSS && g_application.m_pPlayer->IsPlaying() && !g_application.m_pPlayer->IsPaused()) + CApplicationMessenger::Get().SendAction(CAction(ACTION_PAUSE), WINDOW_INVALID, true); +} + void CXBMCApp::SetupEnv() { setenv("XBMC_ANDROID_SYSTEM_LIBS", CJNISystem::getProperty("java.library.path").c_str(), 0); diff --git a/xbmc/android/activity/XBMCApp.h b/xbmc/android/activity/XBMCApp.h index 72b9caad53..2307ca688a 100644 --- a/xbmc/android/activity/XBMCApp.h +++ b/xbmc/android/activity/XBMCApp.h @@ -32,6 +32,7 @@ #include "xbmc.h" #include "android/jni/Activity.h" #include "android/jni/BroadcastReceiver.h" +#include "android/jni/AudioManager.h" #include "threads/Event.h" // forward delares @@ -52,7 +53,7 @@ struct androidPackage std::string packageLabel; }; -class CXBMCApp : public IActivityHandler, public CJNIApplicationMainActivity, public CJNIBroadcastReceiver +class CXBMCApp : public IActivityHandler, public CJNIApplicationMainActivity, public CJNIBroadcastReceiver, public CJNIAudioManagerAudioFocusChangeListener { public: CXBMCApp(ANativeActivity *nativeActivity); @@ -60,6 +61,7 @@ public: virtual void onReceive(CJNIIntent intent); virtual void onNewIntent(CJNIIntent intent); virtual void onVolumeChanged(int volume); + virtual void onAudioFocusChange(int focusChange); bool isValid() { return m_activity != NULL; } @@ -106,13 +108,24 @@ public: static void SetSystemVolume(float percent); static int GetDPI(); + + // Playback callbacks + static void OnPlayBackStarted(); + static void OnPlayBackPaused(); + static void OnPlayBackResumed(); + static void OnPlayBackStopped(); + static void OnPlayBackEnded(); + protected: // limit who can access Volume friend class CAESinkAUDIOTRACK; static int GetMaxSystemVolume(JNIEnv *env); + static bool AcquireAudioFocus(); + static bool ReleaseAudioFocus(); private: + static CXBMCApp* m_xbmcappinstance; static bool HasLaunchIntent(const std::string &package); std::string GetFilenameFromIntent(const CJNIIntent &intent); void run(); diff --git a/xbmc/android/activity/android_main.cpp b/xbmc/android/activity/android_main.cpp index c028eb8a2a..8dd19ede79 100644 --- a/xbmc/android/activity/android_main.cpp +++ b/xbmc/android/activity/android_main.cpp @@ -90,6 +90,7 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) std::string bcReceiver = "org/xbmc/" + appName + "/XBMCBroadcastReceiver"; std::string frameListener = "org/xbmc/" + appName + "/XBMCOnFrameAvailableListener"; std::string settingsObserver = "org/xbmc/" + appName + "/XBMCSettingsContentObserver"; + std::string audioFocusChangeListener = "org/xbmc/" + appName + "/XBMCOnAudioFocusChangeListener"; jclass cMain = env->FindClass(mainClass.c_str()); if(cMain) @@ -135,5 +136,16 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) env->RegisterNatives(cSettingsObserver, &mOnVolumeChanged, 1); } + jclass cAudioFocusChangeListener = env->FindClass(audioFocusChangeListener.c_str()); + if(cAudioFocusChangeListener) + { + JNINativeMethod mOnAudioFocusChange = { + "_onAudioFocusChange", + "(I)V", + (void*)&CJNIApplicationMainActivity::_onAudioFocusChange + }; + env->RegisterNatives(cAudioFocusChangeListener, &mOnAudioFocusChange, 1); + } + return version; } diff --git a/xbmc/android/jni/Activity.cpp b/xbmc/android/jni/Activity.cpp index 4162703b2d..45a5885b82 100644 --- a/xbmc/android/jni/Activity.cpp +++ b/xbmc/android/jni/Activity.cpp @@ -75,3 +75,11 @@ void CJNIApplicationMainActivity::_onVolumeChanged(JNIEnv *env, jobject context, if(m_appInstance) m_appInstance->onVolumeChanged(volume); } + +void CJNIApplicationMainActivity::_onAudioFocusChange(JNIEnv *env, jobject context, jint focusChange) +{ + (void)env; + (void)context; + if(m_appInstance) + m_appInstance->onAudioFocusChange(focusChange); +} diff --git a/xbmc/android/jni/Activity.h b/xbmc/android/jni/Activity.h index 25da7b5a96..460f4a3610 100644 --- a/xbmc/android/jni/Activity.h +++ b/xbmc/android/jni/Activity.h @@ -48,6 +48,7 @@ public: static void _onNewIntent(JNIEnv *env, jobject context, jobject intent); static void _onVolumeChanged(JNIEnv *env, jobject context, jint volume); + static void _onAudioFocusChange(JNIEnv *env, jobject context, jint focusChange); private: static CJNIApplicationMainActivity *m_appInstance; @@ -55,5 +56,6 @@ private: protected: virtual void onNewIntent(CJNIIntent intent)=0; virtual void onVolumeChanged(int volume)=0; + virtual void onAudioFocusChange(int focusChange)=0; }; diff --git a/xbmc/android/jni/AudioManager.cpp b/xbmc/android/jni/AudioManager.cpp index 5983852dea..42b0dc292e 100644 --- a/xbmc/android/jni/AudioManager.cpp +++ b/xbmc/android/jni/AudioManager.cpp @@ -19,16 +19,30 @@ */ #include "AudioManager.h" +#include "Activity.h" +#include "ClassLoader.h" + #include "jutils/jutils-details.hpp" +#include <algorithm> + using namespace jni; int CJNIAudioManager::STREAM_MUSIC(3); +int CJNIAudioManager::AUDIOFOCUS_GAIN(0x00000001); +int CJNIAudioManager::AUDIOFOCUS_LOSS(0xffffffff); +int CJNIAudioManager::AUDIOFOCUS_REQUEST_GRANTED(0x00000001); +int CJNIAudioManager::AUDIOFOCUS_REQUEST_FAILED(0x00000000); + void CJNIAudioManager::PopulateStaticFields() { jhclass clazz = find_class("android/media/AudioManager"); STREAM_MUSIC = (get_static_field<int>(clazz, "STREAM_MUSIC")); + AUDIOFOCUS_GAIN = (get_static_field<int>(clazz, "AUDIOFOCUS_GAIN")); + AUDIOFOCUS_LOSS = (get_static_field<int>(clazz, "AUDIOFOCUS_LOSS")); + AUDIOFOCUS_REQUEST_GRANTED = (get_static_field<int>(clazz, "AUDIOFOCUS_REQUEST_GRANTED")); + AUDIOFOCUS_REQUEST_FAILED = (get_static_field<int>(clazz, "AUDIOFOCUS_REQUEST_FAILED")); } int CJNIAudioManager::getStreamMaxVolume() @@ -49,5 +63,49 @@ void CJNIAudioManager::setStreamVolume(int index /* 0 */, int flags /* NONE */) { call_method<void>(m_object, "setStreamVolume", "(III)V", - STREAM_MUSIC, index, flags); + STREAM_MUSIC, index, flags); +} + +int CJNIAudioManager::requestAudioFocus(const CJNIAudioManagerAudioFocusChangeListener &listener, int streamType, int durationHint) +{ + return call_method<int>(m_object, + "requestAudioFocus", + "(Landroid/media/AudioManager$OnAudioFocusChangeListener;II)I", listener.get_raw(), streamType, durationHint); +} + +int CJNIAudioManager::abandonAudioFocus(const CJNIAudioManagerAudioFocusChangeListener &listener) +{ + return call_method<int>(m_object, + "abandonAudioFocus", + "(Landroid/media/AudioManager$OnAudioFocusChangeListener;)I", listener.get_raw()); +} + +////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////// +CJNIAudioManagerAudioFocusChangeListener* CJNIAudioManagerAudioFocusChangeListener::m_listenerInstance(NULL); + +CJNIAudioManagerAudioFocusChangeListener::CJNIAudioManagerAudioFocusChangeListener() +: CJNIBase("org/xbmc/kodi/XBMCOnAudioFocusChangeListener") +{ + CJNIApplicationMainActivity *appInstance = CJNIApplicationMainActivity::GetAppInstance(); + if (!appInstance) + return; + + // Convert "the/class/name" to "the.class.name" as loadClass() expects it. + std::string dotClassName = GetClassName(); + std::replace(dotClassName.begin(), dotClassName.end(), '/', '.'); + m_object = new_object(appInstance->getClassLoader().loadClass(dotClassName)); + m_object.setGlobal(); + + m_listenerInstance = this; +} + +void CJNIAudioManagerAudioFocusChangeListener::_onAudioFocusChange(JNIEnv *env, jobject context, jint focusChange) +{ + (void)env; + (void)context; + if (m_listenerInstance) + { + m_listenerInstance->onAudioFocusChange(focusChange); + } } diff --git a/xbmc/android/jni/AudioManager.h b/xbmc/android/jni/AudioManager.h index 0eb8291617..c02889fefd 100644 --- a/xbmc/android/jni/AudioManager.h +++ b/xbmc/android/jni/AudioManager.h @@ -21,6 +21,23 @@ #include "JNIBase.h" +class CJNIAudioManagerAudioFocusChangeListener : public CJNIBase +{ +public: + CJNIAudioManagerAudioFocusChangeListener(const jni::jhobject &object) : CJNIBase(object) {}; + virtual ~CJNIAudioManagerAudioFocusChangeListener() {}; + + static void _onAudioFocusChange(JNIEnv *env, jobject context, jint focusChange); + +protected: + CJNIAudioManagerAudioFocusChangeListener(); + + virtual void onAudioFocusChange(int focusChange)=0; + +private: + static CJNIAudioManagerAudioFocusChangeListener *m_listenerInstance; +}; + class CJNIAudioManager : public CJNIBase { public: @@ -32,9 +49,17 @@ public: int getStreamVolume(); void setStreamVolume(int index = 0, int flags = 0); + int requestAudioFocus(const CJNIAudioManagerAudioFocusChangeListener &listener, int streamType, int durationHint); + int abandonAudioFocus (const CJNIAudioManagerAudioFocusChangeListener &listener); + static void PopulateStaticFields(); static int STREAM_MUSIC; + static int AUDIOFOCUS_GAIN; + static int AUDIOFOCUS_LOSS; + static int AUDIOFOCUS_REQUEST_GRANTED; + static int AUDIOFOCUS_REQUEST_FAILED; + private: CJNIAudioManager(); }; |