diff --git a/XBMC.files b/XBMC.files
index fbf8398b5a..d1133bf282 100644
--- a/XBMC.files
+++ b/XBMC.files
@@ -9889,3 +9889,7 @@ lib/libmpeg2/Makefile.am
lib/libmpeg2/configure.ac
lib/libUPnP/Makefile.in
lib/UnrarXLib/Makefile
+xbmc/filesystem/StorageDirectory.h
+xbmc/filesystem/StorageDirectory.cpp
+xbmc/filesystem/StorageFile.h
+xbmc/filesystem/StorageFile.cpp
diff --git a/tools/android/packaging/xbmc/AndroidManifest.xml.in b/tools/android/packaging/xbmc/AndroidManifest.xml.in
index ca75f62ab6..25e5d13df0 100644
--- a/tools/android/packaging/xbmc/AndroidManifest.xml.in
+++ b/tools/android/packaging/xbmc/AndroidManifest.xml.in
@@ -24,11 +24,11 @@
-
+
+ android:logo="@drawable/banner"
+ android:label="@string/app_name"
+ android:hasCode="true">
+
+
+
@@ -112,15 +115,15 @@
android:name=".XBMCMediaContentProvider"
android:authorities="@APP_PACKAGE@.media"
android:exported="true" />
-
+
-
-
-
-
+
+
+
-
-
+
+
diff --git a/xbmc/android/activity/XBMCApp.cpp b/xbmc/android/activity/XBMCApp.cpp
index 79899bcf32..703ed11341 100644
--- a/xbmc/android/activity/XBMCApp.cpp
+++ b/xbmc/android/activity/XBMCApp.cpp
@@ -84,6 +84,8 @@
#include "android/jni/SystemProperties.h"
#include "android/jni/Display.h"
#include "android/jni/View.h"
+#include "android/jni/StorageManager.h"
+
#include "AndroidKey.h"
#include "CompileInfo.h"
@@ -114,6 +116,7 @@ bool CXBMCApp::m_hasFocus = false;
bool CXBMCApp::m_isResumed = false;
bool CXBMCApp::m_hasAudioFocus = false;
bool CXBMCApp::m_headsetPlugged = false;
+CCriticalSection CXBMCApp::m_apiMutex;
CCriticalSection CXBMCApp::m_applicationsMutex;
std::vector CXBMCApp::m_applications;
std::vector CXBMCApp::m_activityResultEvents;
@@ -121,6 +124,7 @@ uint64_t CXBMCApp::m_vsynctime = 0;
CEvent CXBMCApp::m_vsyncEvent;
std::vector CXBMCApp::m_texturePool;
CJNIAudioDeviceInfos CXBMCApp::m_audiodevices;
+VECSOURCES CXBMCApp::m_removableDrives;
void LogAudoDevices(const char* stage, const CJNIAudioDeviceInfos& devices)
{
@@ -932,6 +936,97 @@ bool CXBMCApp::GetStorageUsage(const std::string &path, std::string &usage)
return true;
}
+void CXBMCApp::InvalidateRemovableDrives()
+{
+ CSingleLock lock(m_apiMutex);
+ m_removableDrives.clear();
+}
+
+bool CXBMCApp::GetRemovableDrives(VECSOURCES& removableDrives)
+{
+ CSingleLock lock(m_apiMutex);
+
+ // Uses non-public API: be extra carefull
+ bool inError = false;
+
+ if (m_removableDrives.empty())
+ {
+ CJNIStorageManager manager(CJNIContext::getSystemService("storage"));
+ if (xbmc_jnienv()->ExceptionCheck())
+ {
+ xbmc_jnienv()->ExceptionClear();
+ inError = true;
+ }
+
+ if (!inError)
+ {
+ CJNIStorageVolumes vols = manager.getVolumeList();
+ if (xbmc_jnienv()->ExceptionCheck())
+ {
+ xbmc_jnienv()->ExceptionClear();
+ inError = true;
+ }
+
+ if (!inError)
+ {
+ for (auto vol : vols)
+ {
+ // CLog::Log(LOGDEBUG, "-- Volume: %s(%s) -- %s", vol.getPath().c_str(), vol.getUserLabel().c_str(), vol.getState().c_str());
+ bool removable = vol.isRemovable();
+ if (xbmc_jnienv()->ExceptionCheck())
+ {
+ xbmc_jnienv()->ExceptionClear();
+ inError = true;
+ break;
+ }
+ std::string state = vol.getState();
+ if (xbmc_jnienv()->ExceptionCheck())
+ {
+ xbmc_jnienv()->ExceptionClear();
+ inError = true;
+ break;
+ }
+
+ if (removable && state == CJNIEnvironment::MEDIA_MOUNTED)
+ {
+ CMediaSource share;
+ share.strPath = vol.getPath();
+ if (xbmc_jnienv()->ExceptionCheck())
+ {
+ xbmc_jnienv()->ExceptionClear();
+ inError = true;
+ break;
+ }
+ share.strDiskUniqueId = vol.getUuid();
+ if (xbmc_jnienv()->ExceptionCheck())
+ {
+ xbmc_jnienv()->ExceptionClear();
+ inError = true;
+ break;
+ }
+ share.strName = vol.getUserLabel();
+ if (xbmc_jnienv()->ExceptionCheck())
+ {
+ xbmc_jnienv()->ExceptionClear();
+ inError = true;
+ break;
+ }
+ StringUtils::Trim(share.strName);
+ if (share.strName.empty() || share.strName == "?" || StringUtils::CompareNoCase(share.strName, "null") == 0)
+ share.strName = URIUtils::GetFileName(share.strPath);
+ share.m_ignore = true;
+ m_removableDrives.push_back(share);
+ }
+ }
+ }
+ }
+ }
+ if (!inError)
+ removableDrives.insert(removableDrives.end(), m_removableDrives.begin(), m_removableDrives.end());
+
+ return !inError;
+}
+
// Used in Application.cpp to figure out volume steps
int CXBMCApp::GetMaxSystemVolume()
{
@@ -1073,6 +1168,10 @@ void CXBMCApp::onNewIntent(CJNIIntent intent)
CApplicationMessenger::GetInstance().PostMsg(TMSG_GUI_ACTIVATE_WINDOW, WINDOW_MUSIC_NAV, 0, nullptr, "", params);
}
}
+ else if (action == "android.intent.action.MEDIA_MOUNTED" || action == "android.intent.action.MEDIA_REMOVED")
+ {
+ InvalidateRemovableDrives();
+ }
}
void CXBMCApp::onActivityResult(int requestCode, int resultCode, CJNIIntent resultData)
diff --git a/xbmc/android/activity/XBMCApp.h b/xbmc/android/activity/XBMCApp.h
index dc6156ba5e..e8e82ab4ea 100644
--- a/xbmc/android/activity/XBMCApp.h
+++ b/xbmc/android/activity/XBMCApp.h
@@ -31,6 +31,7 @@
#include "IActivityHandler.h"
#include "IInputHandler.h"
+#include "storage/IStorageProvider.h"
#include "xbmc.h"
#include "android/jni/Activity.h"
@@ -153,6 +154,9 @@ class CXBMCApp
*/
static bool GetExternalStorage(std::string &path, const std::string &type = "");
static bool GetStorageUsage(const std::string &path, std::string &usage);
+ static void InvalidateRemovableDrives();
+ static bool GetRemovableDrives(VECSOURCES& removableDrives);
+
static int GetMaxSystemVolume();
static float GetSystemVolume();
static void SetSystemVolume(float percent);
@@ -208,10 +212,13 @@ class CXBMCApp
bool m_firstrun;
bool m_exiting;
pthread_t m_thread;
+ static CCriticalSection m_apiMutex;
static CCriticalSection m_applicationsMutex;
static std::vector m_applications;
static std::vector m_activityResultEvents;
+ static VECSOURCES m_removableDrives;
+
static ANativeWindow* m_window;
static CEvent m_windowCreated;
static std::vector m_texturePool;
diff --git a/xbmc/filesystem/DirectoryFactory.cpp b/xbmc/filesystem/DirectoryFactory.cpp
index 0bf1ce88cd..669eb126c7 100644
--- a/xbmc/filesystem/DirectoryFactory.cpp
+++ b/xbmc/filesystem/DirectoryFactory.cpp
@@ -42,6 +42,8 @@
#include "HTTPDirectory.h"
#include "DAVDirectory.h"
#include "UDFDirectory.h"
+#include "StorageDirectory.h"
+
#include "Application.h"
#include "utils/log.h"
#include "network/WakeOnAccess.h"
@@ -130,6 +132,7 @@ IDirectory* CDirectoryFactory::Create(const CURL& url)
#error Local directory access is not implemented for this platform
#endif
if (url.IsProtocol("special")) return new CSpecialProtocolDirectory();
+ if (url.IsProtocol("storage")) return new CStorageDirectory();
if (url.IsProtocol("sources")) return new CSourcesDirectory();
if (url.IsProtocol("addons")) return new CAddonsDirectory();
#if defined(HAS_FILESYSTEM_CDDA) && defined(HAS_DVD_DRIVE)
diff --git a/xbmc/filesystem/FileFactory.cpp b/xbmc/filesystem/FileFactory.cpp
index 820db0367f..02df0a7ce3 100644
--- a/xbmc/filesystem/FileFactory.cpp
+++ b/xbmc/filesystem/FileFactory.cpp
@@ -85,6 +85,8 @@
#include "UDFFile.h"
#include "ImageFile.h"
#include "ResourceFile.h"
+#include "StorageFile.h"
+
#include "Application.h"
#include "URL.h"
#include "utils/log.h"
@@ -127,13 +129,14 @@ IFile* CFileFactory::CreateLoader(const CURL& url)
else if (url.IsProtocol("musicdb")) return new CMusicDatabaseFile();
else if (url.IsProtocol("videodb")) return new CVideoDatabaseFile();
else if (url.IsProtocol("special")) return new CSpecialProtocolFile();
+ else if (url.IsProtocol("storage")) return new CStorageFile();
else if (url.IsProtocol("multipath")) return new CMultiPathFile();
else if (url.IsProtocol("image")) return new CImageFile();
#ifdef TARGET_POSIX
else if (url.IsProtocol("file") || url.GetProtocol().empty()) return new CPosixFile();
#elif defined(TARGET_WINDOWS)
else if (url.IsProtocol("file") || url.GetProtocol().empty()) return new CWin32File();
-#endif // TARGET_WINDOWS
+#endif // TARGET_WINDOWS
else if (url.IsProtocol("filereader")) return new CFileReaderFile();
#if defined(HAS_FILESYSTEM_CDDA) && defined(HAS_DVD_DRIVE)
else if (url.IsProtocol("cdda")) return new CFileCDDA();
diff --git a/xbmc/filesystem/Makefile.in b/xbmc/filesystem/Makefile.in
index f9811d304b..9459f37c7f 100644
--- a/xbmc/filesystem/Makefile.in
+++ b/xbmc/filesystem/Makefile.in
@@ -77,6 +77,8 @@ SRCS += ZeroconfDirectory.cpp
SRCS += ZipDirectory.cpp
SRCS += ZipFile.cpp
SRCS += ZipManager.cpp
+SRCS += StorageDirectory.cpp
+SRCS += StorageFile.cpp
ifeq (@USE_ANDROID@,1)
SRCS += APKDirectory.cpp
diff --git a/xbmc/filesystem/StorageDirectory.cpp b/xbmc/filesystem/StorageDirectory.cpp
new file mode 100644
index 0000000000..1e05acf264
--- /dev/null
+++ b/xbmc/filesystem/StorageDirectory.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 Christian Browet
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * .
+ *
+ */
+
+#include "StorageDirectory.h"
+
+#include "Directory.h"
+#include "FileItem.h"
+#include "URL.h"
+
+#include "utils/URIUtils.h"
+#include "utils/StringUtils.h"
+#include "utils/log.h"
+
+#ifdef TARGET_ANDROID
+#include "android/activity/XBMCApp.h"
+#endif
+
+using namespace XFILE;
+
+CStorageDirectory::CStorageDirectory()
+{
+}
+
+CStorageDirectory::~CStorageDirectory()
+{
+}
+
+bool CStorageDirectory::GetDirectory(const CURL& url, CFileItemList &items)
+{
+ const std::string pathToUrl(url.Get());
+ std::string translatedPath = TranslatePath(url);
+ if (CDirectory::GetDirectory(translatedPath, items, m_strFileMask, m_flags | DIR_FLAG_GET_HIDDEN))
+ { // replace our paths as necessary
+ items.SetURL(url);
+ for (int i = 0; i < items.Size(); i++)
+ {
+ CFileItemPtr item = items[i];
+ if (StringUtils::StartsWith(item->GetPath(), translatedPath))
+ item->SetPath(URIUtils::AddFileToFolder(pathToUrl, item->GetPath().substr(translatedPath.size())));
+ }
+ return true;
+ }
+ return false;
+}
+
+std::string CStorageDirectory::TranslatePath(const CURL &url)
+{
+ return TranslatePathImpl(url);
+}
+
+std::string CStorageDirectory::TranslatePathImpl(const CURL& url)
+{
+ std::string returl;
+ std::string uuid = url.GetHostName();
+
+#ifdef TARGET_ANDROID
+ VECSOURCES drives;
+ if (CXBMCApp::GetRemovableDrives(drives))
+ {
+ for (auto d : drives)
+ {
+ if (d.strDiskUniqueId == uuid)
+ {
+ returl = URIUtils::AddFileToFolder(d.strPath, url.GetFileName());
+// CLog::Log(LOGDEBUG, "CStorageDirectory: translated %s to %s", url.Get().c_str(), returl.c_str());
+ }
+ }
+ }
+#endif
+
+ return returl;
+}
+
diff --git a/xbmc/filesystem/StorageDirectory.h b/xbmc/filesystem/StorageDirectory.h
new file mode 100644
index 0000000000..30a55eb598
--- /dev/null
+++ b/xbmc/filesystem/StorageDirectory.h
@@ -0,0 +1,42 @@
+#pragma once
+/*
+ * Copyright (C) 2016 Christian Browet
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * .
+ *
+ */
+
+#include "filesystem/OverrideDirectory.h"
+
+namespace XFILE
+{
+
+class CStorageDirectory : public COverrideDirectory
+{
+ friend class CStorageFile;
+
+public:
+ CStorageDirectory();
+ virtual ~CStorageDirectory(void);
+
+ virtual bool GetDirectory(const CURL& url, CFileItemList& items) override;
+
+protected:
+ std::string TranslatePath(const CURL& url);
+ static std::string TranslatePathImpl(const CURL& url);
+};
+
+}
diff --git a/xbmc/filesystem/StorageFile.cpp b/xbmc/filesystem/StorageFile.cpp
new file mode 100644
index 0000000000..0baa485e54
--- /dev/null
+++ b/xbmc/filesystem/StorageFile.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 Christian Browet
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * .
+ *
+ */
+
+#include "StorageFile.h"
+#include "URL.h"
+
+#include "StorageDirectory.h"
+
+using namespace XFILE;
+
+CStorageFile::CStorageFile(void)
+ : COverrideFile(true)
+{ }
+
+CStorageFile::~CStorageFile(void)
+{ }
+
+std::string CStorageFile::TranslatePath(const CURL& url)
+{
+ return CStorageDirectory::TranslatePathImpl(url);
+}
diff --git a/xbmc/filesystem/StorageFile.h b/xbmc/filesystem/StorageFile.h
new file mode 100644
index 0000000000..49fd272d69
--- /dev/null
+++ b/xbmc/filesystem/StorageFile.h
@@ -0,0 +1,35 @@
+#pragma once
+/*
+ * Copyright (C) 2016 Christian Browet
+ * http://xbmc.org
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This Program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with XBMC; see the file COPYING. If not, see
+ * .
+ *
+ */
+
+#include "filesystem/OverrideFile.h"
+
+namespace XFILE
+{
+class CStorageFile : public COverrideFile
+{
+public:
+ CStorageFile(void);
+ virtual ~CStorageFile(void);
+
+protected:
+ virtual std::string TranslatePath(const CURL& url);
+};
+}
diff --git a/xbmc/storage/android/AndroidStorageProvider.cpp b/xbmc/storage/android/AndroidStorageProvider.cpp
index 3465d7bb67..34ee1a8efa 100644
--- a/xbmc/storage/android/AndroidStorageProvider.cpp
+++ b/xbmc/storage/android/AndroidStorageProvider.cpp
@@ -35,10 +35,6 @@
#include "utils/StringUtils.h"
#include "utils/URIUtils.h"
-#include "android/jni/Context.h"
-#include "android/jni/StorageManager.h"
-#include "android/jni/Environment.h"
-
static const char * typeWL[] = { "vfat", "exfat", "sdcardfs", "fuse", "ntfs", "fat32", "ext3", "ext4", "esdfs" };
static const char * mountWL[] = { "/mnt", "/Removable", "/storage" };
static const char * mountBL[] = {
@@ -123,75 +119,21 @@ void CAndroidStorageProvider::GetLocalDrives(VECSOURCES &localDrives)
void CAndroidStorageProvider::GetRemovableDrives(VECSOURCES &removableDrives)
{
- // Uses non-public API: be extra carefull
- bool inError = false;
- VECSOURCES droidDrives;
-
- CJNIStorageManager manager(CJNIContext::getSystemService("storage"));
- if (xbmc_jnienv()->ExceptionCheck())
- {
- xbmc_jnienv()->ExceptionClear();
- inError = true;
- }
- if (!inError)
+ VECSOURCES droidDrives;
+ if (CXBMCApp::GetRemovableDrives(droidDrives))
{
- CJNIStorageVolumes vols = manager.getVolumeList();
- if (xbmc_jnienv()->ExceptionCheck())
+ for (auto d : droidDrives)
{
- xbmc_jnienv()->ExceptionClear();
- inError = true;
- }
-
- if (!inError)
- {
- for (auto vol : vols)
+ auto r = d;
+ if (!r.strDiskUniqueId.empty())
{
-// CLog::Log(LOGDEBUG, "-- Volume: %s(%s) -- %s", vol.getPath().c_str(), vol.getUserLabel().c_str(), vol.getState().c_str());
- bool removable = vol.isRemovable();
- if (xbmc_jnienv()->ExceptionCheck())
- {
- xbmc_jnienv()->ExceptionClear();
- inError = true;
- break;
- }
- std::string state = vol.getState();
- if (xbmc_jnienv()->ExceptionCheck())
- {
- xbmc_jnienv()->ExceptionClear();
- inError = true;
- break;
- }
-
- if (removable && state == CJNIEnvironment::MEDIA_MOUNTED)
- {
- CMediaSource share;
- share.strPath = vol.getPath();
- if (xbmc_jnienv()->ExceptionCheck())
- {
- xbmc_jnienv()->ExceptionClear();
- inError = true;
- break;
- }
- share.strName = vol.getUserLabel();
- if (xbmc_jnienv()->ExceptionCheck())
- {
- xbmc_jnienv()->ExceptionClear();
- inError = true;
- break;
- }
- StringUtils::Trim(share.strName);
- if (share.strName.empty() || share.strName == "?" || StringUtils::CompareNoCase(share.strName, "null") == 0)
- share.strName = URIUtils::GetFileName(share.strPath);
- share.m_ignore = true;
- droidDrives.push_back(share);
- }
+ CLog::Log(LOGDEBUG, "CAndroidStorageProvider: translated %s to %s", r.strPath.c_str(), ("storage://" + r.strDiskUniqueId).c_str());
+ r.strPath = "storage://" + r.strDiskUniqueId;
}
+ removableDrives.push_back(r);
}
}
-
- if (!inError)
- removableDrives.insert(removableDrives.end(), droidDrives.begin(), droidDrives.end());
else
{
for (const auto& mountStr : GetRemovableDrivesLinux())
@@ -212,7 +154,7 @@ void CAndroidStorageProvider::GetRemovableDrives(VECSOURCES &removableDrives)
std::set CAndroidStorageProvider::GetRemovableDrivesLinux()
{
std::set result;
-
+
// mounted usb disks
char* buf = NULL;
FILE* pipe;