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