diff --git a/project/cmake/scripts/android/Install.cmake b/project/cmake/scripts/android/Install.cmake
index 51b9685045..38658c660f 100644
--- a/project/cmake/scripts/android/Install.cmake
+++ b/project/cmake/scripts/android/Install.cmake
@@ -69,7 +69,7 @@ set(package_files strings.xml
src/content/XBMCYTDLContentProvider.java
src/XBMCSettingsContentObserver.java
src/XBMCMainView.java
- src/KeepAliveService.java
+ src/Service.java
)
foreach(file IN LISTS package_files)
configure_file(${CORE_SOURCE_DIR}/tools/android/packaging/xbmc/${file}.in
diff --git a/project/cmake/treedata/android/subdirs.txt b/project/cmake/treedata/android/subdirs.txt
index 428f8aea8f..c307e1f2c5 100644
--- a/project/cmake/treedata/android/subdirs.txt
+++ b/project/cmake/treedata/android/subdirs.txt
@@ -13,4 +13,5 @@ xbmc/windowing/egl windowing/egl
xbmc/platform/posix posix
xbmc/platform/android android
xbmc/platform/android/activity android_activity
+xbmc/platform/android/service android_service
xbmc/platform/android/bionic_supplement android_bionicsupplement
diff --git a/tools/android/packaging/xbmc/AndroidManifest.xml.in b/tools/android/packaging/xbmc/AndroidManifest.xml.in
index 2d4196a3d4..2789795363 100644
--- a/tools/android/packaging/xbmc/AndroidManifest.xml.in
+++ b/tools/android/packaging/xbmc/AndroidManifest.xml.in
@@ -165,7 +165,7 @@
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE" />
diff --git a/tools/android/packaging/xbmc/src/Service.java.in b/tools/android/packaging/xbmc/src/Service.java.in
new file mode 100644
index 0000000000..9669d31c52
--- /dev/null
+++ b/tools/android/packaging/xbmc/src/Service.java.in
@@ -0,0 +1,71 @@
+package @APP_PACKAGE@;
+
+import android.app.Notification;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.IBinder;
+
+public class Service extends android.app.Service
+{
+ native boolean _launchApplication();
+
+ static private Boolean m_isStarted = false;
+ public static Boolean isStarted()
+ {
+ return m_isStarted;
+ }
+
+ public Service()
+ {
+ }
+
+ @Override
+ public void onCreate()
+ {
+ System.loadLibrary("@APP_NAME_LC@");
+ super.onCreate();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId)
+ {
+ if (!m_isStarted)
+ m_isStarted =_launchApplication();
+ if (m_isStarted)
+ {
+ Intent i = new Intent("@APP_PACKAGE@.SERVICE_STARTED");
+ sendBroadcast(i);
+ }
+
+ Bitmap icon = BitmapFactory.decodeResource(getResources(),
+ R.drawable.ic_recommendation_80dp);
+
+ Notification.Builder builder = new Notification.Builder(this)
+ .setContentTitle(getString(R.string.app_name))
+ .setContentText("@APP_NAME@ is running...")
+ .setSmallIcon(R.drawable.notif_icon)
+ .setLargeIcon(Bitmap.createScaledBitmap(icon, 128, 128, false))
+ ;
+
+ Notification notification = builder.build();
+ startForeground(1, notification);
+ return START_STICKY;
+ }
+
+ @Override
+ public void onDestroy()
+ {
+ Intent i = new Intent("@APP_PACKAGE@.SERVICE_STOPPED");
+ sendBroadcast(i);
+
+ super.onDestroy();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent)
+ {
+ // TODO: Return the communication channel to the service.
+ throw new UnsupportedOperationException("Not implemented");
+ }
+}
diff --git a/tools/android/packaging/xbmc/src/Splash.java.in b/tools/android/packaging/xbmc/src/Splash.java.in
index 13c7e0ea3d..d8a67c6710 100644
--- a/tools/android/packaging/xbmc/src/Splash.java.in
+++ b/tools/android/packaging/xbmc/src/Splash.java.in
@@ -3,7 +3,6 @@ package @APP_PACKAGE@;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
-import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -11,19 +10,15 @@ import java.io.OutputStream;
import java.lang.System;
import java.net.HttpURLConnection;
import java.net.URL;
-import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
-import java.util.Arrays;
-import java.util.Comparator;
import java.util.Properties;
import java.util.Enumeration;
import java.util.ArrayList;
-import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
@@ -63,6 +58,8 @@ public class Splash extends Activity
private static final int StorageChecked = 8;
private static final int DownloadingObb = 90;
private static final int DownloadObbDone = 91;
+ private static final int StartingService = 97;
+ private static final int WaitingService = 98;
private static final int StartingXBMC = 99;
private static final String TAG = "@APP_NAME@";
@@ -85,6 +82,8 @@ public class Splash extends Activity
private BroadcastReceiver mExternalStorageReceiver = null;
private boolean mExternalStorageChecked = false;
+ private BroadcastReceiver mServiceReceiver = null;
+ private boolean mServiceChecked = false;
private boolean mCachingDone = false;
private boolean mInstallLibs = false;
@@ -123,7 +122,7 @@ public class Splash extends Activity
break;
case CachingDone:
mSplash.mCachingDone = true;
- sendEmptyMessage(StartingXBMC);
+ sendEmptyMessage(StartingService);
break;
case WaitingStorageChecked:
mSplash.mTextView.setText("Waiting for external storage...");
@@ -134,7 +133,7 @@ public class Splash extends Activity
mExternalStorageChecked = true;
mSplash.stopWatchingExternalStorage();
if (mSplash.mCachingDone)
- sendEmptyMessage(StartingXBMC);
+ sendEmptyMessage(StartingService);
else
{
SetupEnvironment();
@@ -147,7 +146,7 @@ public class Splash extends Activity
mState = CachingDone;
mCachingDone = true;
- sendEmptyMessage(StartingXBMC);
+ sendEmptyMessage(StartingService);
}
else
{
@@ -155,8 +154,24 @@ public class Splash extends Activity
}
}
+ break;
+ case StartingService:
+ if (mServiceChecked)
+ sendEmptyMessage(StartingXBMC);
+ else
+ {
+ mSplash.mTextView.setText("Starting Service...");
+ mSplash.mProgress.setVisibility(View.INVISIBLE);
+ mSplash.startService();
+ sendEmptyMessage(WaitingService);
+ }
+ break;
+ case WaitingService:
+ mSplash.mTextView.setText("Waiting for Service...");
+ mSplash.mProgress.setVisibility(View.INVISIBLE);
break;
case StartingXBMC:
+ stopWatchingService();
mSplash.mTextView.setText("Starting @APP_NAME@...");
mSplash.mProgress.setVisibility(View.INVISIBLE);
mSplash.startXBMC();
@@ -739,6 +754,45 @@ public class Splash extends Activity
unregisterReceiver(mExternalStorageReceiver);
}
+ void updateServiceState()
+ {
+ mServiceChecked = Service.isStarted();
+ if (!mServiceChecked)
+ mStateMachine.sendEmptyMessage(StartingService);
+ else
+ mStateMachine.sendEmptyMessage(StartingXBMC);
+ }
+
+ void startWatchingService()
+ {
+ mServiceReceiver = new BroadcastReceiver()
+ {
+ @Override
+ public void onReceive(Context context, Intent intent)
+ {
+ updateServiceState();
+ }
+ };
+ IntentFilter filter = new IntentFilter();
+ filter.addAction("@APP_PACKAGE@.SERVICE_STARTED");
+ registerReceiver(mServiceReceiver, filter);
+ }
+
+ void stopWatchingService()
+ {
+ if (mServiceReceiver != null)
+ unregisterReceiver(mServiceReceiver);
+ }
+
+ protected void startService()
+ {
+ if (!Service.isStarted())
+ {
+ startWatchingService();
+ startService(new Intent(this, Service.class));
+ }
+ }
+
protected void startXBMC()
{
// Run @APP_NAME@
@@ -746,6 +800,7 @@ public class Splash extends Activity
intent.setClass(this, @APP_PACKAGE@.Main.class);
intent.addFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
startActivity(intent);
+
finish();
}
@@ -845,6 +900,9 @@ public class Splash extends Activity
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()))
mExternalStorageChecked = true;
+ if (Service.isStarted())
+ mServiceChecked = true;
+
if (mState != InError && mExternalStorageChecked)
{
mState = ChecksDone;
@@ -859,7 +917,7 @@ public class Splash extends Activity
}
}
- if ((mState != DownloadingObb && mState != InError) && mCachingDone && mExternalStorageChecked)
+ if ((mState != DownloadingObb && mState != InError) && mCachingDone && mExternalStorageChecked && mServiceChecked)
{
startXBMC();
return;
diff --git a/tools/depends/target/libandroidjni/Makefile b/tools/depends/target/libandroidjni/Makefile
index 589f591182..9fea83ccd1 100644
--- a/tools/depends/target/libandroidjni/Makefile
+++ b/tools/depends/target/libandroidjni/Makefile
@@ -3,7 +3,7 @@ DEPS= ../../Makefile.include Makefile
# lib name, version
LIBNAME=libandroidjni
-VERSION=29aae460c8144d939d5f0bf16af97f9ede6b12fb
+VERSION=3fadaf0daa4b7b99337a6c41bce54e7db783b5d7
SOURCE=archive
ARCHIVE=$(VERSION).tar.gz
GIT_BASE_URL=https://github.com/koying
diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp
index 3bbe2709c3..9f3b44af95 100644
--- a/xbmc/Application.cpp
+++ b/xbmc/Application.cpp
@@ -177,6 +177,7 @@
#ifdef TARGET_WINDOWS
#include "win32util.h"
+#define __PRETTY_FUNCTION__ __FUNCTION__
#endif
#ifdef TARGET_DARWIN_OSX
@@ -209,6 +210,7 @@
#include
#include
#include "platform/android/activity/XBMCApp.h"
+#include "platform/android/service/XBMCService.h"
#include "platform/android/activity/AndroidFeatures.h"
#endif
@@ -271,6 +273,8 @@ CApplication::CApplication(void)
, m_stackFileItemToUpdate(new CFileItem)
, m_threadID(0)
, m_bInitializing(true)
+ , m_bGUIInitialized(false)
+ , m_bGUICreated(false)
, m_bPlatformDirectories(true)
, m_progressTrackingVideoResumeBookmark(*new CBookmark)
, m_progressTrackingItem(new CFileItem)
@@ -391,7 +395,7 @@ static void CopyUserDataIfNeeded(const std::string &strPath, const std::string &
destPath = URIUtils::AddFileToFolder(strPath, file);
else
destPath = URIUtils::AddFileToFolder(strPath, destname);
-
+
if (!CFile::Exists(destPath))
{
// need to copy it across
@@ -476,7 +480,7 @@ bool CApplication::Create()
// set special://envhome
CSpecialProtocol::SetEnvHomePath(getenv("HOME"));
#endif
-
+
// only the InitDirectories* for the current platform should return true
bool inited = InitDirectoriesLinux();
if (!inited)
@@ -488,7 +492,7 @@ bool CApplication::Create()
CopyUserDataIfNeeded("special://masterprofile/", "RssFeeds.xml");
CopyUserDataIfNeeded("special://masterprofile/", "favourites.xml");
CopyUserDataIfNeeded("special://masterprofile/", "Lircmap.xml");
-
+
//! @todo - move to CPlatformXXX
#ifdef TARGET_DARWIN_IOS
CopyUserDataIfNeeded("special://masterprofile/", "iOS/sources.xml", "sources.xml");
@@ -518,7 +522,7 @@ bool CApplication::Create()
buildType = "Unknown";
#endif
std::string specialVersion;
-
+
//! @todo - move to CPlatformXXX
#if defined(TARGET_RASPBERRY_PI)
specialVersion = " (version for Raspberry Pi)";
@@ -547,7 +551,7 @@ bool CApplication::Create()
CLog::Log(LOGNOTICE, "Host CPU: %s, %d core%s available", cpuModel.c_str(), g_cpuInfo.getCPUCount(), (g_cpuInfo.getCPUCount() == 1) ? "" : "s");
else
CLog::Log(LOGNOTICE, "%d CPU core%s available", g_cpuInfo.getCPUCount(), (g_cpuInfo.getCPUCount() == 1) ? "" : "s");
-
+
//! @todo - move to CPlatformXXX ???
#if defined(TARGET_WINDOWS)
CLog::Log(LOGNOTICE, "%s", CWIN32Util::GetResInfoString().c_str());
@@ -560,11 +564,11 @@ bool CApplication::Create()
CJNIBuild::PRODUCT.c_str(), CJNIBuild::DEVICE.c_str(), CJNIBuild::BOARD.c_str(),
CJNIBuild::MANUFACTURER.c_str(), CJNIBuild::BRAND.c_str(), CJNIBuild::MODEL.c_str(), CJNIBuild::HARDWARE.c_str());
std::string extstorage;
- bool extready = CXBMCApp::GetExternalStorage(extstorage);
+ bool extready = CXBMCService::GetExternalStorage(extstorage);
CLog::Log(LOGNOTICE, "External storage path = %s; status = %s", extstorage.c_str(), extready ? "ok" : "nok");
CLog::Log(LOGNOTICE, "System library paths = %s", CJNISystem::getProperty("java.library.path").c_str());
- CLog::Log(LOGNOTICE, "App library path = %s", CXBMCApp::get()->getApplicationInfo().nativeLibraryDir.c_str());
- CLog::Log(LOGNOTICE, "APK = %s", CXBMCApp::get()->getPackageResourcePath().c_str());
+ CLog::Log(LOGNOTICE, "App library path = %s", CXBMCService::get()->getApplicationInfo().nativeLibraryDir.c_str());
+ CLog::Log(LOGNOTICE, "APK = %s", CXBMCService::get()->getPackageResourcePath().c_str());
CLog::Log(LOGNOTICE, "HasTouchScreen = %s", CAndroidFeatures::HasTouchScreen() ? "yes" : "no");
#endif
@@ -703,7 +707,7 @@ bool CApplication::Create()
bool CApplication::CreateGUI()
{
- m_frameMoveGuard.lock();
+ CLog::Log(LOGDEBUG, "%s", __PRETTY_FUNCTION__);
m_renderGUI = true;
#ifdef HAS_SDL
@@ -780,7 +784,7 @@ bool CApplication::CreateGUI()
CDisplaySettings::GetInstance().SetCurrentResolution(RES_DESKTOP);
sav_res = true;
}
- if (!InitWindow())
+ if (!InitWindow(CDisplaySettings::GetInstance().GetCurrentResolution()))
{
return false;
}
@@ -801,7 +805,90 @@ bool CApplication::CreateGUI()
info.iHeight,
info.strMode.c_str());
- g_windowManager.Initialize();
+ std::string defaultSkin = ((const CSettingString*)CSettings::GetInstance().GetSetting(CSettings::SETTING_LOOKANDFEEL_SKIN))->GetDefault();
+ if (!LoadSkin(CSettings::GetInstance().GetString(CSettings::SETTING_LOOKANDFEEL_SKIN)))
+ {
+ CLog::Log(LOGERROR, "Failed to load skin '%s'", CSettings::GetInstance().GetString(CSettings::SETTING_LOOKANDFEEL_SKIN).c_str());
+ if (!LoadSkin(defaultSkin))
+ {
+ CLog::Log(LOGFATAL, "Default skin '%s' could not be loaded! Terminating..", defaultSkin.c_str());
+ return false;
+ }
+ }
+ m_bGUICreated = true;
+
+ CLog::Log(LOGDEBUG, ">>> %s", __PRETTY_FUNCTION__);
+
+ return true;
+}
+
+bool CApplication::DestroyGUI()
+{
+ CLog::Log(LOGDEBUG, "%s", __PRETTY_FUNCTION__);
+
+ UnloadSkin();
+
+ g_Windowing.DestroyRenderSystem();
+ g_Windowing.DestroyWindowSystem();
+
+ m_bGUICreated = false;
+
+ return true;
+}
+
+bool CApplication::StartGUI()
+{
+ CLog::Log(LOGDEBUG, "%s", __PRETTY_FUNCTION__);
+ bool uiInitializationFinished = true;
+
+ // initialize splash window after splash screen disappears
+ // because we need a real window in the background which gets
+ // rendered while we load the main window or enter the master lock key
+ if (g_advancedSettings.m_splashImage)
+ g_windowManager.ActivateWindow(WINDOW_SPLASH);
+
+ if (CSettings::GetInstance().GetBool(CSettings::SETTING_MASTERLOCK_STARTUPLOCK) &&
+ CProfilesManager::GetInstance().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE &&
+ !CProfilesManager::GetInstance().GetMasterProfile().getLockCode().empty())
+ {
+ g_passwordManager.CheckStartUpLock();
+ }
+
+ // check if we should use the login screen
+ if (CProfilesManager::GetInstance().UsingLoginScreen())
+ {
+ // the login screen still needs to perform additional initialization
+ uiInitializationFinished = false;
+
+ g_windowManager.ActivateWindow(WINDOW_LOGIN_SCREEN);
+ }
+ else
+ {
+ // activate the configured start window
+ int firstWindow = g_SkinInfo->GetFirstWindow();
+ g_windowManager.ActivateWindow(firstWindow);
+
+ if (g_windowManager.GetActiveWindowID() == WINDOW_STARTUP_ANIM)
+ {
+ CLog::Log(LOGWARNING, "CApplication::Initialize - startup.xml taints init process");
+ }
+
+ // the startup window is considered part of the initialization as it most likely switches to the final window
+ uiInitializationFinished = firstWindow != WINDOW_STARTUP_ANIM;
+
+ CStereoscopicsManager::GetInstance().Initialize();
+
+ if (!m_ServiceManager->Init3())
+ {
+ CLog::Log(LOGERROR, "Application - Init3 failed");
+ }
+ }
+ // if the user interfaces has been fully initialized let everyone know
+ if (uiInitializationFinished)
+ {
+ CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UI_READY);
+ g_windowManager.SendThreadMessage(msg);
+ }
return true;
}
@@ -944,7 +1031,7 @@ bool CApplication::InitDirectoriesLinux()
CSpecialProtocol::SetXBMCBinAddonPath(appBinPath + "/addons");
#if defined(TARGET_ANDROID)
- CXBMCApp::get()->InitDirectories();
+ CXBMCService::get()->InitDirectories();
#endif
return true;
@@ -1093,6 +1180,8 @@ void CApplication::CreateUserDirs() const
bool CApplication::Initialize()
{
+ m_frameMoveGuard.lock();
+
#if defined(HAS_DVD_DRIVE) && !defined(TARGET_WINDOWS) // somehow this throws an "unresolved external symbol" on win32
// turn off cdio logging
cdio_loglevel_default = CDIO_LOG_ERROR;
@@ -1116,10 +1205,6 @@ bool CApplication::Initialize()
StringUtils::Format(g_localizeStrings.Get(178).c_str(), g_sysinfo.GetAppName().c_str()),
"special://xbmc/media/icon256x256.png", EventLevel::Basic)));
-#if !defined(TARGET_DARWIN_IOS)
- g_peripherals.Initialise();
-#endif
-
// Load curl so curl_global_init gets called before any service threads
// are started. Unloading will have no effect as curl is never fully unloaded.
// To quote man curl_global_init:
@@ -1164,38 +1249,42 @@ bool CApplication::Initialize()
// Init DPMS, before creating the corresponding setting control.
m_dpms = new DPMSSupport();
- bool uiInitializationFinished = true;
+ bool uiInitializationFinished = false;
+
+ std::vector incompatibleAddons;
+ event.Reset();
+ std::atomic isMigratingAddons(false);
+ CJobManager::GetInstance().Submit([&event, &incompatibleAddons, &isMigratingAddons]() {
+ incompatibleAddons = CAddonSystemSettings::GetInstance().MigrateAddons([&isMigratingAddons]() {
+ isMigratingAddons = true;
+ });
+ event.Set();
+ }, CJob::PRIORITY_DEDICATED);
+ localizedStr = g_localizeStrings.Get(24151);
+ iDots = 1;
+ while (!event.WaitMSec(1000))
+ {
+ if (isMigratingAddons)
+ CSplash::GetInstance().Show(std::string(iDots, ' ') + localizedStr + std::string(iDots, '.'));
+ if (iDots == 3)
+ iDots = 1;
+ else
+ ++iDots;
+ }
+ CSplash::GetInstance().Show();
+ m_incompatibleAddons = incompatibleAddons;
+
+ g_windowManager.CreateWindows();
+
if (g_windowManager.Initialized())
{
- CSettings::GetInstance().GetSetting(CSettings::SETTING_POWERMANAGEMENT_DISPLAYSOFF)->SetRequirementsMet(m_dpms->IsSupported());
+ CLog::Log(LOGDEBUG, "CApplication: Start with GUI");
- g_windowManager.CreateWindows();
-
- m_confirmSkinChange = false;
-
- std::vector incompatibleAddons;
- event.Reset();
- std::atomic isMigratingAddons(false);
- CJobManager::GetInstance().Submit([&event, &incompatibleAddons, &isMigratingAddons]() {
- incompatibleAddons = CAddonSystemSettings::GetInstance().MigrateAddons([&isMigratingAddons]() {
- isMigratingAddons = true;
- });
- event.Set();
- }, CJob::PRIORITY_DEDICATED);
- localizedStr = g_localizeStrings.Get(24151);
- iDots = 1;
- while (!event.WaitMSec(1000))
- {
- if (isMigratingAddons)
- CSplash::GetInstance().Show(std::string(iDots, ' ') + localizedStr + std::string(iDots, '.'));
- if (iDots == 3)
- iDots = 1;
- else
- ++iDots;
- }
- CSplash::GetInstance().Show();
- m_incompatibleAddons = incompatibleAddons;
- m_confirmSkinChange = true;
+#if !defined(TARGET_DARWIN_IOS)
+ g_peripherals.Initialise();
+#endif
+
+ CSettings::GetInstance().GetSetting(CSettings::SETTING_POWERMANAGEMENT_DISPLAYSOFF)->SetRequirementsMet(m_dpms->IsSupported());
std::string defaultSkin = ((const CSettingString*)CSettings::GetInstance().GetSetting(CSettings::SETTING_LOOKANDFEEL_SKIN))->GetDefault();
if (!LoadSkin(CSettings::GetInstance().GetString(CSettings::SETTING_LOOKANDFEEL_SKIN)))
@@ -1221,6 +1310,7 @@ bool CApplication::Initialize()
g_passwordManager.CheckStartUpLock();
}
+ uiInitializationFinished = true;
// check if we should use the login screen
if (CProfilesManager::GetInstance().UsingLoginScreen())
{
@@ -1259,6 +1349,8 @@ bool CApplication::Initialize()
}
else //No GUI Created
{
+ CLog::Log(LOGDEBUG, "CApplication: No GUI");
+
#ifdef HAS_JSONRPC
CJSONRPC::Initialize();
#endif
@@ -1300,6 +1392,7 @@ bool CApplication::Initialize()
CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UI_READY);
g_windowManager.SendThreadMessage(msg);
}
+ m_bInitializing = false;
return true;
}
@@ -1582,7 +1675,7 @@ bool CApplication::OnSettingsSaving() const
void CApplication::ReloadSkin(bool confirm/*=false*/)
{
- if (!g_SkinInfo || m_bInitializing)
+ if (!g_SkinInfo || !m_bGUIInitialized)
return; // Don't allow reload before skin is loaded by system
std::string oldSkin = g_SkinInfo->ID();
@@ -2523,19 +2616,43 @@ void CApplication::OnApplicationMessage(ThreadMessage* pMsg)
break;
#ifdef TARGET_ANDROID
+ case TMSG_DISPLAY_INIT:
+ if (m_renderGUI)
+ break;
+ CreateGUI();
+ if (!g_application.IsGUIInitialized())
+ StartGUI();
+
+ if (g_advancedSettings.m_videoUseDroidProjectionCapture)
+ CXBMCApp::get()->startProjection();
+
+ CXBMCApp::get()->Initialize();
+ break;
+
case TMSG_DISPLAY_SETUP:
- // If we are rendering GUI, we are in first run
if (m_renderGUI)
break;
+
+ if (!g_application.IsGUICreated())
+ CreateGUI();
+
// We might come from a refresh rate switch destroying the native window; use the context resolution
InitWindow(g_graphicsContext.GetVideoResolution());
SetRenderGUI(true);
break;
- case TMSG_DISPLAY_DESTROY:
+ case TMSG_DISPLAY_CLEANUP:
DestroyWindow();
SetRenderGUI(false);
break;
+
+ case TMSG_DISPLAY_DESTROY:
+ if (g_application.IsGUICreated())
+ {
+ DestroyGUI();
+ SetRenderGUI(false);
+ }
+ break;
#endif
case TMSG_SETAUDIODSPSTATE:
@@ -2870,9 +2987,7 @@ bool CApplication::Cleanup()
// unloading
CScriptInvocationManager::GetInstance().Uninitialize();
- g_Windowing.DestroyRenderSystem();
- g_Windowing.DestroyWindow();
- g_Windowing.DestroyWindowSystem();
+ DestroyGUI();
g_windowManager.DestroyWindows();
CLog::Log(LOGNOTICE, "unload sections");
@@ -2978,16 +3093,22 @@ void CApplication::Stop(int exitCode)
CLog::Log(LOGNOTICE, "stop all");
// cancel any jobs from the jobmanager
+ CLog::Log(LOGNOTICE, "CancelJobs");
CJobManager::GetInstance().CancelJobs();
// stop scanning before we kill the network and so on
+ CLog::Log(LOGNOTICE, "m_musicInfoScanner->Stop");
if (m_musicInfoScanner->IsScanning())
m_musicInfoScanner->Stop(true);
+ CLog::Log(LOGNOTICE, "CancelAllJobs");
if (CVideoLibraryQueue::GetInstance().IsRunning())
CVideoLibraryQueue::GetInstance().CancelAllJobs();
+ CLog::Log(LOGNOTICE, "GetADSP().Deactivate");
CServiceBroker::GetADSP().Deactivate();
+
+ CLog::Log(LOGNOTICE, "messenger cleanup");
CApplicationMessenger::GetInstance().Cleanup();
CLog::Log(LOGNOTICE, "stop player");
@@ -3028,7 +3149,7 @@ void CApplication::Stop(int exitCode)
// Close network handles
CloseNetworkShares();
-
+
g_audioManager.DeInitialize();
// shutdown the AudioEngine
CAEFactory::Shutdown();
@@ -4235,7 +4356,7 @@ bool CApplication::OnMessage(CGUIMessage& message)
// show info dialog about moved configuration files if needed
ShowAppMigrationMessage();
- m_bInitializing = false;
+ m_bGUIInitialized = true;
}
}
break;
@@ -4602,7 +4723,8 @@ void CApplication::ProcessSlow()
#ifdef TARGET_ANDROID
// Pass the slow loop to droid
- CXBMCApp::get()->ProcessSlow();
+ if (CXBMCApp::get())
+ CXBMCApp::get()->ProcessSlow();
#endif
// check for any idle curl connections
diff --git a/xbmc/Application.h b/xbmc/Application.h
index d186ca6dc6..53324f590a 100644
--- a/xbmc/Application.h
+++ b/xbmc/Application.h
@@ -149,8 +149,12 @@ class CApplication : public CXBApplicationEx, public IPlayerCallback, public IMs
virtual bool Cleanup() override;
bool IsInitialized() { return !m_bInitializing; }
+ bool IsGUICreated() { return m_bGUICreated; }
+ bool IsGUIInitialized() { return m_bGUIInitialized; }
bool CreateGUI();
+ bool DestroyGUI();
+ bool StartGUI();
bool InitWindow(RESOLUTION res = RES_INVALID);
bool DestroyWindow();
void StartServices();
@@ -475,6 +479,8 @@ class CApplication : public CXBApplicationEx, public IPlayerCallback, public IMs
std::string m_prevMedia;
ThreadIdentifier m_threadID; // application thread ID. Used in applicationMessanger to know where we are firing a thread with delay from.
bool m_bInitializing;
+ bool m_bGUIInitialized;
+ bool m_bGUICreated;
bool m_bPlatformDirectories;
CBookmark& m_progressTrackingVideoResumeBookmark;
diff --git a/xbmc/Util.cpp b/xbmc/Util.cpp
index 2561ce055e..074208daf0 100644
--- a/xbmc/Util.cpp
+++ b/xbmc/Util.cpp
@@ -40,7 +40,7 @@
#if defined(TARGET_ANDROID)
#include
#include "platform/android/bionic_supplement/bionic_supplement.h"
-#include "platform/android/activity/XBMCApp.h"
+#include "platform/android/service/XBMCService.h"
#include "CompileInfo.h"
#endif
#include
@@ -1792,7 +1792,7 @@ std::string CUtil::ResolveExecutablePath()
else
strExecutablePath = buf;
#elif defined(TARGET_ANDROID)
- strExecutablePath = CXBMCApp::get()->getApplicationInfo().nativeLibraryDir;
+ strExecutablePath = CXBMCService::get()->getApplicationInfo().nativeLibraryDir;
std::string appName = CCompileInfo::GetAppName();
std::string libName = "lib" + appName + ".so";
diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp
index e9626e3fc4..829895b64b 100644
--- a/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp
+++ b/xbmc/cores/AudioEngine/Sinks/AESinkAUDIOTRACK.cpp
@@ -27,6 +27,7 @@
#include "cores/AudioEngine/Utils/AEUtil.h"
#include "platform/android/activity/XBMCApp.h"
+#include "platform/android/service/XBMCService.h"
#include "settings/Settings.h"
#include "settings/AdvancedSettings.h"
#include "utils/log.h"
@@ -923,7 +924,7 @@ void CAESinkAUDIOTRACK::EnumerateDevicesEx(AEDeviceInfoList &list, bool force)
// Enumerate audio devices on API >= 23
if (CJNIAudioManager::GetSDKVersion() >= 23)
{
- CJNIAudioManager audioManager(CXBMCApp::get()->getSystemService("audio"));
+ CJNIAudioManager audioManager(CXBMCService::get()->getSystemService("audio"));
CJNIAudioDeviceInfos audiodevices = audioManager.getDevices(CJNIAudioManager::GET_DEVICES_OUTPUTS);
if (g_advancedSettings.CanLogComponent(LOGAUDIO))
LogAudoDevices("EnumerateDevicesEx", audiodevices);
diff --git a/xbmc/dialogs/GUIDialogMediaSource.cpp b/xbmc/dialogs/GUIDialogMediaSource.cpp
index 27dde9dfff..f9a95facec 100644
--- a/xbmc/dialogs/GUIDialogMediaSource.cpp
+++ b/xbmc/dialogs/GUIDialogMediaSource.cpp
@@ -42,7 +42,7 @@
#include "pvr/recordings/PVRRecordingsPath.h"
#if defined(TARGET_ANDROID)
-#include "platform/android/activity/XBMCApp.h"
+#include "platform/android/service/XBMCService.h"
#include "filesystem/File.h"
#endif
@@ -238,7 +238,7 @@ void CGUIDialogMediaSource::OnPathBrowse(int item)
#if defined(TARGET_ANDROID)
// add the default android music directory
std::string path;
- if (CXBMCApp::GetExternalStorage(path, "music") && !path.empty() && CDirectory::Exists(path))
+ if (CXBMCService::GetExternalStorage(path, "music") && !path.empty() && CDirectory::Exists(path))
{
share1.strPath = path;
share1.strName = g_localizeStrings.Get(20240);
@@ -280,7 +280,7 @@ void CGUIDialogMediaSource::OnPathBrowse(int item)
#if defined(TARGET_ANDROID)
// add the default android video directory
std::string path;
- if (CXBMCApp::GetExternalStorage(path, "videos") && !path.empty() && CFile::Exists(path))
+ if (CXBMCService::GetExternalStorage(path, "videos") && !path.empty() && CFile::Exists(path))
{
share1.strPath = path;
share1.strName = g_localizeStrings.Get(20241);
@@ -315,7 +315,7 @@ void CGUIDialogMediaSource::OnPathBrowse(int item)
#if defined(TARGET_ANDROID)
// add the default android music directory
std::string path;
- if (CXBMCApp::GetExternalStorage(path, "pictures") && !path.empty() && CFile::Exists(path))
+ if (CXBMCService::GetExternalStorage(path, "pictures") && !path.empty() && CFile::Exists(path))
{
share1.strPath = path;
share1.strName = g_localizeStrings.Get(20242);
@@ -324,7 +324,7 @@ void CGUIDialogMediaSource::OnPathBrowse(int item)
}
path.clear();
- if (CXBMCApp::GetExternalStorage(path, "photos") && !path.empty() && CFile::Exists(path))
+ if (CXBMCService::GetExternalStorage(path, "photos") && !path.empty() && CFile::Exists(path))
{
share1.strPath = path;
share1.strName = g_localizeStrings.Get(20243);
diff --git a/xbmc/messaging/ApplicationMessenger.h b/xbmc/messaging/ApplicationMessenger.h
index 5f15c6da85..9bac470153 100644
--- a/xbmc/messaging/ApplicationMessenger.h
+++ b/xbmc/messaging/ApplicationMessenger.h
@@ -91,6 +91,8 @@
#define TMSG_VIDEORESIZE TMSG_MASK_APPLICATION + 28
#define TMSG_SETAUDIODSPSTATE TMSG_MASK_APPLICATION + 29
#define TMSG_SYSTEM_POWERDOWN TMSG_MASK_APPLICATION + 30
+#define TMSG_DISPLAY_INIT TMSG_MASK_APPLICATION + 31
+#define TMSG_DISPLAY_CLEANUP TMSG_MASK_APPLICATION + 32
#define TMSG_GUI_INFOLABEL TMSG_MASK_GUIINFOMANAGER + 0
#define TMSG_GUI_INFOBOOL TMSG_MASK_GUIINFOMANAGER + 1
diff --git a/xbmc/network/android/NetworkAndroid.cpp b/xbmc/network/android/NetworkAndroid.cpp
index b720ee485d..f5257c9a1a 100644
--- a/xbmc/network/android/NetworkAndroid.cpp
+++ b/xbmc/network/android/NetworkAndroid.cpp
@@ -28,11 +28,12 @@
#include
#include
-#include "platform/android/activity/XBMCApp.h"
+#include "platform/android/service/XBMCService.h"
#include "utils/StringUtils.h"
#include "utils/log.h"
+#include
#include
#include
#include
@@ -66,7 +67,7 @@ std::string& CNetworkInterfaceAndroid::GetName()
bool CNetworkInterfaceAndroid::IsEnabled()
{
- CJNIConnectivityManager connman(CXBMCApp::get()->getSystemService(CJNIContext::CONNECTIVITY_SERVICE));
+ CJNIConnectivityManager connman(CXBMCService::get()->getSystemService(CJNIContext::CONNECTIVITY_SERVICE));
CJNINetworkInfo ni = connman.getNetworkInfo(m_network);
if (!ni)
return false;
@@ -76,7 +77,7 @@ bool CNetworkInterfaceAndroid::IsEnabled()
bool CNetworkInterfaceAndroid::IsConnected()
{
- CJNIConnectivityManager connman(CXBMCApp::get()->getSystemService(CJNIContext::CONNECTIVITY_SERVICE));
+ CJNIConnectivityManager connman(CXBMCService::get()->getSystemService(CJNIContext::CONNECTIVITY_SERVICE));
CJNINetworkInfo ni = connman.getNetworkInfo(m_network);
if (!ni)
return false;
@@ -86,7 +87,7 @@ bool CNetworkInterfaceAndroid::IsConnected()
bool CNetworkInterfaceAndroid::IsWireless()
{
- CJNIConnectivityManager connman(CXBMCApp::get()->getSystemService(CJNIContext::CONNECTIVITY_SERVICE));
+ CJNIConnectivityManager connman(CXBMCService::get()->getSystemService(CJNIContext::CONNECTIVITY_SERVICE));
CJNINetworkInfo ni = connman.getNetworkInfo(m_network);
if (!ni)
return false;
@@ -237,14 +238,14 @@ std::string CNetworkInterfaceAndroid::GetCurrentWirelessEssId()
{
std::string ret;
- CJNIConnectivityManager connman(CXBMCApp::get()->getSystemService(CJNIContext::CONNECTIVITY_SERVICE));
+ CJNIConnectivityManager connman(CXBMCService::get()->getSystemService(CJNIContext::CONNECTIVITY_SERVICE));
CJNINetworkInfo ni = connman.getNetworkInfo(m_network);
if (!ni)
return "";
if (ni.getType() == CJNIConnectivityManager::TYPE_WIFI)
{
- CJNIWifiManager wm = CXBMCApp::get()->getSystemService("wifi");
+ CJNIWifiManager wm = CXBMCService::get()->getSystemService("wifi");
if (wm.isWifiEnabled())
{
CJNIWifiInfo wi = wm.getConnectionInfo();
@@ -384,7 +385,7 @@ void CNetworkAndroid::RetrieveInterfaces()
m_oldInterfaces = m_interfaces;
m_interfaces.clear();
- CJNIConnectivityManager connman(CXBMCApp::get()->getSystemService(CJNIContext::CONNECTIVITY_SERVICE));
+ CJNIConnectivityManager connman(CXBMCService::get()->getSystemService(CJNIContext::CONNECTIVITY_SERVICE));
std::vector networks = connman.getAllNetworks();
for (auto n : networks)
diff --git a/xbmc/network/android/ZeroconfAndroid.cpp b/xbmc/network/android/ZeroconfAndroid.cpp
index 6851e510f1..866ae48c39 100644
--- a/xbmc/network/android/ZeroconfAndroid.cpp
+++ b/xbmc/network/android/ZeroconfAndroid.cpp
@@ -22,12 +22,12 @@
#include "utils/log.h"
-#include "platform/android/activity/XBMCApp.h"
+#include "platform/android/service/XBMCService.h"
#include "threads/SingleLock.h"
CZeroconfAndroid::CZeroconfAndroid()
{
- m_manager = CXBMCApp::get()->getSystemService(CJNIContext::NSD_SERVICE);
+ m_manager = CXBMCService::get()->getSystemService(CJNIContext::NSD_SERVICE);
}
CZeroconfAndroid::~CZeroconfAndroid()
diff --git a/xbmc/network/android/ZeroconfBrowserAndroid.cpp b/xbmc/network/android/ZeroconfBrowserAndroid.cpp
index 627af36861..54fa210dc1 100644
--- a/xbmc/network/android/ZeroconfBrowserAndroid.cpp
+++ b/xbmc/network/android/ZeroconfBrowserAndroid.cpp
@@ -22,7 +22,7 @@
#include
-#include "platform/android/activity/XBMCApp.h"
+#include "platform/android/service/XBMCService.h"
#include "guilib/GUIMessage.h"
#include "guilib/GUIWindowManager.h"
#include "GUIUserMessages.h"
@@ -35,7 +35,7 @@
CZeroconfBrowserAndroid::CZeroconfBrowserAndroid()
{
- m_manager = CXBMCApp::get()->getSystemService(CJNIContext::NSD_SERVICE);
+ m_manager = CXBMCService::get()->getSystemService(CJNIContext::NSD_SERVICE);
}
CZeroconfBrowserAndroid::~CZeroconfBrowserAndroid()
diff --git a/xbmc/platform/android/activity/AndroidFeatures.cpp b/xbmc/platform/android/activity/AndroidFeatures.cpp
index 72ff92496d..234feccdac 100644
--- a/xbmc/platform/android/activity/AndroidFeatures.cpp
+++ b/xbmc/platform/android/activity/AndroidFeatures.cpp
@@ -25,6 +25,7 @@
#include
#include "platform/android/activity/XBMCApp.h"
+#include "platform/android/service/XBMCService.h"
#include "utils/log.h"
bool CAndroidFeatures::HasNeon()
@@ -82,7 +83,7 @@ bool CAndroidFeatures::HasTouchScreen()
static int hastouchscreen = -1;
if (hastouchscreen == -1)
{
- if (CXBMCApp::get()->GetPackageManager().hasSystemFeature("android.hardware.touchscreen"))
+ if (CXBMCService::get()->GetPackageManager().hasSystemFeature("android.hardware.touchscreen"))
hastouchscreen = 1;
else
hastouchscreen = 0;
diff --git a/xbmc/platform/android/activity/JNIXBMCNsdManagerDiscoveryListener.cpp b/xbmc/platform/android/activity/JNIXBMCNsdManagerDiscoveryListener.cpp
index a554240c18..d5519e1201 100644
--- a/xbmc/platform/android/activity/JNIXBMCNsdManagerDiscoveryListener.cpp
+++ b/xbmc/platform/android/activity/JNIXBMCNsdManagerDiscoveryListener.cpp
@@ -23,7 +23,7 @@
#include
#include
-#include "XBMCApp.h"
+#include "platform/android/service/XBMCService.h"
#include "CompileInfo.h"
#include "utils/log.h"
@@ -35,7 +35,7 @@ static std::string s_className = std::string(CCompileInfo::GetClass()) + "/inter
CJNIXBMCNsdManagerDiscoveryListener::CJNIXBMCNsdManagerDiscoveryListener()
: CJNIBase(s_className)
{
- m_object = new_object(CXBMCApp::get()->getClassLoader().loadClass(GetDotClassName(GetClassName())));
+ m_object = new_object(CXBMCService::get()->getClassLoader().loadClass(GetDotClassName(GetClassName())));
m_object.setGlobal();
add_instance(m_object, this);
diff --git a/xbmc/platform/android/activity/JNIXBMCNsdManagerRegistrationListener.cpp b/xbmc/platform/android/activity/JNIXBMCNsdManagerRegistrationListener.cpp
index 26a24ae1df..8e947958c5 100644
--- a/xbmc/platform/android/activity/JNIXBMCNsdManagerRegistrationListener.cpp
+++ b/xbmc/platform/android/activity/JNIXBMCNsdManagerRegistrationListener.cpp
@@ -23,7 +23,7 @@
#include
#include
-#include "XBMCApp.h"
+#include "platform/android/service/XBMCService.h"
#include "CompileInfo.h"
#include "utils/log.h"
@@ -35,7 +35,7 @@ static std::string s_className = std::string(CCompileInfo::GetClass()) + "/inter
CJNIXBMCNsdManagerRegistrationListener::CJNIXBMCNsdManagerRegistrationListener()
: CJNIBase(s_className)
{
- m_object = new_object(CXBMCApp::get()->getClassLoader().loadClass(GetDotClassName(GetClassName())));
+ m_object = new_object(CXBMCService::get()->getClassLoader().loadClass(GetDotClassName(GetClassName())));
m_object.setGlobal();
add_instance(m_object, this);
diff --git a/xbmc/platform/android/activity/JNIXBMCNsdManagerResolveListener.cpp b/xbmc/platform/android/activity/JNIXBMCNsdManagerResolveListener.cpp
index 0fba8efdb4..b6952bd6ec 100644
--- a/xbmc/platform/android/activity/JNIXBMCNsdManagerResolveListener.cpp
+++ b/xbmc/platform/android/activity/JNIXBMCNsdManagerResolveListener.cpp
@@ -23,7 +23,7 @@
#include
#include
-#include "XBMCApp.h"
+#include "platform/android/service/XBMCService.h"
#include "CompileInfo.h"
using namespace jni;
@@ -34,7 +34,7 @@ static std::string s_className = std::string(CCompileInfo::GetClass()) + "/inter
CJNIXBMCNsdManagerResolveListener::CJNIXBMCNsdManagerResolveListener()
: CJNIBase(s_className)
{
- m_object = new_object(CXBMCApp::get()->getClassLoader().loadClass(GetDotClassName(GetClassName())));
+ m_object = new_object(CXBMCService::get()->getClassLoader().loadClass(GetDotClassName(GetClassName())));
m_object.setGlobal();
add_instance(m_object, this);
diff --git a/xbmc/platform/android/activity/JNIXBMCVideoView.cpp b/xbmc/platform/android/activity/JNIXBMCVideoView.cpp
index e695b01e9e..ddbe0006e6 100644
--- a/xbmc/platform/android/activity/JNIXBMCVideoView.cpp
+++ b/xbmc/platform/android/activity/JNIXBMCVideoView.cpp
@@ -21,8 +21,8 @@
#include "JNIXBMCVideoView.h"
#include
-#include
+#include "platform/android/service/XBMCService.h"
#include "utils/StringUtils.h"
#include "utils/log.h"
@@ -71,7 +71,7 @@ CJNIXBMCVideoView* CJNIXBMCVideoView::createVideoView(CJNISurfaceHolderCallback*
{
std::string signature = "()L" + s_className + ";";
- CJNIXBMCVideoView* pvw = new CJNIXBMCVideoView(call_static_method(s_className.c_str(),
+ CJNIXBMCVideoView* pvw = new CJNIXBMCVideoView(call_static_method(CXBMCService::get()->getClassLoader().loadClass(GetDotClassName(s_className)),
"createVideoView", signature.c_str()));
if (!*pvw)
{
diff --git a/xbmc/platform/android/activity/XBMCApp.cpp b/xbmc/platform/android/activity/XBMCApp.cpp
index 454c4732e1..f106d55dc7 100644
--- a/xbmc/platform/android/activity/XBMCApp.cpp
+++ b/xbmc/platform/android/activity/XBMCApp.cpp
@@ -92,8 +92,6 @@
#include "utils/Variant.h"
#include "video/videosync/VideoSyncAndroid.h"
#include "windowing/WinEvents.h"
-#include "platform/xbmc.h"
-#include "platform/XbmcContext.h"
#include "CompileInfo.h"
#include "interfaces/AnnouncementManager.h"
@@ -102,7 +100,6 @@
#include "utils/AMLUtils.h"
#endif
-#define GIGABYTES 1073741824
#define CAPTURE_QUEUE_MAXDEPTH 3
#define ACTION_XBMC_RESUME "android.intent.XBMC_RESUME"
@@ -117,15 +114,8 @@ using namespace KODI::MESSAGING;
using namespace ANNOUNCEMENT;
using namespace jni;
-template
-void* thread_run(void* obj)
-{
- (static_cast(obj)->*fn)();
- return NULL;
-}
-
CXBMCApp* CXBMCApp::m_xbmcappinstance = NULL;
-CCriticalSection CXBMCApp::m_AppMutex;
+CCriticalSection CXBMCApp::m_LayoutMutex;
std::unique_ptr CXBMCApp::m_mainView;
ANativeActivity *CXBMCApp::m_activity = NULL;
@@ -174,9 +164,10 @@ CXBMCApp::CXBMCApp(ANativeActivity* nativeActivity)
exit(1);
return;
}
+ m_audioFocusListener.reset(new CJNIXBMCAudioManagerOnAudioFocusChangeListener());
m_broadcastReceiver.reset(new CJNIXBMCBroadcastReceiver(this));
m_mainView.reset(new CJNIXBMCMainView(this));
- m_firstrun = true;
+ m_firstActivityRun = true;
m_exiting = false;
android_printf("CXBMCApp: Created");
}
@@ -240,13 +231,12 @@ void CXBMCApp::onStart()
}
#endif
- if (m_firstrun)
+ if (m_firstActivityRun)
{
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
- pthread_create(&m_thread, &attr, thread_run, this);
- pthread_attr_destroy(&attr);
+ if (!g_application.IsInitialized())
+ abort();
+
+ CApplicationMessenger::GetInstance().PostMsg(TMSG_DISPLAY_INIT);
// Some intent filters MUST be registered in code rather than through the manifest
CJNIIntentFilter intentFilter;
@@ -259,6 +249,8 @@ void CXBMCApp::onStart()
registerReceiver(*m_broadcastReceiver, intentFilter);
m_mediaSession.reset(new CJNIXBMCMediaSession());
+
+ m_firstActivityRun = false;
}
}
@@ -283,7 +275,7 @@ void CXBMCApp::onResume()
// Re-request Visible Behind
if ((m_playback_state & PLAYBACK_STATE_PLAYING) && (m_playback_state & PLAYBACK_STATE_VIDEO))
RequestVisibleBehind(true);
-
+
m_isResumed = true;
}
@@ -322,17 +314,12 @@ void CXBMCApp::onDestroy()
android_printf("%s", __PRETTY_FUNCTION__);
unregisterReceiver(*m_broadcastReceiver);
-
m_mediaSession.release();
- // If android is forcing us to stop, ask XBMC to exit then wait until it's
- // been destroyed.
- if (!m_exiting)
- {
- CApplicationMessenger::GetInstance().PostMsg(TMSG_QUIT);
- pthread_join(m_thread, NULL);
- android_printf(" => XBMC finished");
- }
+ if (m_playback_state & PLAYBACK_STATE_PLAYING)
+ CApplicationMessenger::GetInstance().SendMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1, static_cast(new CAction(ACTION_STOP)));
+
+ CApplicationMessenger::GetInstance().PostMsg(TMSG_DISPLAY_DESTROY);
}
void CXBMCApp::onSaveState(void **data, size_t *size)
@@ -350,7 +337,14 @@ void CXBMCApp::onConfigurationChanged()
void CXBMCApp::onLowMemory()
{
android_printf("%s: ", __PRETTY_FUNCTION__);
- // can't do much as we don't want to close completely
+
+ if (!m_isResumed)
+ {
+ if (m_playback_state & PLAYBACK_STATE_PLAYING)
+ CApplicationMessenger::GetInstance().SendMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1, static_cast(new CAction(ACTION_STOP)));
+
+ CApplicationMessenger::GetInstance().PostMsg(TMSG_DISPLAY_DESTROY);
+ }
}
void CXBMCApp::onCreateWindow(ANativeWindow* window)
@@ -391,10 +385,10 @@ void CXBMCApp::Initialize()
for (int i=0; i<5; ++i)
m_texturePool.push_back(texture_ids[i]);
- g_application.m_ServiceManager->GetAnnouncementManager().AddAnnouncer(CXBMCApp::get());
+ g_application.m_ServiceManager->GetAnnouncementManager().AddAnnouncer(this);
}
-void CXBMCApp::Deinitialize()
+void CXBMCApp::Deinitialize(int status)
{
while(!m_texturePool.empty())
{
@@ -402,6 +396,16 @@ void CXBMCApp::Deinitialize()
glDeleteTextures(1, &texture_id);
m_texturePool.pop_back();
}
+
+ // Pass the return code to Java
+ set_field(m_object, "mExitCode", status);
+
+ // If we are have not been force by Android to exit, notify its finish routine.
+ // This will cause android to run through its teardown events, it calls:
+ // onPause(), onLostFocus(), onDestroyWindow(), onStop(), onDestroy().
+ ANativeActivity_finish(m_activity);
+ m_exiting=true;
+
}
bool CXBMCApp::EnableWakeLock(bool on)
@@ -455,7 +459,7 @@ bool CXBMCApp::AcquireAudioFocus()
}
// Request audio focus for playback
- int result = audioManager.requestAudioFocus(m_audioFocusListener,
+ int result = audioManager.requestAudioFocus(*m_audioFocusListener,
// Use the music stream.
CJNIAudioManager::STREAM_MUSIC,
// Request permanent focus.
@@ -486,7 +490,7 @@ bool CXBMCApp::ReleaseAudioFocus()
}
// Release audio focus after playback
- int result = audioManager.abandonAudioFocus(m_audioFocusListener);
+ int result = audioManager.abandonAudioFocus(*m_audioFocusListener);
if (result != CJNIAudioManager::AUDIOFOCUS_REQUEST_GRANTED)
{
CXBMCApp::android_printf("Audio Focus abandon failed");
@@ -536,48 +540,6 @@ bool CXBMCApp::IsHDMIPlugged()
return m_hdmiPlugged;
}
-void CXBMCApp::run()
-{
- int status = 0;
-
- SetupEnv();
- XBMC::Context context;
-
- m_firstrun=false;
- android_printf(" => running XBMC_Run...");
- try
- {
- CFileItemList dummyPL;
- status = XBMC_Run(true, dummyPL);
- android_printf(" => XBMC_Run finished with %d", status);
- }
- catch(...)
- {
- android_printf("ERROR: Exception caught on main loop. Exiting");
- }
-
- // Pass the return code to Java
- set_field(m_context, "mExitCode", status);
-
- // If we are have not been force by Android to exit, notify its finish routine.
- // This will cause android to run through its teardown events, it calls:
- // onPause(), onLostFocus(), onDestroyWindow(), onStop(), onDestroy().
- ANativeActivity_finish(m_activity);
- m_exiting=true;
-}
-
-void CXBMCApp::XBMC_SetupDisplay()
-{
- android_printf("XBMC_SetupDisplay()");
- CApplicationMessenger::GetInstance().PostMsg(TMSG_DISPLAY_SETUP);
-}
-
-void CXBMCApp::XBMC_DestroyDisplay()
-{
- android_printf("XBMC_DestroyDisplay()");
- CApplicationMessenger::GetInstance().PostMsg(TMSG_DISPLAY_DESTROY);
-}
-
int CXBMCApp::SetBuffersGeometry(int width, int height)
{
return ANativeWindow_setBuffersGeometry(m_window, width, height, WINDOW_FORMAT_RGBA_8888);
@@ -658,6 +620,12 @@ void CXBMCApp::BringToFront()
}
}
+void CXBMCApp::Minimize()
+{
+ CApplicationMessenger::GetInstance().PostMsg(TMSG_DISPLAY_DESTROY);
+ moveTaskToBack(true);
+}
+
GLuint CXBMCApp::pullTexture()
{
if (m_texturePool.empty())
@@ -689,21 +657,21 @@ int CXBMCApp::GetDPI()
CRect CXBMCApp::GetSurfaceRect()
{
- CSingleLock lock(m_AppMutex);
+ CSingleLock lock(m_LayoutMutex);
return m_surface_rect;
}
CRect CXBMCApp::MapRenderToDroid(const CRect& srcRect)
{
- CSingleLock lock(m_AppMutex);
+ CSingleLock lock(m_LayoutMutex);
return CRect(srcRect.x1 / m_droid2guiRatio.x2, srcRect.y1 / m_droid2guiRatio.y2, srcRect.x2 / m_droid2guiRatio.x2, srcRect.y2 / m_droid2guiRatio.y2);
}
CPoint CXBMCApp::MapDroidToGui(const CPoint& src)
{
- CSingleLock lock(m_AppMutex);
+ CSingleLock lock(m_LayoutMutex);
return CPoint((src.x - m_droid2guiRatio.x1) * m_droid2guiRatio.x2, (src.y - m_droid2guiRatio.y1) * m_droid2guiRatio.y2);
}
@@ -979,91 +947,6 @@ int CXBMCApp::GetBatteryLevel()
return m_batteryLevel;
}
-bool CXBMCApp::GetExternalStorage(std::string &path, const std::string &type /* = "" */)
-{
- std::string sType;
- std::string mountedState;
- bool mounted = false;
-
- if(type == "files" || type.empty())
- {
- CJNIFile external = CJNIEnvironment::getExternalStorageDirectory();
- if (external)
- path = external.getAbsolutePath();
- }
- else
- {
- if (type == "music")
- sType = "Music"; // Environment.DIRECTORY_MUSIC
- else if (type == "videos")
- sType = "Movies"; // Environment.DIRECTORY_MOVIES
- else if (type == "pictures")
- sType = "Pictures"; // Environment.DIRECTORY_PICTURES
- else if (type == "photos")
- sType = "DCIM"; // Environment.DIRECTORY_DCIM
- else if (type == "downloads")
- sType = "Download"; // Environment.DIRECTORY_DOWNLOADS
- if (!sType.empty())
- {
- CJNIFile external = CJNIEnvironment::getExternalStoragePublicDirectory(sType);
- if (external)
- path = external.getAbsolutePath();
- }
- }
- mountedState = CJNIEnvironment::getExternalStorageState();
- mounted = (mountedState == "mounted" || mountedState == "mounted_ro");
- return mounted && !path.empty();
-}
-
-bool CXBMCApp::GetStorageUsage(const std::string &path, std::string &usage)
-{
-#define PATH_MAXLEN 50
-
- if (path.empty())
- {
- std::ostringstream fmt;
- fmt.width(PATH_MAXLEN); fmt << std::left << "Filesystem";
- fmt.width(12); fmt << std::right << "Size";
- fmt.width(12); fmt << "Used";
- fmt.width(12); fmt << "Avail";
- fmt.width(12); fmt << "Use %";
-
- usage = fmt.str();
- return false;
- }
-
- CJNIStatFs fileStat(path);
- if (!fileStat)
- {
- CLog::Log(LOGERROR, "CXBMCApp::GetStorageUsage cannot stat %s", path.c_str());
- return false;
- }
- int blockSize = fileStat.getBlockSize();
- int blockCount = fileStat.getBlockCount();
- int freeBlocks = fileStat.getFreeBlocks();
-
- if (blockSize <= 0 || blockCount <= 0 || freeBlocks < 0)
- return false;
-
- float totalSize = (float)blockSize * blockCount / GIGABYTES;
- float freeSize = (float)blockSize * freeBlocks / GIGABYTES;
- float usedSize = totalSize - freeSize;
- float usedPercentage = usedSize / totalSize * 100;
-
- std::ostringstream fmt;
- fmt << std::fixed;
- fmt.precision(1);
- fmt.width(PATH_MAXLEN); fmt << std::left << (path.size() < PATH_MAXLEN-1 ? path : StringUtils::Left(path, PATH_MAXLEN-4) + "...");
- fmt.width(12); fmt << std::right << totalSize << "G"; // size in GB
- fmt.width(12); fmt << usedSize << "G"; // used in GB
- fmt.width(12); fmt << freeSize << "G"; // free
- fmt.precision(0);
- fmt.width(12); fmt << usedPercentage << "%"; // percentage used
-
- usage = fmt.str();
- return true;
-}
-
// Used in Application.cpp to figure out volume steps
int CXBMCApp::GetMaxSystemVolume()
{
@@ -1108,10 +991,6 @@ void CXBMCApp::SetSystemVolume(float percent)
android_printf("CXBMCApp::SetSystemVolume: Could not get Audio Manager");
}
-void CXBMCApp::InitDirectories()
-{
- CSpecialProtocol::SetXBMCBinAddonPath(getApplicationInfo().nativeLibraryDir.c_str());
-}
void CXBMCApp::onReceive(CJNIIntent intent)
{
@@ -1419,53 +1298,6 @@ bool CXBMCApp::WaitVSync(unsigned int milliSeconds)
return m_vsyncEvent.WaitMSec(milliSeconds);
}
-void CXBMCApp::SetupEnv()
-{
- setenv("XBMC_ANDROID_SYSTEM_LIBS", CJNISystem::getProperty("java.library.path").c_str(), 0);
- setenv("XBMC_ANDROID_LIBS", getApplicationInfo().nativeLibraryDir.c_str(), 0);
- setenv("XBMC_ANDROID_APK", getPackageResourcePath().c_str(), 0);
-
- std::string appName = CCompileInfo::GetAppName();
- StringUtils::ToLower(appName);
- std::string className = CCompileInfo::GetPackage();
-
- std::string xbmcHome = CJNISystem::getProperty("xbmc.home", "");
- if (xbmcHome.empty())
- {
- std::string cacheDir = getCacheDir().getAbsolutePath();
- setenv("KODI_BIN_HOME", (cacheDir + "/apk/assets").c_str(), 0);
- setenv("KODI_HOME", (cacheDir + "/apk/assets").c_str(), 0);
- }
- else
- {
- setenv("KODI_BIN_HOME", (xbmcHome + "/assets").c_str(), 0);
- setenv("KODI_HOME", (xbmcHome + "/assets").c_str(), 0);
- }
-
- std::string externalDir = CJNISystem::getProperty("xbmc.data", "");
- if (externalDir.empty())
- {
- CJNIFile androidPath = getExternalFilesDir("");
- if (!androidPath)
- androidPath = getDir(className.c_str(), 1);
-
- if (androidPath)
- externalDir = androidPath.getAbsolutePath();
- }
-
- if (!externalDir.empty())
- setenv("HOME", externalDir.c_str(), 0);
- else
- setenv("HOME", getenv("KODI_TEMP"), 0);
-
- std::string apkPath = getenv("XBMC_ANDROID_APK");
- apkPath += "/assets/python2.7";
- setenv("PYTHONHOME", apkPath.c_str(), 1);
- setenv("PYTHONPATH", "", 1);
- setenv("PYTHONOPTIMIZE","", 1);
- setenv("PYTHONNOUSERSITE", "1", 1);
-}
-
std::string CXBMCApp::GetFilenameFromIntent(const CJNIIntent &intent)
{
std::string ret;
@@ -1595,28 +1427,30 @@ void CXBMCApp::surfaceChanged(CJNISurfaceHolder holder, int format, int width, i
void CXBMCApp::surfaceCreated(CJNISurfaceHolder holder)
{
+ CLog::Log(LOGDEBUG, "%s", __PRETTY_FUNCTION__);
m_window = ANativeWindow_fromSurface(xbmc_jnienv(), holder.getSurface().get_raw());
if (m_window == NULL)
{
android_printf(" => invalid ANativeWindow object");
return;
}
- XBMC_SetupDisplay();
+ CApplicationMessenger::GetInstance().PostMsg(TMSG_DISPLAY_SETUP);
}
void CXBMCApp::surfaceDestroyed(CJNISurfaceHolder holder)
{
+ CLog::Log(LOGDEBUG, "%s", __PRETTY_FUNCTION__);
// If we have exited XBMC, it no longer exists.
if (!m_exiting)
{
- XBMC_DestroyDisplay();
+ CApplicationMessenger::GetInstance().PostMsg(TMSG_DISPLAY_CLEANUP);
m_window = NULL;
}
}
void CXBMCApp::onLayoutChange(int left, int top, int width, int height)
{
- CSingleLock lock(m_AppMutex);
+ CSingleLock lock(m_LayoutMutex);
m_surface_rect.x1 = left;
m_surface_rect.y1 = top;
@@ -1628,3 +1462,4 @@ void CXBMCApp::onLayoutChange(int left, int top, int width, int height)
if (g_application.GetRenderGUI())
CalculateGUIRatios();
}
+
diff --git a/xbmc/platform/android/activity/XBMCApp.h b/xbmc/platform/android/activity/XBMCApp.h
index bd93bd4ebf..c2984a8a31 100644
--- a/xbmc/platform/android/activity/XBMCApp.h
+++ b/xbmc/platform/android/activity/XBMCApp.h
@@ -168,12 +168,13 @@ class CXBMCApp
void onLostFocus();
void Initialize();
- void Deinitialize();
+ void Deinitialize(int status);
static const ANativeWindow** GetNativeWindow(int timeout);
static int SetBuffersGeometry(int width, int height);
static int android_printf(const char *format, ...);
void BringToFront();
+ void Minimize();
static GLuint pullTexture();
static void pushTexture(GLuint tex);
@@ -195,12 +196,9 @@ class CXBMCApp
* \param type optional type. Possible values are "", "files", "music", "videos", "pictures", "photos, "downloads"
* \return true if external storage is available and a valid path has been stored in the path parameter
*/
- static bool GetExternalStorage(std::string &path, const std::string &type = "");
- static bool GetStorageUsage(const std::string &path, std::string &usage);
int GetMaxSystemVolume();
float GetSystemVolume();
void SetSystemVolume(float percent);
- void InitDirectories();
void SetRefreshRate(float rate);
void SetDisplayMode(int mode);
@@ -260,9 +258,9 @@ class CXBMCApp
private:
static CXBMCApp* m_xbmcappinstance;
- static CCriticalSection m_AppMutex;
+ static CCriticalSection m_LayoutMutex;
- CJNIXBMCAudioManagerOnAudioFocusChangeListener m_audioFocusListener;
+ std::unique_ptr m_audioFocusListener;
std::unique_ptr m_broadcastReceiver;
static std::unique_ptr m_mainView;
std::unique_ptr m_mediaSession;
@@ -284,9 +282,8 @@ class CXBMCApp
static bool m_hasReqVisible;
static bool m_hasPIP;
bool m_videosurfaceInUse;
- bool m_firstrun;
+ bool m_firstActivityRun;
bool m_exiting;
- pthread_t m_thread;
static CCriticalSection m_applicationsMutex;
static std::vector m_applications;
static std::vector m_activityResultEvents;
@@ -301,8 +298,6 @@ class CXBMCApp
static uint64_t m_vsynctime;
static CEvent m_vsyncEvent;
- void XBMC_DestroyDisplay();
- void XBMC_SetupDisplay();
static void CalculateGUIRatios();
static CRect m_droid2guiRatio;
diff --git a/xbmc/platform/android/android_onload.cpp b/xbmc/platform/android/android_onload.cpp
index 3dccd755c7..110a3daa0a 100644
--- a/xbmc/platform/android/android_onload.cpp
+++ b/xbmc/platform/android/android_onload.cpp
@@ -49,7 +49,7 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved)
std::string pkgRoot = CCompileInfo::GetClass();
- std::string keepAliveService = pkgRoot + "/KeepAliveService";
+ std::string serviceClass = pkgRoot + "/Service";
std::string mainClass = pkgRoot + "/Main";
std::string bcReceiver = pkgRoot + "/XBMCBroadcastReceiver";
std::string settingsObserver = pkgRoot + "/XBMCSettingsContentObserver";
@@ -67,12 +67,12 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved)
jni::CJNIXBMCInputDeviceListener::RegisterNatives(env);
jni::CJNIXBMCBroadcastReceiver::RegisterNatives(env);
- jclass cKASvc = env->FindClass(keepAliveService.c_str());
+ jclass cKASvc = env->FindClass(serviceClass.c_str());
if(cKASvc)
{
JNINativeMethod methods[] =
{
- {"_launchApplication", "()V", (void*)&CXBMCService::_launchApplication},
+ {"_launchApplication", "()Z", (void*)&CXBMCService::_launchApplication},
};
env->RegisterNatives(cKASvc, methods, sizeof(methods)/sizeof(methods[0]));
}
diff --git a/xbmc/platform/android/service/CMakeLists.txt b/xbmc/platform/android/service/CMakeLists.txt
new file mode 100644
index 0000000000..e031428adf
--- /dev/null
+++ b/xbmc/platform/android/service/CMakeLists.txt
@@ -0,0 +1,9 @@
+set(SOURCES
+ XBMCService.cpp
+ )
+
+set(HEADERS
+ XBMCService.h
+ )
+
+core_add_library(platform_android_service)
diff --git a/xbmc/platform/android/service/XBMCService.cpp b/xbmc/platform/android/service/XBMCService.cpp
new file mode 100644
index 0000000000..b2264566b4
--- /dev/null
+++ b/xbmc/platform/android/service/XBMCService.cpp
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2018 Christian Browet
+ * http://kodi.tv
+ *
+ * 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
+ * .
+ *
+ */
+
+#include "XBMCService.h"
+
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "Application.h"
+#include "CompileInfo.h"
+#include "FileItem.h"
+#include "utils/StringUtils.h"
+#include "platform/XbmcContext.h"
+#include "platform/xbmc.h"
+#include "filesystem/SpecialProtocol.h"
+#include "utils/log.h"
+
+#define GIGABYTES 1073741824
+
+CCriticalSection CXBMCService::m_SvcMutex;
+bool CXBMCService::m_SvcThreadCreated = false;
+pthread_t CXBMCService::m_SvcThread;
+CXBMCService* CXBMCService::m_xbmcserviceinstance = nullptr;
+
+template
+void* thread_run(void* obj)
+{
+ (static_cast(obj)->*fn)();
+ return NULL;
+}
+
+using namespace jni;
+
+CXBMCService::CXBMCService(jobject thiz)
+ : CJNIBase()
+ , CJNIService(thiz)
+
+{
+ m_xbmcserviceinstance = this;
+}
+
+int CXBMCService::android_printf(const char *format, ...)
+{
+ // For use before CLog is setup by XBMC_Run()
+ va_list args;
+ va_start(args, format);
+ int result = __android_log_vprint(ANDROID_LOG_DEBUG, CCompileInfo::GetAppName(), format, args);
+ va_end(args);
+ return result;
+}
+
+void CXBMCService::SetupEnv()
+{
+ setenv("XBMC_ANDROID_SYSTEM_LIBS", CJNISystem::getProperty("java.library.path").c_str(), 0);
+ setenv("XBMC_ANDROID_LIBS", getApplicationInfo().nativeLibraryDir.c_str(), 0);
+ setenv("XBMC_ANDROID_APK", getPackageResourcePath().c_str(), 0);
+
+ std::string appName = CCompileInfo::GetAppName();
+ StringUtils::ToLower(appName);
+ std::string className = CCompileInfo::GetPackage();
+
+ std::string xbmcHome = CJNISystem::getProperty("xbmc.home", "");
+ if (xbmcHome.empty())
+ {
+ std::string cacheDir = getCacheDir().getAbsolutePath();
+ setenv("KODI_BIN_HOME", (cacheDir + "/apk/assets").c_str(), 0);
+ setenv("KODI_HOME", (cacheDir + "/apk/assets").c_str(), 0);
+ }
+ else
+ {
+ setenv("KODI_BIN_HOME", (xbmcHome + "/assets").c_str(), 0);
+ setenv("KODI_HOME", (xbmcHome + "/assets").c_str(), 0);
+ }
+
+ std::string externalDir = CJNISystem::getProperty("xbmc.data", "");
+ if (externalDir.empty())
+ {
+ CJNIFile androidPath = getExternalFilesDir("");
+ if (!androidPath)
+ androidPath = getDir(className.c_str(), 1);
+
+ if (androidPath)
+ externalDir = androidPath.getAbsolutePath();
+ }
+
+ if (!externalDir.empty())
+ setenv("HOME", externalDir.c_str(), 0);
+ else
+ setenv("HOME", getenv("KODI_TEMP"), 0);
+
+ std::string apkPath = getenv("XBMC_ANDROID_APK");
+ apkPath += "/assets/python2.7";
+ setenv("PYTHONHOME", apkPath.c_str(), 1);
+ setenv("PYTHONPATH", "", 1);
+ setenv("PYTHONOPTIMIZE","", 1);
+ setenv("PYTHONNOUSERSITE", "1", 1);
+}
+
+void CXBMCService::run()
+{
+ int status = 0;
+
+ SetupEnv();
+ XBMC::Context context;
+
+ android_printf(" => running XBMC_Run...");
+ try
+ {
+ CFileItemList dummyPL;
+ status = XBMC_Run(false, dummyPL);
+ android_printf(" => XBMC_Run finished with %d", status);
+ }
+ catch(...)
+ {
+ android_printf("ERROR: Exception caught on main loop. Exiting");
+ }
+}
+
+void CXBMCService::InitDirectories()
+{
+ CSpecialProtocol::SetXBMCBinAddonPath(getApplicationInfo().nativeLibraryDir.c_str());
+}
+
+void CXBMCService::Deinitialize()
+{
+ stopSelf();
+}
+
+bool CXBMCService::GetExternalStorage(std::string &path, const std::string &type /* = "" */)
+{
+ std::string sType;
+ std::string mountedState;
+ bool mounted = false;
+
+ if(type == "files" || type.empty())
+ {
+ CJNIFile external = CJNIEnvironment::getExternalStorageDirectory();
+ if (external)
+ path = external.getAbsolutePath();
+ }
+ else
+ {
+ if (type == "music")
+ sType = "Music"; // Environment.DIRECTORY_MUSIC
+ else if (type == "videos")
+ sType = "Movies"; // Environment.DIRECTORY_MOVIES
+ else if (type == "pictures")
+ sType = "Pictures"; // Environment.DIRECTORY_PICTURES
+ else if (type == "photos")
+ sType = "DCIM"; // Environment.DIRECTORY_DCIM
+ else if (type == "downloads")
+ sType = "Download"; // Environment.DIRECTORY_DOWNLOADS
+ if (!sType.empty())
+ {
+ CJNIFile external = CJNIEnvironment::getExternalStoragePublicDirectory(sType);
+ if (external)
+ path = external.getAbsolutePath();
+ }
+ }
+ mountedState = CJNIEnvironment::getExternalStorageState();
+ mounted = (mountedState == "mounted" || mountedState == "mounted_ro");
+ return mounted && !path.empty();
+}
+
+bool CXBMCService::GetStorageUsage(const std::string &path, std::string &usage)
+{
+#define PATH_MAXLEN 50
+
+ if (path.empty())
+ {
+ std::ostringstream fmt;
+ fmt.width(PATH_MAXLEN); fmt << std::left << "Filesystem";
+ fmt.width(12); fmt << std::right << "Size";
+ fmt.width(12); fmt << "Used";
+ fmt.width(12); fmt << "Avail";
+ fmt.width(12); fmt << "Use %";
+
+ usage = fmt.str();
+ return false;
+ }
+
+ CJNIStatFs fileStat(path);
+ if (!fileStat)
+ {
+ CLog::Log(LOGERROR, "CXBMCApp::GetStorageUsage cannot stat %s", path.c_str());
+ return false;
+ }
+ int blockSize = fileStat.getBlockSize();
+ int blockCount = fileStat.getBlockCount();
+ int freeBlocks = fileStat.getFreeBlocks();
+
+ if (blockSize <= 0 || blockCount <= 0 || freeBlocks < 0)
+ return false;
+
+ float totalSize = (float)blockSize * blockCount / GIGABYTES;
+ float freeSize = (float)blockSize * freeBlocks / GIGABYTES;
+ float usedSize = totalSize - freeSize;
+ float usedPercentage = usedSize / totalSize * 100;
+
+ std::ostringstream fmt;
+ fmt << std::fixed;
+ fmt.precision(1);
+ fmt.width(PATH_MAXLEN); fmt << std::left << (path.size() < PATH_MAXLEN-1 ? path : StringUtils::Left(path, PATH_MAXLEN-4) + "...");
+ fmt.width(12); fmt << std::right << totalSize << "G"; // size in GB
+ fmt.width(12); fmt << usedSize << "G"; // used in GB
+ fmt.width(12); fmt << freeSize << "G"; // free
+ fmt.precision(0);
+ fmt.width(12); fmt << usedPercentage << "%"; // percentage used
+
+ usage = fmt.str();
+ return true;
+}
+
+void CXBMCService::StartApplication()
+{
+ CSingleLock lock(m_SvcMutex);
+
+ if( !m_SvcThreadCreated)
+ {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+ pthread_create(&m_SvcThread, &attr, thread_run, this);
+ pthread_attr_destroy(&attr);
+
+ m_SvcThreadCreated = true;
+ }
+ // Wait for the service to settle
+ int nb = 0;
+ while(!g_application.IsInitialized() && nb < 30)
+ {
+ usleep(1 * 1000000);
+ nb++;
+ }
+}
+
+void CXBMCService::StopApplication()
+{
+ pthread_join(m_SvcThread, NULL);
+}
+
+jboolean CXBMCService::_launchApplication(JNIEnv*, jobject thiz)
+{
+ jobject o = (jobject)xbmc_jnienv()->NewGlobalRef(thiz);
+ m_xbmcserviceinstance = new CXBMCService(o);
+ m_xbmcserviceinstance->StartApplication();
+ return g_application.IsInitialized();
+}
diff --git a/xbmc/platform/android/service/XBMCService.h b/xbmc/platform/android/service/XBMCService.h
new file mode 100644
index 0000000000..758c910afe
--- /dev/null
+++ b/xbmc/platform/android/service/XBMCService.h
@@ -0,0 +1,62 @@
+#pragma once
+/*
+ * Copyright (C) 2018 Christian Browet
+ * http://kodi.tv
+ *
+ * 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
+ * .
+ *
+ */
+
+#include
+
+#include
+#include
+
+#include "threads/Event.h"
+#include "threads/SharedSection.h"
+
+class CXBMCService
+ : public CJNIService
+{
+ friend class XBMCApp;
+
+public:
+ CXBMCService(jobject thiz);
+
+ static CXBMCService* get() { return m_xbmcserviceinstance; }
+ static jboolean _launchApplication(JNIEnv*, jobject thiz);
+ int android_printf(const char* format...);
+
+ void InitDirectories();
+ void Deinitialize();
+
+ static bool GetExternalStorage(std::string &path, const std::string &type = "");
+ static bool GetStorageUsage(const std::string &path, std::string &usage);
+
+protected:
+ void run();
+ void SetupEnv();
+
+ CEvent m_appReady;
+
+private:
+ static CCriticalSection m_SvcMutex;
+ static bool m_SvcThreadCreated;
+ static pthread_t m_SvcThread;
+ static CXBMCService* m_xbmcserviceinstance;
+
+ void StartApplication();
+ void StopApplication();
+};
diff --git a/xbmc/platform/xbmc.cpp b/xbmc/platform/xbmc.cpp
index 410f4b90d1..18261ce143 100644
--- a/xbmc/platform/xbmc.cpp
+++ b/xbmc/platform/xbmc.cpp
@@ -32,6 +32,7 @@
#if defined(TARGET_ANDROID)
#include "platform/android/activity/XBMCApp.h"
+#include "platform/android/service/XBMCService.h"
#endif
#include "platform/MessagePrinter.h"
@@ -63,10 +64,7 @@ extern "C" int XBMC_Run(bool renderGUI, CFileItemList &playlist)
if(!g_RBP.Initialize())
return false;
g_RBP.LogFirmwareVerison();
-#elif defined(TARGET_ANDROID)
- CXBMCApp::get()->Initialize();
#endif
-
if (renderGUI && !g_application.CreateGUI())
{
CMessagePrinter::DisplayError("ERROR: Unable to create GUI. Exiting");
@@ -90,11 +88,6 @@ extern "C" int XBMC_Run(bool renderGUI, CFileItemList &playlist)
}
#endif
-#if defined(TARGET_ANDROID)
- if (g_advancedSettings.m_videoUseDroidProjectionCapture)
- CXBMCApp::get()->startProjection();
-#endif
-
try
{
status = g_application.Run(playlist);
@@ -127,7 +120,9 @@ extern "C" int XBMC_Run(bool renderGUI, CFileItemList &playlist)
#ifdef TARGET_RASPBERRY_PI
g_RBP.Deinitialize();
#elif defined(TARGET_ANDROID)
- CXBMCApp::get()->Deinitialize();
+ if (CXBMCApp::get())
+ CXBMCApp::get()->Deinitialize(status);
+ CXBMCService::get()->Deinitialize();
#endif
return status;
diff --git a/xbmc/storage/android/AndroidStorageProvider.cpp b/xbmc/storage/android/AndroidStorageProvider.cpp
index 9a7877baec..d67c013dc0 100644
--- a/xbmc/storage/android/AndroidStorageProvider.cpp
+++ b/xbmc/storage/android/AndroidStorageProvider.cpp
@@ -31,7 +31,7 @@
#include "filesystem/Directory.h"
#include "filesystem/File.h"
#include "guilib/LocalizeStrings.h"
-#include "platform/android/activity/XBMCApp.h"
+#include "platform/android/service/XBMCService.h"
#include "Util.h"
#include "utils/log.h"
@@ -107,7 +107,7 @@ void CAndroidStorageProvider::GetLocalDrives(VECSOURCES &localDrives)
// external directory
std::string path;
- if (CXBMCApp::GetExternalStorage(path) && !path.empty() && XFILE::CDirectory::Exists(path))
+ if (CXBMCService::GetExternalStorage(path) && !path.empty() && XFILE::CDirectory::Exists(path))
{
share.strPath = path;
share.strName = g_localizeStrings.Get(21456);
@@ -127,7 +127,7 @@ void CAndroidStorageProvider::GetRemovableDrives(VECSOURCES &removableDrives)
bool inError = false;
VECSOURCES droidDrives;
- CJNIStorageManager manager(CXBMCApp::get()->getSystemService("storage"));
+ CJNIStorageManager manager(CXBMCService::get()->getSystemService("storage"));
if (xbmc_jnienv()->ExceptionCheck())
{
xbmc_jnienv()->ExceptionClear();
@@ -321,19 +321,19 @@ std::vector CAndroidStorageProvider::GetDiskUsage()
std::string usage;
// add header
- CXBMCApp::GetStorageUsage("", usage);
+ CXBMCService::GetStorageUsage("", usage);
result.push_back(usage);
usage.clear();
// add rootfs
- if (CXBMCApp::GetStorageUsage("/", usage) && !usage.empty())
+ if (CXBMCService::GetStorageUsage("/", usage) && !usage.empty())
result.push_back(usage);
usage.clear();
// add external storage if available
std::string path;
- if (CXBMCApp::GetExternalStorage(path) && !path.empty() &&
- CXBMCApp::GetStorageUsage(path, usage) && !usage.empty())
+ if (CXBMCService::GetExternalStorage(path) && !path.empty() &&
+ CXBMCService::GetStorageUsage(path, usage) && !usage.empty())
result.push_back(usage);
// add removable storage
@@ -342,7 +342,7 @@ std::vector CAndroidStorageProvider::GetDiskUsage()
for (unsigned int i = 0; i < drives.size(); i++)
{
usage.clear();
- if (CXBMCApp::GetStorageUsage(drives[i].strPath, usage) && !usage.empty())
+ if (CXBMCService::GetStorageUsage(drives[i].strPath, usage) && !usage.empty())
result.push_back(usage);
}
diff --git a/xbmc/utils/Splash.cpp b/xbmc/utils/Splash.cpp
index e66428494b..1f7483b12c 100644
--- a/xbmc/utils/Splash.cpp
+++ b/xbmc/utils/Splash.cpp
@@ -20,6 +20,7 @@
#include "system.h"
#include "Splash.h"
+#include "Application.h"
#include "guilib/GUIImage.h"
#include "guilib/GUILabelControl.h"
#include "guilib/GUIFontManager.h"
@@ -41,6 +42,9 @@ CSplash& CSplash::GetInstance()
void CSplash::Show(const std::string& message /* = "" */)
{
+ if (!g_application.GetRenderGUI())
+ return;
+
if (!g_advancedSettings.m_splashImage && !(m_image || !message.empty()))
return;
diff --git a/xbmc/windowing/egl/WinSystemEGL.cpp b/xbmc/windowing/egl/WinSystemEGL.cpp
index ace1501df8..38ab0ec3a2 100644
--- a/xbmc/windowing/egl/WinSystemEGL.cpp
+++ b/xbmc/windowing/egl/WinSystemEGL.cpp
@@ -505,7 +505,7 @@ void CWinSystemEGL::NotifyAppActiveChange(bool bActivated)
bool CWinSystemEGL::Minimize()
{
#ifdef TARGET_ANDROID
- CXBMCApp::get()->moveTaskToBack(true);
+ CXBMCApp::get()->Minimize();
#else
Hide();
#endif