aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris "Koying" Browet <cbro@semperpax.com>2015-07-20 15:59:42 +0200
committerChris "koying" Browet <cbro@semperpax.com>2015-10-29 13:37:21 +0100
commit54018e9d208be6aeff8b99d09e832b9ff83684aa (patch)
tree3c0ca933bceddad6bb185f71e88fb71f7a7817ad
parentec8f051a6e56b3748d30fdaee8e08391fa904648 (diff)
CHG: [droid] proper handling of MEDIA keys
This allows MEDIA key to control Kodi when minimized
-rw-r--r--tools/android/packaging/xbmc/AndroidManifest.xml.in12
-rw-r--r--tools/android/packaging/xbmc/src/org/xbmc/kodi/Main.java.in15
-rw-r--r--xbmc/android/activity/AndroidKey.h4
-rw-r--r--xbmc/android/activity/JNIMainActivity.cpp12
-rw-r--r--xbmc/android/activity/JNIMainActivity.h2
-rw-r--r--xbmc/android/activity/XBMCApp.cpp62
-rw-r--r--xbmc/android/jni/Context.cpp4
-rw-r--r--xbmc/android/jni/Intent.cpp15
-rw-r--r--xbmc/android/jni/Intent.h4
-rw-r--r--xbmc/android/jni/KeyEvent.cpp68
-rw-r--r--xbmc/android/jni/KeyEvent.h68
-rw-r--r--xbmc/android/jni/Makefile.in1
12 files changed, 250 insertions, 17 deletions
diff --git a/tools/android/packaging/xbmc/AndroidManifest.xml.in b/tools/android/packaging/xbmc/AndroidManifest.xml.in
index c10630b99c..924fe1fbd1 100644
--- a/tools/android/packaging/xbmc/AndroidManifest.xml.in
+++ b/tools/android/packaging/xbmc/AndroidManifest.xml.in
@@ -82,6 +82,18 @@
android:name="android.app.lib_name"
android:value="@APP_NAME_LC@" />
</activity>
+
+ <receiver android:name=".XBMCBroadcastReceiver" >
+ <intent-filter>
+ <action android:name="android.intent.action.BATTERY_CHANGED" />
+ <action android:name="android.intent.action.DREAMING_STOPPED" />
+ <action android:name="android.intent.action.SCREEN_ON" />
+ <action android:name="android.intent.action.HEADSET_PLUG" />
+ <action android:name="android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED" />
+ <action android:name="android.intent.action.MEDIA_BUTTON" />
+ </intent-filter>
+ </receiver>
+
</application>
</manifest><!-- END_INCLUDE(manifest) -->
diff --git a/tools/android/packaging/xbmc/src/org/xbmc/kodi/Main.java.in b/tools/android/packaging/xbmc/src/org/xbmc/kodi/Main.java.in
index 8b1848396a..d2399b43c9 100644
--- a/tools/android/packaging/xbmc/src/org/xbmc/kodi/Main.java.in
+++ b/tools/android/packaging/xbmc/src/org/xbmc/kodi/Main.java.in
@@ -1,6 +1,7 @@
package org.xbmc.@APP_NAME_LC@;
import android.app.NativeActivity;
+import android.content.ComponentName;
import android.content.Intent;
import android.media.AudioManager;
import android.os.Bundle;
@@ -61,6 +62,20 @@ public class Main extends NativeActivity
}
});
}
+
+ public void registerMediaButtonEventReceiver()
+ {
+ Log.d(TAG, "registerMediaButtonEventReceiver");
+ AudioManager manager = (AudioManager) getSystemService(AUDIO_SERVICE);
+ manager.registerMediaButtonEventReceiver(new ComponentName(getPackageName(), XBMCBroadcastReceiver.class.getName()));
+ }
+
+ public void unregisterMediaButtonEventReceiver()
+ {
+ Log.d(TAG, "unregisterMediaButtonEventReceiver");
+ AudioManager manager = (AudioManager) getSystemService(AUDIO_SERVICE);
+ manager.unregisterMediaButtonEventReceiver(new ComponentName(getPackageName(), XBMCBroadcastReceiver.class.getName()));
+ }
@Override
public void onCreate(Bundle savedInstanceState)
diff --git a/xbmc/android/activity/AndroidKey.h b/xbmc/android/activity/AndroidKey.h
index 7ded4e63d2..81f056c25c 100644
--- a/xbmc/android/activity/AndroidKey.h
+++ b/xbmc/android/activity/AndroidKey.h
@@ -32,6 +32,6 @@ public:
~CAndroidKey() {};
bool onKeyboardEvent(AInputEvent *event);
- void XBMC_Key(uint8_t code, uint16_t key, uint16_t modifiers, uint16_t unicode, bool up);
- void XBMC_JoyButton(uint8_t id, uint8_t button, bool up);
+
+ static void XBMC_Key(uint8_t code, uint16_t key, uint16_t modifiers, uint16_t unicode, bool up);
};
diff --git a/xbmc/android/activity/JNIMainActivity.cpp b/xbmc/android/activity/JNIMainActivity.cpp
index 1074d0124b..3cdbdec0d3 100644
--- a/xbmc/android/activity/JNIMainActivity.cpp
+++ b/xbmc/android/activity/JNIMainActivity.cpp
@@ -93,3 +93,15 @@ void CJNIMainActivity::setVideoViewSurfaceRect(int l, int t, int r, int b)
call_method<void>(m_context,
"setVideoViewSurfaceRect", "(IIII)V", l, t, r, b);
}
+
+void CJNIMainActivity::registerMediaButtonEventReceiver()
+{
+ call_method<void>(m_context,
+ "registerMediaButtonEventReceiver", "()V");
+}
+
+void CJNIMainActivity::unregisterMediaButtonEventReceiver()
+{
+ call_method<void>(m_context,
+ "unregisterMediaButtonEventReceiver", "()V");
+}
diff --git a/xbmc/android/activity/JNIMainActivity.h b/xbmc/android/activity/JNIMainActivity.h
index 300ea81af7..c03ca86461 100644
--- a/xbmc/android/activity/JNIMainActivity.h
+++ b/xbmc/android/activity/JNIMainActivity.h
@@ -36,6 +36,8 @@ public:
static void _callNative(JNIEnv *env, jobject context, jlong funcAddr, jlong variantAddr);
static void runNativeOnUiThread(void (*callback)(CVariant *), CVariant *variant);
+ static void registerMediaButtonEventReceiver();
+ static void unregisterMediaButtonEventReceiver();
CJNISurface getVideoViewSurface();
void clearVideoView();
diff --git a/xbmc/android/activity/XBMCApp.cpp b/xbmc/android/activity/XBMCApp.cpp
index bfb4241d41..69f7f23f9e 100644
--- a/xbmc/android/activity/XBMCApp.cpp
+++ b/xbmc/android/activity/XBMCApp.cpp
@@ -76,6 +76,8 @@
#endif
#include "android/jni/Window.h"
#include "android/jni/WindowManager.h"
+#include "android/jni/KeyEvent.h"
+#include "AndroidKey.h"
#include "CompileInfo.h"
@@ -154,13 +156,6 @@ void CXBMCApp::onStart()
void CXBMCApp::onResume()
{
android_printf("%s: ", __PRETTY_FUNCTION__);
- CJNIIntentFilter intentFilter;
- intentFilter.addAction("android.intent.action.BATTERY_CHANGED");
- intentFilter.addAction("android.intent.action.DREAMING_STOPPED");
- intentFilter.addAction("android.intent.action.SCREEN_ON");
- intentFilter.addAction("android.intent.action.HEADSET_PLUG");
- intentFilter.addAction("android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED");
- registerReceiver(*this, intentFilter);
if (!g_application.IsInScreenSaver())
EnableWakeLock(true);
@@ -170,6 +165,8 @@ void CXBMCApp::onResume()
CJNIAudioManager audioManager(getSystemService("audio"));
m_headsetPlugged = audioManager.isWiredHeadsetOn() || audioManager.isBluetoothA2dpOn();
+ unregisterMediaButtonEventReceiver();
+
// Clear the applications cache. We could have installed/deinstalled apps
{
CSingleLock lock(m_applicationsMutex);
@@ -180,11 +177,13 @@ void CXBMCApp::onResume()
void CXBMCApp::onPause()
{
android_printf("%s: ", __PRETTY_FUNCTION__);
-
- unregisterReceiver(*this);
-
- if (g_application.m_pPlayer->IsPlayingVideo())
- CApplicationMessenger::GetInstance().SendMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1, static_cast<void*>(new CAction(ACTION_STOP)));
+ if (g_application.m_pPlayer->IsPlaying())
+ {
+ if (g_application.m_pPlayer->IsPlayingVideo())
+ CApplicationMessenger::GetInstance().SendMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1, static_cast<void*>(new CAction(ACTION_STOP)));
+ else
+ registerMediaButtonEventReceiver();
+ }
#if defined(HAS_LIBAMCODEC)
if (aml_permissions())
@@ -740,6 +739,35 @@ void CXBMCApp::onReceive(CJNIIntent intent)
CAEFactory::DeviceChange();
}
}
+ else if (action == "android.intent.action.MEDIA_BUTTON")
+ {
+ CJNIKeyEvent keyevt = (CJNIKeyEvent)intent.getParcelableExtra(CJNIIntent::EXTRA_KEY_EVENT);
+
+ int keycode = keyevt.getKeyCode();
+ bool up = (keyevt.getAction() == CJNIKeyEvent::ACTION_UP);
+
+ CLog::Log(LOGINFO, "Got MEDIA_BUTTON intent: %d, up:%s", keycode, up ? "true" : "false");
+ if (keycode == CJNIKeyEvent::KEYCODE_MEDIA_RECORD)
+ CAndroidKey::XBMC_Key(keycode, XBMCK_RECORD, 0, 0, up);
+ else if (keycode == CJNIKeyEvent::KEYCODE_MEDIA_EJECT)
+ CAndroidKey::XBMC_Key(keycode, XBMCK_EJECT, 0, 0, up);
+ else if (keycode == CJNIKeyEvent::KEYCODE_MEDIA_FAST_FORWARD)
+ CAndroidKey::XBMC_Key(keycode, XBMCK_MEDIA_FASTFORWARD, 0, 0, up);
+ else if (keycode == CJNIKeyEvent::KEYCODE_MEDIA_NEXT)
+ CAndroidKey::XBMC_Key(keycode, XBMCK_MEDIA_NEXT_TRACK, 0, 0, up);
+ else if (keycode == CJNIKeyEvent::KEYCODE_MEDIA_PAUSE)
+ CAndroidKey::XBMC_Key(keycode, XBMCK_MEDIA_PLAY_PAUSE, 0, 0, up);
+ else if (keycode == CJNIKeyEvent::KEYCODE_MEDIA_PLAY)
+ CAndroidKey::XBMC_Key(keycode, XBMCK_MEDIA_PLAY_PAUSE, 0, 0, up);
+ else if (keycode == CJNIKeyEvent::KEYCODE_MEDIA_PLAY_PAUSE)
+ CAndroidKey::XBMC_Key(keycode, XBMCK_MEDIA_PLAY_PAUSE, 0, 0, up);
+ else if (keycode == CJNIKeyEvent::KEYCODE_MEDIA_PREVIOUS)
+ CAndroidKey::XBMC_Key(keycode, XBMCK_MEDIA_PREV_TRACK, 0, 0, up);
+ else if (keycode == CJNIKeyEvent::KEYCODE_MEDIA_REWIND)
+ CAndroidKey::XBMC_Key(keycode, XBMCK_MEDIA_REWIND, 0, 0, up);
+ else if (keycode == CJNIKeyEvent::KEYCODE_MEDIA_STOP)
+ CAndroidKey::XBMC_Key(keycode, XBMCK_MEDIA_STOP, 0, 0, up);
+ }
}
void CXBMCApp::onNewIntent(CJNIIntent intent)
@@ -763,9 +791,13 @@ void CXBMCApp::onVolumeChanged(int volume)
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::GetInstance().SendMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1, static_cast<void*>(
- new CAction(ACTION_PAUSE)));
+ if (focusChange == CJNIAudioManager::AUDIOFOCUS_LOSS)
+ {
+ unregisterMediaButtonEventReceiver();
+
+ if (g_application.m_pPlayer->IsPlaying() && !g_application.m_pPlayer->IsPaused())
+ CApplicationMessenger::GetInstance().SendMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1, static_cast<void*>(new CAction(ACTION_PAUSE)));
+ }
}
void CXBMCApp::SetupEnv()
diff --git a/xbmc/android/jni/Context.cpp b/xbmc/android/jni/Context.cpp
index d747d44fbb..8b9b5d79f6 100644
--- a/xbmc/android/jni/Context.cpp
+++ b/xbmc/android/jni/Context.cpp
@@ -46,6 +46,8 @@
#include "View.h"
#include "Build.h"
#include "DisplayMetrics.h"
+#include "Intent.h"
+#include "KeyEvent.h"
#include <android/native_activity.h>
@@ -86,6 +88,8 @@ void CJNIContext::PopulateStaticFields()
CJNIView::PopulateStaticFields();
CJNIBuild::PopulateStaticFields();
CJNIDisplayMetrics::PopulateStaticFields();
+ CJNIIntent::PopulateStaticFields();
+ CJNIKeyEvent::PopulateStaticFields();
}
CJNIPackageManager CJNIContext::GetPackageManager()
diff --git a/xbmc/android/jni/Intent.cpp b/xbmc/android/jni/Intent.cpp
index eb7eeec8d2..264a629d8c 100644
--- a/xbmc/android/jni/Intent.cpp
+++ b/xbmc/android/jni/Intent.cpp
@@ -24,6 +24,14 @@
using namespace jni;
+std::string CJNIIntent::EXTRA_KEY_EVENT;
+
+void CJNIIntent::PopulateStaticFields()
+{
+ jhclass clazz = find_class("android/content/Intent");
+ EXTRA_KEY_EVENT = jcast<std::string>(get_static_field<jhstring>(clazz,"EXTRA_KEY_EVENT"));
+}
+
CJNIIntent::CJNIIntent(const std::string &action) : CJNIBase("android/content/Intent")
{
if(action.empty())
@@ -72,6 +80,13 @@ std::string CJNIIntent::getStringExtra(const std::string &name) const
jcast<jhstring>(name)));
}
+jni::jhobject CJNIIntent::getParcelableExtra(const std::string &name) const
+{
+ return call_method<jhobject>(m_object,
+ "getParcelableExtra", "(Ljava/lang/String;)Landroid/os/Parcelable;",
+ jcast<jhstring>(name));
+}
+
bool CJNIIntent::hasExtra(const std::string &name) const
{
return call_method<jboolean>(m_object,
diff --git a/xbmc/android/jni/Intent.h b/xbmc/android/jni/Intent.h
index c187642c88..6855fba284 100644
--- a/xbmc/android/jni/Intent.h
+++ b/xbmc/android/jni/Intent.h
@@ -36,6 +36,7 @@ public:
int getIntExtra(const std::string &name, int defaultValue) const;
std::string getStringExtra(const std::string &name) const;
+ jni::jhobject getParcelableExtra(const std::string &name) const;
bool hasExtra(const std::string &name) const;
bool hasCategory(const std::string &category) const;
@@ -53,4 +54,7 @@ public:
void setPackage(const std::string &packageName);
void setType(const std::string &type);
CJNIURI getData() const;
+
+ static void PopulateStaticFields();
+ static std::string EXTRA_KEY_EVENT;
};
diff --git a/xbmc/android/jni/KeyEvent.cpp b/xbmc/android/jni/KeyEvent.cpp
new file mode 100644
index 0000000000..55c38c824d
--- /dev/null
+++ b/xbmc/android/jni/KeyEvent.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2013 Team XBMC
+ * http://xbmc.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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "KeyEvent.h"
+
+#include "jutils/jutils-details.hpp"
+
+using namespace jni;
+
+int CJNIKeyEvent::ACTION_DOWN;
+int CJNIKeyEvent::ACTION_UP;
+
+int CJNIKeyEvent::KEYCODE_MEDIA_RECORD;
+int CJNIKeyEvent::KEYCODE_MEDIA_EJECT;
+int CJNIKeyEvent::KEYCODE_MEDIA_FAST_FORWARD;
+int CJNIKeyEvent::KEYCODE_MEDIA_NEXT ;
+int CJNIKeyEvent::KEYCODE_MEDIA_PAUSE;
+int CJNIKeyEvent::KEYCODE_MEDIA_PLAY;
+int CJNIKeyEvent::KEYCODE_MEDIA_PLAY_PAUSE;
+int CJNIKeyEvent::KEYCODE_MEDIA_PREVIOUS;
+int CJNIKeyEvent::KEYCODE_MEDIA_REWIND;
+int CJNIKeyEvent::KEYCODE_MEDIA_STOP;
+
+void CJNIKeyEvent::PopulateStaticFields()
+{
+ jhclass clazz = find_class("android/view/KeyEvent");
+ ACTION_DOWN = (get_static_field<int>(clazz, "ACTION_DOWN"));
+ ACTION_UP = (get_static_field<int>(clazz, "ACTION_UP"));
+ KEYCODE_MEDIA_RECORD = (get_static_field<int>(clazz, "KEYCODE_MEDIA_RECORD"));
+ KEYCODE_MEDIA_EJECT = (get_static_field<int>(clazz, "KEYCODE_MEDIA_EJECT"));
+ KEYCODE_MEDIA_FAST_FORWARD = (get_static_field<int>(clazz, "KEYCODE_MEDIA_FAST_FORWARD"));
+ KEYCODE_MEDIA_NEXT = (get_static_field<int>(clazz, "KEYCODE_MEDIA_NEXT"));
+ KEYCODE_MEDIA_PAUSE = (get_static_field<int>(clazz, "KEYCODE_MEDIA_PAUSE"));
+ KEYCODE_MEDIA_PLAY = (get_static_field<int>(clazz, "KEYCODE_MEDIA_PLAY"));
+ KEYCODE_MEDIA_PLAY_PAUSE = (get_static_field<int>(clazz, "KEYCODE_MEDIA_PLAY_PAUSE"));
+ KEYCODE_MEDIA_PREVIOUS = (get_static_field<int>(clazz, "KEYCODE_MEDIA_PREVIOUS"));
+ KEYCODE_MEDIA_REWIND = (get_static_field<int>(clazz, "KEYCODE_MEDIA_REWIND"));
+ KEYCODE_MEDIA_STOP = (get_static_field<int>(clazz, "KEYCODE_MEDIA_STOP"));
+}
+
+int CJNIKeyEvent::getKeyCode()
+{
+ return call_method<jint>(m_object,
+ "getKeyCode", "()I");
+}
+
+int CJNIKeyEvent::getAction()
+{
+ return call_method<jint>(m_object,
+ "getAction", "()I");
+}
diff --git a/xbmc/android/jni/KeyEvent.h b/xbmc/android/jni/KeyEvent.h
new file mode 100644
index 0000000000..5ad8474610
--- /dev/null
+++ b/xbmc/android/jni/KeyEvent.h
@@ -0,0 +1,68 @@
+#pragma once
+/*
+ * Copyright (C) 2013 Team XBMC
+ * http://xbmc.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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "JNIBase.h"
+
+class CJNIKeyEventAudioFocusChangeListener : public CJNIBase
+{
+public:
+ CJNIKeyEventAudioFocusChangeListener(const jni::jhobject &object) : CJNIBase(object) {};
+ virtual ~CJNIKeyEventAudioFocusChangeListener() {};
+
+ static void _onAudioFocusChange(JNIEnv *env, jobject context, jint focusChange);
+
+protected:
+ CJNIKeyEventAudioFocusChangeListener();
+
+ virtual void onAudioFocusChange(int focusChange)=0;
+
+private:
+ static CJNIKeyEventAudioFocusChangeListener *m_listenerInstance;
+};
+
+class CJNIKeyEvent : public CJNIBase
+{
+public:
+ CJNIKeyEvent(const jni::jhobject &object) : CJNIBase(object) {};
+ ~CJNIKeyEvent() {};
+
+ int getKeyCode();
+ int getAction();
+
+ static void PopulateStaticFields();
+ static int ACTION_DOWN;
+ static int ACTION_UP;
+
+ static int KEYCODE_MEDIA_RECORD;
+ static int KEYCODE_MEDIA_EJECT;
+ static int KEYCODE_MEDIA_FAST_FORWARD;
+ static int KEYCODE_MEDIA_NEXT ;
+ static int KEYCODE_MEDIA_PAUSE;
+ static int KEYCODE_MEDIA_PLAY;
+ static int KEYCODE_MEDIA_PLAY_PAUSE;
+ static int KEYCODE_MEDIA_PREVIOUS;
+ static int KEYCODE_MEDIA_REWIND;
+ static int KEYCODE_MEDIA_STOP;
+
+private:
+ CJNIKeyEvent();
+};
+
diff --git a/xbmc/android/jni/Makefile.in b/xbmc/android/jni/Makefile.in
index abbfcab17a..7e1eeff619 100644
--- a/xbmc/android/jni/Makefile.in
+++ b/xbmc/android/jni/Makefile.in
@@ -57,6 +57,7 @@ SRCS += WindowManager.cpp
SRCS += Resources.cpp
SRCS += PackageItemInfo.cpp
SRCS += DisplayMetrics.cpp
+SRCS += KeyEvent.cpp
LIB = jni.a