diff options
author | Chris "Koying" Browet <cbro@semperpax.com> | 2015-07-20 15:59:42 +0200 |
---|---|---|
committer | Chris "koying" Browet <cbro@semperpax.com> | 2015-10-29 13:37:21 +0100 |
commit | 54018e9d208be6aeff8b99d09e832b9ff83684aa (patch) | |
tree | 3c0ca933bceddad6bb185f71e88fb71f7a7817ad | |
parent | ec8f051a6e56b3748d30fdaee8e08391fa904648 (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.in | 12 | ||||
-rw-r--r-- | tools/android/packaging/xbmc/src/org/xbmc/kodi/Main.java.in | 15 | ||||
-rw-r--r-- | xbmc/android/activity/AndroidKey.h | 4 | ||||
-rw-r--r-- | xbmc/android/activity/JNIMainActivity.cpp | 12 | ||||
-rw-r--r-- | xbmc/android/activity/JNIMainActivity.h | 2 | ||||
-rw-r--r-- | xbmc/android/activity/XBMCApp.cpp | 62 | ||||
-rw-r--r-- | xbmc/android/jni/Context.cpp | 4 | ||||
-rw-r--r-- | xbmc/android/jni/Intent.cpp | 15 | ||||
-rw-r--r-- | xbmc/android/jni/Intent.h | 4 | ||||
-rw-r--r-- | xbmc/android/jni/KeyEvent.cpp | 68 | ||||
-rw-r--r-- | xbmc/android/jni/KeyEvent.h | 68 | ||||
-rw-r--r-- | xbmc/android/jni/Makefile.in | 1 |
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 |