aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris "Koying" Browet <cbro@semperpax.com>2015-05-31 14:15:04 +0200
committerChris "Koying" Browet <cbro@semperpax.com>2015-06-14 10:30:24 +0200
commit8b3ee73c2cd16ce6d276b2505d5e2039c2ee4f92 (patch)
tree1a28a3236fdcdc73b5cd495fc0b678f544802520
parent790843dfbb346dd7ca1e31e7fca200116a8877fc (diff)
FIX: [droid] proper audio focus management
-rw-r--r--configure.ac1
-rw-r--r--tools/android/packaging/xbmc/src/org/xbmc/kodi/XBMCOnAudioFocusChangeListener.java.in16
-rw-r--r--xbmc/Application.cpp15
-rw-r--r--xbmc/android/activity/XBMCApp.cpp76
-rw-r--r--xbmc/android/activity/XBMCApp.h15
-rw-r--r--xbmc/android/activity/android_main.cpp12
-rw-r--r--xbmc/android/jni/Activity.cpp8
-rw-r--r--xbmc/android/jni/Activity.h2
-rw-r--r--xbmc/android/jni/AudioManager.cpp60
-rw-r--r--xbmc/android/jni/AudioManager.h25
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();
};