2 * Copyright (C) 2012 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
28 #include <android/native_window.h>
29 #include <android/configuration.h>
34 #include "input/MouseStat.h"
35 #include "input/XBMC_keysym.h"
36 #include "guilib/Key.h"
37 #include "windowing/XBMC_events.h"
38 #include <android/log.h>
40 #include "Application.h"
41 #include "settings/AdvancedSettings.h"
43 #include "windowing/WinEvents.h"
44 #include "guilib/GUIWindowManager.h"
45 #include "utils/log.h"
46 #include "ApplicationMessenger.h"
48 #define GIGABYTES 1073741824
52 template<class T, void(T::*fn)()>
53 void* thread_run(void* obj)
55 (static_cast<T*>(obj)->*fn)();
59 ANativeActivity *CXBMCApp::m_activity = NULL;
60 ANativeWindow* CXBMCApp::m_window = NULL;
62 CXBMCApp::CXBMCApp(ANativeActivity *nativeActivity)
65 m_activity = nativeActivity;
67 if (m_activity == NULL)
69 android_printf("CXBMCApp: invalid ANativeActivity instance");
74 m_state.appState = Uninitialized;
76 if (pthread_mutex_init(&m_state.mutex, NULL) != 0)
78 android_printf("CXBMCApp: pthread_mutex_init() failed");
79 m_state.appState = Error;
90 pthread_mutex_destroy(&m_state.mutex);
93 ActivityResult CXBMCApp::onActivate()
95 android_printf("%s: %d", __PRETTY_FUNCTION__, m_state.appState);
97 switch (m_state.appState)
103 pthread_attr_init(&attr);
104 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
105 pthread_create(&m_state.thread, &attr, thread_run<CXBMCApp, &CXBMCApp::run>, this);
106 pthread_attr_destroy(&attr);
111 setAppState(Rendering);
119 setAppState(Rendering);
133 void CXBMCApp::onDeactivate()
135 android_printf("%s: %d", __PRETTY_FUNCTION__, m_state.appState);
136 // this is called on pause, stop and window destroy which
137 // require specific (and different) actions
140 void CXBMCApp::onStart()
142 android_printf("%s: %d", __PRETTY_FUNCTION__, m_state.appState);
143 // wait for onCreateWindow() and onGainFocus()
146 void CXBMCApp::onResume()
148 android_printf("%s: %d", __PRETTY_FUNCTION__, m_state.appState);
149 // wait for onCreateWindow() and onGainFocus()
152 void CXBMCApp::onPause()
154 android_printf("%s: %d", __PRETTY_FUNCTION__, m_state.appState);
155 // wait for onDestroyWindow() and/or onLostFocus()
158 void CXBMCApp::onStop()
160 android_printf("%s: %d", __PRETTY_FUNCTION__, m_state.appState);
161 // everything has been handled in onLostFocus() so wait
162 // if onDestroy() is called
165 void CXBMCApp::onDestroy()
167 android_printf("%s: %d", __PRETTY_FUNCTION__, m_state.appState);
171 void CXBMCApp::onSaveState(void **data, size_t *size)
173 android_printf("%s: %d", __PRETTY_FUNCTION__, m_state.appState);
174 // no need to save anything as XBMC is running in its own thread
177 void CXBMCApp::onConfigurationChanged()
179 android_printf("%s: %d", __PRETTY_FUNCTION__, m_state.appState);
180 // ignore any configuration changes like screen rotation etc
183 void CXBMCApp::onLowMemory()
185 android_printf("%s: %d", __PRETTY_FUNCTION__, m_state.appState);
186 // can't do much as we don't want to close completely
189 void CXBMCApp::onCreateWindow(ANativeWindow* window)
191 android_printf("%s: %d", __PRETTY_FUNCTION__, m_state.appState);
194 android_printf(" => invalid ANativeWindow object");
200 void CXBMCApp::onResizeWindow()
202 android_printf("%s: %d", __PRETTY_FUNCTION__, m_state.appState);
203 // no need to do anything because we are fixed in fullscreen landscape mode
206 void CXBMCApp::onDestroyWindow()
208 android_printf("%s: %d", __PRETTY_FUNCTION__, m_state.appState);
210 if (m_state.appState < Paused)
212 XBMC_DestroyDisplay();
218 void CXBMCApp::onGainFocus()
220 android_printf("%s: %d", __PRETTY_FUNCTION__, m_state.appState);
221 // everything is handled in onActivate()
224 void CXBMCApp::onLostFocus()
226 android_printf("%s: %d", __PRETTY_FUNCTION__, m_state.appState);
227 switch (m_state.appState)
232 setAppState(Unfocused);
240 bool CXBMCApp::getWakeLock(JNIEnv *env)
242 android_printf("%s", __PRETTY_FUNCTION__);
243 if (m_activity == NULL)
245 android_printf(" missing activity => unable to use WakeLocks");
252 if (m_wakeLock == NULL)
254 jobject oActivity = m_activity->clazz;
255 jclass cActivity = env->GetObjectClass(oActivity);
258 jmethodID midActivityGetSystemService = env->GetMethodID(cActivity, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
259 jstring sPowerService = env->NewStringUTF("power"); // POWER_SERVICE
260 jobject oPowerManager = env->CallObjectMethod(oActivity, midActivityGetSystemService, sPowerService);
262 jclass cPowerManager = env->GetObjectClass(oPowerManager);
263 jmethodID midNewWakeLock = env->GetMethodID(cPowerManager, "newWakeLock", "(ILjava/lang/String;)Landroid/os/PowerManager$WakeLock;");
264 jstring sXbmcPackage = env->NewStringUTF("org.xbmc.xbmc");
265 jobject oWakeLock = env->CallObjectMethod(oPowerManager, midNewWakeLock, (jint)0x1a /* FULL_WAKE_LOCK */, sXbmcPackage);
266 m_wakeLock = env->NewGlobalRef(oWakeLock);
268 env->DeleteLocalRef(oWakeLock);
269 env->DeleteLocalRef(cPowerManager);
270 env->DeleteLocalRef(oPowerManager);
271 env->DeleteLocalRef(sPowerService);
272 env->DeleteLocalRef(sXbmcPackage);
273 env->DeleteLocalRef(cActivity);
276 return m_wakeLock != NULL;
279 void CXBMCApp::acquireWakeLock()
281 if (m_activity == NULL)
285 AttachCurrentThread(&env);
287 if (!getWakeLock(env))
289 android_printf("%s: unable to acquire a WakeLock");
293 jclass cWakeLock = env->GetObjectClass(m_wakeLock);
294 jmethodID midWakeLockAcquire = env->GetMethodID(cWakeLock, "acquire", "()V");
295 env->CallVoidMethod(m_wakeLock, midWakeLockAcquire);
296 env->DeleteLocalRef(cWakeLock);
298 DetachCurrentThread();
301 void CXBMCApp::releaseWakeLock()
303 if (m_activity == NULL)
307 AttachCurrentThread(&env);
309 if (!getWakeLock(env))
311 android_printf("%s: unable to release a WakeLock");
315 jclass cWakeLock = env->GetObjectClass(m_wakeLock);
316 jmethodID midWakeLockRelease = env->GetMethodID(cWakeLock, "release", "()V");
317 env->CallVoidMethod(m_wakeLock, midWakeLockRelease);
318 env->DeleteLocalRef(cWakeLock);
320 DetachCurrentThread();
326 setAppState(Initialized);
328 android_printf(" => running XBMC_Run...");
331 setAppState(Rendering);
332 status = XBMC_Run(true);
333 android_printf(" => XBMC_Run finished with %d", status);
337 android_printf("ERROR: Exception caught on main loop. Exiting");
341 bool finishActivity = false;
342 pthread_mutex_lock(&m_state.mutex);
343 finishActivity = m_state.appState != Stopping;
344 m_state.appState = Stopped;
345 pthread_mutex_unlock(&m_state.mutex);
349 android_printf(" => calling ANativeActivity_finish()");
350 ANativeActivity_finish(m_activity);
354 void CXBMCApp::stop()
356 android_printf("%s", __PRETTY_FUNCTION__);
358 pthread_mutex_lock(&m_state.mutex);
359 if (m_state.appState < Stopped)
361 m_state.appState = Stopping;
362 pthread_mutex_unlock(&m_state.mutex);
364 android_printf(" => executing XBMC_Stop");
366 android_printf(" => waiting for XBMC to finish");
367 pthread_join(m_state.thread, NULL);
368 android_printf(" => XBMC finished");
371 pthread_mutex_unlock(&m_state.mutex);
373 if (m_wakeLock != NULL && m_activity != NULL)
376 m_activity->vm->AttachCurrentThread(&env, NULL);
378 env->DeleteGlobalRef(m_wakeLock);
383 void CXBMCApp::setAppState(AppState state)
385 pthread_mutex_lock(&m_state.mutex);
386 m_state.appState = state;
387 pthread_mutex_unlock(&m_state.mutex);
390 void CXBMCApp::XBMC_Pause(bool pause)
392 android_printf("XBMC_Pause(%s)", pause ? "true" : "false");
393 // Only send the PAUSE action if we are pausing XBMC and something is currently playing
394 if (pause && g_application.IsPlaying() && !g_application.IsPaused())
395 CApplicationMessenger::Get().SendAction(CAction(ACTION_PAUSE), WINDOW_INVALID, true);
397 g_application.m_AppActive = g_application.m_AppFocused = !pause;
400 void CXBMCApp::XBMC_Stop()
402 CApplicationMessenger::Get().Quit();
405 bool CXBMCApp::XBMC_SetupDisplay()
407 android_printf("XBMC_SetupDisplay()");
408 return CApplicationMessenger::Get().SetupDisplay();
411 bool CXBMCApp::XBMC_DestroyDisplay()
413 android_printf("XBMC_DestroyDisplay()");
414 return CApplicationMessenger::Get().DestroyDisplay();
417 int CXBMCApp::AttachCurrentThread(JNIEnv** p_env, void* thr_args /* = NULL */)
419 // Until a thread is attached, it has no JNIEnv, and cannot make JNI calls.
420 // The JNIEnv is used for thread-local storage. For this reason,
421 // you cannot share a JNIEnv between threads.
422 // If a thread is attached to JNIEnv and garbage collection is in progress,
423 // or the debugger has issued a suspend request, Android will
424 // pause the thread the next time it makes a JNI call.
425 return m_activity->vm->AttachCurrentThread(p_env, thr_args);
428 int CXBMCApp::DetachCurrentThread()
430 // Threads attached through JNIEnv must
431 // call DetachCurrentThread before they exit
432 return m_activity->vm->DetachCurrentThread();
435 int CXBMCApp::SetBuffersGeometry(int width, int height, int format)
437 return ANativeWindow_setBuffersGeometry(m_window, width, height, format);
440 int CXBMCApp::android_printf(const char *format, ...)
442 // For use before CLog is setup by XBMC_Run()
444 va_start(args, format);
445 int result = __android_log_vprint(ANDROID_LOG_VERBOSE, "XBMC", format, args);
450 int CXBMCApp::GetDPI()
452 if (m_activity == NULL || m_activity->assetManager == NULL)
455 // grab DPI from the current configuration - this is approximate
456 // but should be close enough for what we need
457 AConfiguration *config = AConfiguration_new();
458 AConfiguration_fromAssetManager(config, m_activity->assetManager);
459 int dpi = AConfiguration_getDensity(config);
460 AConfiguration_delete(config);
465 bool CXBMCApp::ListApplications(vector<androidPackage> *applications)
471 AttachCurrentThread(&env);
472 jobject oActivity = m_activity->clazz;
473 jclass cActivity = env->GetObjectClass(oActivity);
475 // oPackageManager = new PackageManager();
476 jmethodID mgetPackageManager = env->GetMethodID(cActivity, "getPackageManager", "()Landroid/content/pm/PackageManager;");
477 jobject oPackageManager = (jobject)env->CallObjectMethod(oActivity, mgetPackageManager);
478 env->DeleteLocalRef(cActivity);
480 // adata[] = oPackageManager.getInstalledApplications(0);
481 jclass cPackageManager = env->GetObjectClass(oPackageManager);
482 jmethodID mgetInstalledApplications = env->GetMethodID(cPackageManager, "getInstalledApplications", "(I)Ljava/util/List;");
483 jmethodID mgetApplicationLabel = env->GetMethodID(cPackageManager, "getApplicationLabel", "(Landroid/content/pm/ApplicationInfo;)Ljava/lang/CharSequence;");
484 jobject odata = env->CallObjectMethod(oPackageManager, mgetInstalledApplications, 0);
485 jclass cdata = env->GetObjectClass(odata);
486 jmethodID mtoArray = env->GetMethodID(cdata, "toArray", "()[Ljava/lang/Object;");
487 jobjectArray adata = (jobjectArray)env->CallObjectMethod(odata, mtoArray);
488 env->DeleteLocalRef(cdata);
489 env->DeleteLocalRef(odata);
490 env->DeleteLocalRef(cPackageManager);
492 int size = env->GetArrayLength(adata);
493 for (int i = 0; i < size; i++)
495 // oApplicationInfo = adata[i];
496 jobject oApplicationInfo = env->GetObjectArrayElement(adata, i);
497 jclass cApplicationInfo = env->GetObjectClass(oApplicationInfo);
498 jfieldID mclassName = env->GetFieldID(cApplicationInfo, "packageName", "Ljava/lang/String;");
499 jstring sapplication = (jstring)env->GetObjectField(oApplicationInfo, mclassName);
503 env->DeleteLocalRef(cApplicationInfo);
504 env->DeleteLocalRef(oApplicationInfo);
507 // cname = oApplicationInfo.packageName;
508 const char* cname = env->GetStringUTFChars(sapplication, NULL);
510 desc.packageName = cname;
511 env->ReleaseStringUTFChars(sapplication, cname);
512 env->DeleteLocalRef(sapplication);
513 env->DeleteLocalRef(cApplicationInfo);
515 jstring spackageLabel = (jstring) env->CallObjectMethod(oPackageManager, mgetApplicationLabel, oApplicationInfo);
518 env->DeleteLocalRef(oApplicationInfo);
521 // cname = opackageManager.getApplicationLabel(oApplicationInfo);
522 const char* cpackageLabel = env->GetStringUTFChars(spackageLabel, NULL);
523 desc.packageLabel = cpackageLabel;
524 env->ReleaseStringUTFChars(spackageLabel, cpackageLabel);
525 env->DeleteLocalRef(spackageLabel);
526 env->DeleteLocalRef(oApplicationInfo);
528 if (!HasLaunchIntent(desc.packageName))
531 applications->push_back(desc);
533 env->DeleteLocalRef(oPackageManager);
534 DetachCurrentThread();
538 bool CXBMCApp::GetIconSize(const string &packageName, int *width, int *height)
545 AttachCurrentThread(&env);
547 jobject oActivity = m_activity->clazz;
548 jclass cActivity = env->GetObjectClass(oActivity);
550 // oPackageManager = new PackageManager();
551 jmethodID mgetPackageManager = env->GetMethodID(cActivity, "getPackageManager", "()Landroid/content/pm/PackageManager;");
552 jobject oPackageManager = (jobject)env->CallObjectMethod(oActivity, mgetPackageManager);
553 env->DeleteLocalRef(cActivity);
555 jclass cPackageManager = env->GetObjectClass(oPackageManager);
556 jmethodID mgetApplicationIcon = env->GetMethodID(cPackageManager, "getApplicationIcon", "(Ljava/lang/String;)Landroid/graphics/drawable/Drawable;");
557 env->DeleteLocalRef(cPackageManager);
559 jclass cBitmapDrawable = env->FindClass("android/graphics/drawable/BitmapDrawable");
560 jmethodID mBitmapDrawableCtor = env->GetMethodID(cBitmapDrawable, "<init>", "()V");
561 jmethodID mgetBitmap = env->GetMethodID(cBitmapDrawable, "getBitmap", "()Landroid/graphics/Bitmap;");
563 // BitmapDrawable oBitmapDrawable;
564 jobject oBitmapDrawable = env->NewObject(cBitmapDrawable, mBitmapDrawableCtor);
565 jstring sPackageName = env->NewStringUTF(packageName.c_str());
567 // oBitmapDrawable = oPackageManager.getApplicationIcon(sPackageName)
568 oBitmapDrawable = env->CallObjectMethod(oPackageManager, mgetApplicationIcon, sPackageName);
569 jobject oBitmap = env->CallObjectMethod(oBitmapDrawable, mgetBitmap);
570 env->DeleteLocalRef(sPackageName);
571 env->DeleteLocalRef(cBitmapDrawable);
572 env->DeleteLocalRef(oBitmapDrawable);
573 env->DeleteLocalRef(oPackageManager);
574 exc = env->ExceptionOccurred();
577 CLog::Log(LOGERROR, "CXBMCApp::GetIconSize Error getting icon size for %s. Exception follows:", packageName.c_str());
578 env->ExceptionDescribe();
579 env->ExceptionClear();
580 env->DeleteLocalRef(oBitmap);
581 DetachCurrentThread();
584 jclass cBitmap = env->GetObjectClass(oBitmap);
585 jmethodID mgetWidth = env->GetMethodID(cBitmap, "getWidth", "()I");
586 jmethodID mgetHeight = env->GetMethodID(cBitmap, "getHeight", "()I");
587 env->DeleteLocalRef(cBitmap);
589 // width = oBitmap.getWidth;
590 *width = (int)env->CallIntMethod(oBitmap, mgetWidth);
592 exc = env->ExceptionOccurred();
595 CLog::Log(LOGERROR, "CXBMCApp::GetIconSize Error getting icon width for %s. Exception follows:", packageName.c_str());
596 env->ExceptionDescribe();
597 env->ExceptionClear();
598 env->DeleteLocalRef(oBitmap);
599 DetachCurrentThread();
602 // height = oBitmap.getHeight;
603 *height = (int)env->CallIntMethod(oBitmap, mgetHeight);
604 env->DeleteLocalRef(oBitmap);
606 exc = env->ExceptionOccurred();
609 CLog::Log(LOGERROR, "CXBMCApp::GetIconSize Error getting icon height for %s. Exception follows:", packageName.c_str());
610 env->ExceptionDescribe();
611 env->ExceptionClear();
612 DetachCurrentThread();
616 DetachCurrentThread();
620 bool CXBMCApp::GetIcon(const string &packageName, void* buffer, unsigned int bufSize)
627 AttachCurrentThread(&env);
629 CLog::Log(LOGERROR, "CXBMCApp::GetIconSize Looking for: %s", packageName.c_str());
631 jobject oActivity = m_activity->clazz;
632 jclass cActivity = env->GetObjectClass(oActivity);
634 // oPackageManager = new PackageManager();
635 jmethodID mgetPackageManager = env->GetMethodID(cActivity, "getPackageManager", "()Landroid/content/pm/PackageManager;");
636 jobject oPackageManager = (jobject)env->CallObjectMethod(oActivity, mgetPackageManager);
637 env->DeleteLocalRef(cActivity);
639 jclass cPackageManager = env->GetObjectClass(oPackageManager);
640 jmethodID mgetApplicationIcon = env->GetMethodID(cPackageManager, "getApplicationIcon", "(Ljava/lang/String;)Landroid/graphics/drawable/Drawable;");
641 env->DeleteLocalRef(cPackageManager);
643 jclass cBitmapDrawable = env->FindClass("android/graphics/drawable/BitmapDrawable");
644 jmethodID mBitmapDrawableCtor = env->GetMethodID(cBitmapDrawable, "<init>", "()V");
645 jmethodID mgetBitmap = env->GetMethodID(cBitmapDrawable, "getBitmap", "()Landroid/graphics/Bitmap;");
647 // BitmapDrawable oBitmapDrawable;
648 jobject oBitmapDrawable = env->NewObject(cBitmapDrawable, mBitmapDrawableCtor);
649 jstring sPackageName = env->NewStringUTF(packageName.c_str());
651 // oBitmapDrawable = oPackageManager.getApplicationIcon(sPackageName)
652 oBitmapDrawable = env->CallObjectMethod(oPackageManager, mgetApplicationIcon, sPackageName);
653 env->DeleteLocalRef(sPackageName);
654 env->DeleteLocalRef(cBitmapDrawable);
655 env->DeleteLocalRef(oPackageManager);
656 exc = env->ExceptionOccurred();
659 CLog::Log(LOGERROR, "CXBMCApp::GetIcon Error getting icon for %s. Exception follows:", packageName.c_str());
660 env->ExceptionDescribe();
661 env->ExceptionClear();
662 DetachCurrentThread();
665 jobject oBitmap = env->CallObjectMethod(oBitmapDrawable, mgetBitmap);
666 env->DeleteLocalRef(oBitmapDrawable);
667 jclass cBitmap = env->GetObjectClass(oBitmap);
668 jmethodID mcopyPixelsToBuffer = env->GetMethodID(cBitmap, "copyPixelsToBuffer", "(Ljava/nio/Buffer;)V");
669 jobject oPixels = env->NewDirectByteBuffer(buffer,bufSize);
670 env->DeleteLocalRef(cBitmap);
672 // memcpy(buffer,oPixels,bufSize);
673 env->CallVoidMethod(oBitmap, mcopyPixelsToBuffer, oPixels);
674 env->DeleteLocalRef(oPixels);
675 env->DeleteLocalRef(oBitmap);
676 exc = env->ExceptionOccurred();
679 CLog::Log(LOGERROR, "CXBMCApp::GetIcon Error copying icon for %s. Exception follows:", packageName.c_str());
680 env->ExceptionDescribe();
681 env->ExceptionClear();
682 DetachCurrentThread();
685 DetachCurrentThread();
690 bool CXBMCApp::HasLaunchIntent(const string &package)
696 AttachCurrentThread(&env);
699 jobject oActivity = m_activity->clazz;
700 jclass cActivity = env->GetObjectClass(oActivity);
702 // oPackageManager = new PackageManager();
703 jmethodID mgetPackageManager = env->GetMethodID(cActivity, "getPackageManager", "()Landroid/content/pm/PackageManager;");
704 jobject oPackageManager = (jobject)env->CallObjectMethod(oActivity, mgetPackageManager);
706 // oPackageIntent = oPackageManager.getLaunchIntentForPackage(package);
707 jclass cPackageManager = env->GetObjectClass(oPackageManager);
708 jmethodID mgetLaunchIntentForPackage = env->GetMethodID(cPackageManager, "getLaunchIntentForPackage", "(Ljava/lang/String;)Landroid/content/Intent;");
709 jstring sPackageName = env->NewStringUTF(package.c_str());
710 jobject oPackageIntent = env->CallObjectMethod(oPackageManager, mgetLaunchIntentForPackage, sPackageName);
711 env->DeleteLocalRef(sPackageName);
712 env->DeleteLocalRef(cPackageManager);
713 env->DeleteLocalRef(oPackageManager);
715 exc = env->ExceptionOccurred();
718 CLog::Log(LOGERROR, "CXBMCApp::HasLaunchIntent Error checking for Launch Intent for %s. Exception follows:", package.c_str());
719 env->ExceptionDescribe();
720 env->ExceptionClear();
728 env->DeleteLocalRef(oPackageIntent);
732 // Note intent, dataType, dataURI all default to ""
733 bool CXBMCApp::StartActivity(const string &package, const string &intent, const string &dataType, const string &dataURI)
735 if (!m_activity || !package.size())
738 CLog::Log(LOGDEBUG, "CXBMCApp::StartActivity package: '%s' intent: '%s' dataType: '%s' dataURI: '%s'", package.c_str(), intent.c_str(), dataType.c_str(), dataURI.c_str());
742 AttachCurrentThread(&env);
744 jobject oActivity = m_activity->clazz;
745 jclass cActivity = env->GetObjectClass(oActivity);
747 jobject oIntent = NULL;
748 jclass cIntent = NULL;
751 // Java equivalent for following JNI
752 // Intent oIntent = new Intent(Intent.ACTION_VIEW);
753 cIntent = env->FindClass("android/content/Intent");
754 jmethodID midIntentCtor = env->GetMethodID(cIntent, "<init>", "(Ljava/lang/String;)V");
755 jstring sIntent = env->NewStringUTF(intent.c_str());
756 oIntent = env->NewObject(cIntent, midIntentCtor, sIntent);
757 env->DeleteLocalRef(sIntent);
761 // oPackageManager = new PackageManager();
762 jmethodID mgetPackageManager = env->GetMethodID(cActivity, "getPackageManager", "()Landroid/content/pm/PackageManager;");
763 jobject oPackageManager = (jobject)env->CallObjectMethod(oActivity, mgetPackageManager);
765 // oPackageIntent = oPackageManager.getLaunchIntentForPackage(package);
766 jclass cPackageManager = env->GetObjectClass(oPackageManager);
767 jmethodID mgetLaunchIntentForPackage = env->GetMethodID(cPackageManager, "getLaunchIntentForPackage", "(Ljava/lang/String;)Landroid/content/Intent;");
768 jstring sPackageName = env->NewStringUTF(package.c_str());
769 oIntent = env->CallObjectMethod(oPackageManager, mgetLaunchIntentForPackage, sPackageName);
770 cIntent = env->GetObjectClass(oIntent);
771 env->DeleteLocalRef(cPackageManager);
772 env->DeleteLocalRef(sPackageName);
773 env->DeleteLocalRef(oPackageManager);
775 exc = env->ExceptionOccurred();
778 CLog::Log(LOGERROR, "CXBMCApp::StartActivity Failed to load %s. Exception follows:", package.c_str());
779 env->ExceptionDescribe();
780 env->ExceptionClear();
781 env->DeleteLocalRef(cActivity);
782 DetachCurrentThread();
787 CLog::Log(LOGERROR, "CXBMCApp::StartActivity %s has no Launch Intent", package.c_str());
788 env->DeleteLocalRef(cActivity);
789 DetachCurrentThread();
797 // Java equivalent for the following JNI
798 // Uri oUri = Uri.parse(sPath);
799 jclass cUri = env->FindClass("android/net/Uri");
800 jmethodID midUriParse = env->GetStaticMethodID(cUri, "parse", "(Ljava/lang/String;)Landroid/net/Uri;");
801 jstring sPath = env->NewStringUTF(dataURI.c_str());
802 oUri = env->CallStaticObjectMethod(cUri, midUriParse, sPath);
803 env->DeleteLocalRef(sPath);
804 env->DeleteLocalRef(cUri);
806 // Run setData or setDataAndType depending on what was passed into the method
807 // This allows opening market links or external players using the same method
810 // Java equivalent for the following JNI
811 // oIntent.setDataAndType(oUri, "video/*");
812 jmethodID midIntentSetDataAndType = env->GetMethodID(cIntent, "setDataAndType", "(Landroid/net/Uri;Ljava/lang/String;)Landroid/content/Intent;");
813 jstring sMimeType = env->NewStringUTF(dataType.c_str());
814 oIntent = env->CallObjectMethod(oIntent, midIntentSetDataAndType, oUri, sMimeType);
815 env->DeleteLocalRef(sMimeType);
819 // Java equivalent for the following JNI
820 // oIntent.setData(oUri);
821 jmethodID midIntentSetData = env->GetMethodID(cIntent, "setData", "(Landroid/net/Uri;)Landroid/content/Intent;");
822 oIntent = env->CallObjectMethod(oIntent, midIntentSetData, oUri);
826 // Java equivalent for the following JNI
827 // oIntent.setPackage(sPackage);
828 jstring sPackage = env->NewStringUTF(package.c_str());
829 jmethodID mSetPackage = env->GetMethodID(cIntent, "setPackage", "(Ljava/lang/String;)Landroid/content/Intent;");
830 oIntent = env->CallObjectMethod(oIntent, mSetPackage, sPackage);
834 env->DeleteLocalRef(oUri);
836 env->DeleteLocalRef(cIntent);
837 env->DeleteLocalRef(sPackage);
839 // Java equivalent for the following JNI
840 // startActivity(oIntent);
841 jmethodID mStartActivity = env->GetMethodID(cActivity, "startActivity", "(Landroid/content/Intent;)V");
842 env->CallVoidMethod(oActivity, mStartActivity, oIntent);
843 env->DeleteLocalRef(cActivity);
844 env->DeleteLocalRef(oIntent);
846 exc = env->ExceptionOccurred();
849 CLog::Log(LOGERROR, "CXBMCApp::StartActivity Failed to load %s. Exception follows:", package.c_str());
850 env->ExceptionDescribe();
851 env->ExceptionClear();
852 DetachCurrentThread();
856 DetachCurrentThread();
860 int CXBMCApp::GetBatteryLevel()
862 if (m_activity == NULL)
866 AttachCurrentThread(&env);
867 jobject oActivity = m_activity->clazz;
869 // IntentFilter oIntentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
870 jclass cIntentFilter = env->FindClass("android/content/IntentFilter");
871 jmethodID midIntentFilterCtor = env->GetMethodID(cIntentFilter, "<init>", "(Ljava/lang/String;)V");
872 jstring sIntentBatteryChanged = env->NewStringUTF("android.intent.action.BATTERY_CHANGED"); // Intent.ACTION_BATTERY_CHANGED
873 jobject oIntentFilter = env->NewObject(cIntentFilter, midIntentFilterCtor, sIntentBatteryChanged);
874 env->DeleteLocalRef(cIntentFilter);
875 env->DeleteLocalRef(sIntentBatteryChanged);
877 // Intent oBatteryStatus = activity.registerReceiver(null, oIntentFilter);
878 jclass cActivity = env->GetObjectClass(oActivity);
879 jmethodID midActivityRegisterReceiver = env->GetMethodID(cActivity, "registerReceiver", "(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;");
880 env->DeleteLocalRef(cActivity);
881 jobject oBatteryStatus = env->CallObjectMethod(oActivity, midActivityRegisterReceiver, NULL, oIntentFilter);
883 jclass cIntent = env->GetObjectClass(oBatteryStatus);
884 jmethodID midIntentGetIntExtra = env->GetMethodID(cIntent, "getIntExtra", "(Ljava/lang/String;I)I");
885 env->DeleteLocalRef(cIntent);
887 // int iLevel = oBatteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
888 jstring sBatteryManagerExtraLevel = env->NewStringUTF("level"); // BatteryManager.EXTRA_LEVEL
889 jint iLevel = env->CallIntMethod(oBatteryStatus, midIntentGetIntExtra, sBatteryManagerExtraLevel, (jint)-1);
890 env->DeleteLocalRef(sBatteryManagerExtraLevel);
891 // int iScale = oBatteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
892 jstring sBatteryManagerExtraScale = env->NewStringUTF("scale"); // BatteryManager.EXTRA_SCALE
893 jint iScale = env->CallIntMethod(oBatteryStatus, midIntentGetIntExtra, sBatteryManagerExtraScale, (jint)-1);
894 env->DeleteLocalRef(sBatteryManagerExtraScale);
895 env->DeleteLocalRef(oBatteryStatus);
896 env->DeleteLocalRef(oIntentFilter);
898 DetachCurrentThread();
900 if (iLevel <= 0 || iScale < 0)
903 return ((int)iLevel * 100) / (int)iScale;
906 bool CXBMCApp::GetExternalStorage(std::string &path, const std::string &type /* = "" */)
908 if (m_activity == NULL)
912 AttachCurrentThread(&env);
914 // check if external storage is available
915 // String sStorageState = android.os.Environment.getExternalStorageState();
916 jclass cEnvironment = env->FindClass("android/os/Environment");
917 jmethodID midEnvironmentGetExternalStorageState = env->GetStaticMethodID(cEnvironment, "getExternalStorageState", "()Ljava/lang/String;");
918 jstring sStorageState = (jstring)env->CallStaticObjectMethod(cEnvironment, midEnvironmentGetExternalStorageState);
919 // if (sStorageState != Environment.MEDIA_MOUNTED && sStorageState != Environment.MEDIA_MOUNTED_READ_ONLY) return false;
920 const char* storageState = env->GetStringUTFChars(sStorageState, NULL);
921 bool mounted = strcmp(storageState, "mounted") == 0 || strcmp(storageState, "mounted_ro") == 0;
922 env->ReleaseStringUTFChars(sStorageState, storageState);
923 env->DeleteLocalRef(sStorageState);
927 jobject oExternalStorageDirectory = NULL;
928 if (type.empty() || type == "files")
930 // File oExternalStorageDirectory = Environment.getExternalStorageDirectory();
931 jmethodID midEnvironmentGetExternalStorageDirectory = env->GetStaticMethodID(cEnvironment, "getExternalStorageDirectory", "()Ljava/io/File;");
932 oExternalStorageDirectory = env->CallStaticObjectMethod(cEnvironment, midEnvironmentGetExternalStorageDirectory);
934 else if (type == "music" || type == "videos" || type == "pictures" || type == "photos" || type == "downloads")
936 jstring sType = NULL;
938 sType = env->NewStringUTF("Music"); // Environment.DIRECTORY_MUSIC
939 else if (type == "videos")
940 sType = env->NewStringUTF("Movies"); // Environment.DIRECTORY_MOVIES
941 else if (type == "pictures")
942 sType = env->NewStringUTF("Pictures"); // Environment.DIRECTORY_PICTURES
943 else if (type == "photos")
944 sType = env->NewStringUTF("DCIM"); // Environment.DIRECTORY_DCIM
945 else if (type == "downloads")
946 sType = env->NewStringUTF("Download"); // Environment.DIRECTORY_DOWNLOADS
948 // File oExternalStorageDirectory = Environment.getExternalStoragePublicDirectory(sType);
949 jmethodID midEnvironmentGetExternalStoragePublicDirectory = env->GetStaticMethodID(cEnvironment, "getExternalStoragePublicDirectory", "(Ljava/lang/String;)Ljava/io/File;");
950 oExternalStorageDirectory = env->CallStaticObjectMethod(cEnvironment, midEnvironmentGetExternalStoragePublicDirectory, sType);
951 env->DeleteLocalRef(sType);
954 if (oExternalStorageDirectory != NULL)
956 // path = oExternalStorageDirectory.getAbsolutePath();
957 jclass cFile = env->GetObjectClass(oExternalStorageDirectory);
958 jmethodID midFileGetAbsolutePath = env->GetMethodID(cFile, "getAbsolutePath", "()Ljava/lang/String;");
959 env->DeleteLocalRef(cFile);
960 jstring sPath = (jstring)env->CallObjectMethod(oExternalStorageDirectory, midFileGetAbsolutePath);
961 const char* cPath = env->GetStringUTFChars(sPath, NULL);
963 env->ReleaseStringUTFChars(sPath, cPath);
964 env->DeleteLocalRef(sPath);
965 env->DeleteLocalRef(oExternalStorageDirectory);
971 env->DeleteLocalRef(cEnvironment);
973 DetachCurrentThread();
975 return mounted && !path.empty();
978 bool CXBMCApp::GetStorageUsage(const std::string &path, std::string &usage)
980 if (m_activity == NULL)
986 fmt.width(24); fmt << left << "Filesystem";
987 fmt.width(12); fmt << right << "Size";
988 fmt.width(12); fmt << "Used";
989 fmt.width(12); fmt << "Avail";
990 fmt.width(12); fmt << "Use %";
997 AttachCurrentThread(&env);
999 // android.os.StatFs oStats = new android.os.StatFs(sPath);
1000 jclass cStatFs = env->FindClass("android/os/StatFs");
1001 jmethodID midStatFsCtor = env->GetMethodID(cStatFs, "<init>", "(Ljava/lang/String;)V");
1002 jstring sPath = env->NewStringUTF(path.c_str());
1003 jobject oStats = env->NewObject(cStatFs, midStatFsCtor, sPath);
1004 env->DeleteLocalRef(sPath);
1006 // int iBlockSize = oStats.getBlockSize();
1007 jmethodID midStatFsGetBlockSize = env->GetMethodID(cStatFs, "getBlockSize", "()I");
1008 jint iBlockSize = env->CallIntMethod(oStats, midStatFsGetBlockSize);
1010 // int iBlocksTotal = oStats.getBlockCount();
1011 jmethodID midStatFsGetBlockCount = env->GetMethodID(cStatFs, "getBlockCount", "()I");
1012 jint iBlocksTotal = env->CallIntMethod(oStats, midStatFsGetBlockCount);
1014 // int iBlocksFree = oStats.getFreeBlocks();
1015 jmethodID midStatFsGetFreeBlocks = env->GetMethodID(cStatFs, "getFreeBlocks", "()I");
1016 jint iBlocksFree = env->CallIntMethod(oStats, midStatFsGetFreeBlocks);
1018 env->DeleteLocalRef(oStats);
1019 env->DeleteLocalRef(cStatFs);
1021 DetachCurrentThread();
1023 if (iBlockSize <= 0 || iBlocksTotal <= 0 || iBlocksFree < 0)
1026 float totalSize = (float)iBlockSize * iBlocksTotal / GIGABYTES;
1027 float freeSize = (float)iBlockSize * iBlocksFree / GIGABYTES;
1028 float usedSize = totalSize - freeSize;
1029 float usedPercentage = usedSize / totalSize * 100;
1034 fmt.width(24); fmt << left << path;
1035 fmt.width(12); fmt << right << totalSize << "G"; // size in GB
1036 fmt.width(12); fmt << usedSize << "G"; // used in GB
1037 fmt.width(12); fmt << freeSize << "G"; // free
1039 fmt.width(12); fmt << usedPercentage << "%"; // percentage used
1045 // Used in Application.cpp to figure out volume steps
1046 int CXBMCApp::GetMaxSystemVolume()
1048 static int maxVolume = -1;
1049 if (maxVolume == -1)
1052 AttachCurrentThread(&env);
1053 maxVolume = GetMaxSystemVolume(env);
1054 DetachCurrentThread();
1059 int CXBMCApp::GetMaxSystemVolume(JNIEnv *env)
1061 jobject oActivity = m_activity->clazz;
1062 jclass cActivity = env->GetObjectClass(oActivity);
1064 // Get Audio manager
1065 // (AudioManager)getSystemService(Context.AUDIO_SERVICE)
1066 jmethodID mgetSystemService = env->GetMethodID(cActivity, "getSystemService","(Ljava/lang/String;)Ljava/lang/Object;");
1067 jstring sAudioService = env->NewStringUTF("audio");
1068 jobject oAudioManager = env->CallObjectMethod(oActivity, mgetSystemService, sAudioService);
1069 env->DeleteLocalRef(sAudioService);
1070 env->DeleteLocalRef(cActivity);
1073 // int max_volume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
1074 jclass cAudioManager = env->GetObjectClass(oAudioManager);
1075 jmethodID mgetStreamMaxVolume = env->GetMethodID(cAudioManager, "getStreamMaxVolume", "(I)I");
1076 jfieldID fstreamMusic = env->GetStaticFieldID(cAudioManager, "STREAM_MUSIC", "I");
1077 jint stream_music = env->GetStaticIntField(cAudioManager, fstreamMusic);
1078 int maxVolume = (int)env->CallObjectMethod(oAudioManager, mgetStreamMaxVolume, stream_music); // AudioManager.STREAM_MUSIC
1080 env->DeleteLocalRef(oAudioManager);
1081 env->DeleteLocalRef(cAudioManager);
1086 void CXBMCApp::SetSystemVolume(JNIEnv *env, float percent)
1088 CLog::Log(LOGDEBUG, "CXBMCApp::SetSystemVolume: %f", percent);
1090 jobject oActivity = m_activity->clazz;
1091 jclass cActivity = env->GetObjectClass(oActivity);
1093 // Get Audio manager
1094 // (AudioManager)getSystemService(Context.AUDIO_SERVICE)
1095 jmethodID mgetSystemService = env->GetMethodID(cActivity, "getSystemService","(Ljava/lang/String;)Ljava/lang/Object;");
1096 jstring sAudioService = env->NewStringUTF("audio");
1097 jobject oAudioManager = env->CallObjectMethod(oActivity, mgetSystemService, sAudioService);
1098 jclass cAudioManager = env->GetObjectClass(oAudioManager);
1099 env->DeleteLocalRef(sAudioService);
1100 env->DeleteLocalRef(cActivity);
1103 // mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, max_volume, 0);
1104 jfieldID fstreamMusic = env->GetStaticFieldID(cAudioManager, "STREAM_MUSIC", "I");
1105 jint stream_music = env->GetStaticIntField(cAudioManager, fstreamMusic);
1106 jmethodID msetStreamVolume = env->GetMethodID(cAudioManager, "setStreamVolume", "(III)V");
1107 env->CallObjectMethod(oAudioManager, msetStreamVolume, stream_music, int(GetMaxSystemVolume(env)*percent), 0);
1108 env->DeleteLocalRef(oAudioManager);
1109 env->DeleteLocalRef(cAudioManager);