release v0.23.9
-----BEGIN PGP SIGNATURE----- iQJBBAABCgArFiEEA5IzWngIOJSkMBxDI26KWMbbRRIFAmL+ZuYNHG1heEBibGFy Zy5kZQAKCRAjbopYxttFEmxjD/4sJEq9DroRRK2qnVL1c6rwdzAL05LQwXGfWMCY /eIfoYGgu+PPN65Xf/A7qEvsXXSpuDzq0jTzdmtSvQwfKgg5jy72Qn+LN0dmoSRn mqQoqPFFxr7URf7QhL/6/EKcKYSexu/dxLnnuOC/yB/32WB+JINGNLWABrCpFMW0 4kbVSC6t/e07bfBxNoYKx+kSxX88n82v5LNjkPctx0oU0gRoyRZds273uuKHOIYQ KFHHuaW9eNT0x/JrrsBD0ASXcp/9CfyVvyoag4bqJIc6Bg4fJTEZ9QPy9SUF2R8L 0QCG2DBqFoht7Xqyo7qe5vYy2lww/flVrH/UwB1V7xfWzbuv3qUYs2pRnFmPKjQx 23FRBkmC3tWmno+pZFaYmzprRKQK7WS9DJUzaQ8DCjNVpncadNPDRvz13BZjVLAE LeAFxPZ3B3yrDIjNRGl9LEySMJs7M4jrqzXQZuWS3+O9IZV19ajGczWmFCyQOyu4 F7bj9tfy1yhUjMYOiUIxXsWvxZSVIo8wK1payCJzvKTobnUUeDfDucnD/lpVRMF8 HyJsaZrXwFIClBmK8nCh3LiB5Dh9nxl8xjtmrCiPVzGZvj5qnzMkZWF+C4wxblnZ XTVmHFrpdcI3nK4BzLVrxF+3A08gPkIHta9boqrMhfX4gPWkKmD1vH/+u2OD2BIY I2MASg== =tk1U -----END PGP SIGNATURE----- Merge tag 'v0.23.9' release v0.23.9
This commit is contained in:
commit
4f041694d3
4
NEWS
4
NEWS
|
@ -18,7 +18,7 @@ ver 0.24 (not yet released)
|
||||||
* static partition configuration
|
* static partition configuration
|
||||||
* remove Haiku support
|
* remove Haiku support
|
||||||
|
|
||||||
ver 0.23.9 (not yet released)
|
ver 0.23.9 (2022/08/18)
|
||||||
* input
|
* input
|
||||||
- cdio_paranoia: add options "mode" and "skip"
|
- cdio_paranoia: add options "mode" and "skip"
|
||||||
* decoder
|
* decoder
|
||||||
|
@ -30,6 +30,8 @@ ver 0.23.9 (not yet released)
|
||||||
* fix bogus volume levels with multiple partitions
|
* fix bogus volume levels with multiple partitions
|
||||||
* improve iconv detection
|
* improve iconv detection
|
||||||
* macOS: fix macOS 10 build problem (0.23.8 regression)
|
* macOS: fix macOS 10 build problem (0.23.8 regression)
|
||||||
|
* Android
|
||||||
|
- load mpd.conf from app data directory
|
||||||
|
|
||||||
ver 0.23.8 (2022/07/09)
|
ver 0.23.8 (2022/07/09)
|
||||||
* storage
|
* storage
|
||||||
|
|
|
@ -36,7 +36,9 @@ Installing on Android
|
||||||
|
|
||||||
An experimental Android build is available on Google Play. After installing and launching it, :program:`MPD` will scan the music in your Music directory and you can control it as usual with a :program:`MPD` client.
|
An experimental Android build is available on Google Play. After installing and launching it, :program:`MPD` will scan the music in your Music directory and you can control it as usual with a :program:`MPD` client.
|
||||||
|
|
||||||
If you need to tweak the configuration, you can create a file called :file:`mpd.conf` on the data partition (the directory which is returned by Android's :dfn:`getExternalStorageDirectory()` API function).
|
If you need to tweak the configuration, you can create a file called
|
||||||
|
:file:`mpd.conf` in MPD's data directory on the external storage
|
||||||
|
(usually :file:`Android/data/org.musicpd/files/mpd.conf`).
|
||||||
|
|
||||||
ALSA is not available on Android; only the :ref:`OpenSL ES
|
ALSA is not available on Android; only the :ref:`OpenSL ES
|
||||||
<sles_output>` output plugin can be used for local playback.
|
<sles_output>` output plugin can be used for local playback.
|
||||||
|
|
48
src/Main.cxx
48
src/Main.cxx
|
@ -540,19 +540,46 @@ MainConfigured(const CommandLineOptions &options,
|
||||||
|
|
||||||
#ifdef ANDROID
|
#ifdef ANDROID
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for ReadConfigFile() which returns false if the file was
|
||||||
|
* not found.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
TryReadConfigFile(ConfigData &config, Path path)
|
||||||
|
{
|
||||||
|
if (!FileExists(path))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ReadConfigFile(config, path);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
AndroidMain()
|
LoadConfigFile(JNIEnv *env, ConfigData &config)
|
||||||
|
{
|
||||||
|
/* try loading mpd.conf from
|
||||||
|
"Android/data/org.musicpd/files/mpd.conf" (the app specific
|
||||||
|
data directory) first */
|
||||||
|
if (const auto dir = context->GetExternalFilesDir(env);
|
||||||
|
!dir.IsNull() &&
|
||||||
|
TryReadConfigFile(config, dir / Path::FromFS("mpd.conf")))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* if that fails, attempt to load "mpd.conf" from the root of
|
||||||
|
the SD card (pre-0.23.9, ceases to work since Android
|
||||||
|
12) */
|
||||||
|
if (const auto dir = Environment::getExternalStorageDirectory(env);
|
||||||
|
!dir.IsNull())
|
||||||
|
TryReadConfigFile(config, dir / Path::FromFS("mpd.conf"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
AndroidMain(JNIEnv *env)
|
||||||
{
|
{
|
||||||
CommandLineOptions options;
|
CommandLineOptions options;
|
||||||
ConfigData raw_config;
|
ConfigData raw_config;
|
||||||
|
|
||||||
const auto sdcard = Environment::getExternalStorageDirectory();
|
LoadConfigFile(env, raw_config);
|
||||||
if (!sdcard.IsNull()) {
|
|
||||||
const auto config_path =
|
|
||||||
sdcard / Path::FromFS("mpd.conf");
|
|
||||||
if (FileExists(config_path))
|
|
||||||
ReadConfigFile(raw_config, config_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
MainConfigured(options, raw_config);
|
MainConfigured(options, raw_config);
|
||||||
}
|
}
|
||||||
|
@ -564,9 +591,12 @@ Java_org_musicpd_Bridge_run(JNIEnv *env, jclass, jobject _context, jobject _logL
|
||||||
Java::Init(env);
|
Java::Init(env);
|
||||||
Java::Object::Initialise(env);
|
Java::Object::Initialise(env);
|
||||||
Java::File::Initialise(env);
|
Java::File::Initialise(env);
|
||||||
|
|
||||||
Environment::Initialise(env);
|
Environment::Initialise(env);
|
||||||
AtScopeExit(env) { Environment::Deinitialise(env); };
|
AtScopeExit(env) { Environment::Deinitialise(env); };
|
||||||
|
|
||||||
|
Context::Initialise(env);
|
||||||
|
|
||||||
context = new Context(env, _context);
|
context = new Context(env, _context);
|
||||||
AtScopeExit() { delete context; };
|
AtScopeExit() { delete context; };
|
||||||
|
|
||||||
|
@ -575,7 +605,7 @@ Java_org_musicpd_Bridge_run(JNIEnv *env, jclass, jobject _context, jobject _logL
|
||||||
AtScopeExit() { delete logListener; };
|
AtScopeExit() { delete logListener; };
|
||||||
|
|
||||||
try {
|
try {
|
||||||
AndroidMain();
|
AndroidMain(env);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
LogError(std::current_exception());
|
LogError(std::current_exception());
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,19 +26,30 @@
|
||||||
|
|
||||||
#include "AudioManager.hxx"
|
#include "AudioManager.hxx"
|
||||||
|
|
||||||
|
static jmethodID getExternalFilesDir_method,
|
||||||
|
getCacheDir_method,
|
||||||
|
getSystemService_method;
|
||||||
|
|
||||||
|
void
|
||||||
|
Context::Initialise(JNIEnv *env) noexcept
|
||||||
|
{
|
||||||
|
Java::Class cls{env, "android/content/Context"};
|
||||||
|
|
||||||
|
getExternalFilesDir_method = env->GetMethodID(cls, "getExternalFilesDir",
|
||||||
|
"(Ljava/lang/String;)Ljava/io/File;");
|
||||||
|
getCacheDir_method = env->GetMethodID(cls, "getCacheDir",
|
||||||
|
"()Ljava/io/File;");
|
||||||
|
getSystemService_method = env->GetMethodID(cls, "getSystemService",
|
||||||
|
"(Ljava/lang/String;)Ljava/lang/Object;");
|
||||||
|
}
|
||||||
|
|
||||||
AllocatedPath
|
AllocatedPath
|
||||||
Context::GetExternalFilesDir(JNIEnv *env, const char *_type) noexcept
|
Context::GetExternalFilesDir(JNIEnv *env, const char *type) noexcept
|
||||||
{
|
{
|
||||||
assert(_type != nullptr);
|
assert(_type != nullptr);
|
||||||
|
|
||||||
Java::Class cls{env, env->GetObjectClass(Get())};
|
jobject file = env->CallObjectMethod(Get(), getExternalFilesDir_method,
|
||||||
jmethodID method = env->GetMethodID(cls, "getExternalFilesDir",
|
Java::String::Optional(env, type).Get());
|
||||||
"(Ljava/lang/String;)Ljava/io/File;");
|
|
||||||
assert(method);
|
|
||||||
|
|
||||||
Java::String type{env, _type};
|
|
||||||
|
|
||||||
jobject file = env->CallObjectMethod(Get(), method, type.Get());
|
|
||||||
if (Java::DiscardException(env) || file == nullptr)
|
if (Java::DiscardException(env) || file == nullptr)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
@ -50,12 +61,7 @@ Context::GetCacheDir(JNIEnv *env) const noexcept
|
||||||
{
|
{
|
||||||
assert(env != nullptr);
|
assert(env != nullptr);
|
||||||
|
|
||||||
Java::Class cls(env, env->GetObjectClass(Get()));
|
jobject file = env->CallObjectMethod(Get(), getCacheDir_method);
|
||||||
jmethodID method = env->GetMethodID(cls, "getCacheDir",
|
|
||||||
"()Ljava/io/File;");
|
|
||||||
assert(method);
|
|
||||||
|
|
||||||
jobject file = env->CallObjectMethod(Get(), method);
|
|
||||||
if (Java::DiscardException(env) || file == nullptr)
|
if (Java::DiscardException(env) || file == nullptr)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
@ -67,13 +73,8 @@ Context::GetAudioManager(JNIEnv *env) noexcept
|
||||||
{
|
{
|
||||||
assert(env != nullptr);
|
assert(env != nullptr);
|
||||||
|
|
||||||
Java::Class cls(env, env->GetObjectClass(Get()));
|
|
||||||
jmethodID method = env->GetMethodID(cls, "getSystemService",
|
|
||||||
"(Ljava/lang/String;)Ljava/lang/Object;");
|
|
||||||
assert(method);
|
|
||||||
|
|
||||||
Java::String name(env, "audio");
|
Java::String name(env, "audio");
|
||||||
jobject am = env->CallObjectMethod(Get(), method, name.Get());
|
jobject am = env->CallObjectMethod(Get(), getSystemService_method, name.Get());
|
||||||
if (Java::DiscardException(env) || am == nullptr)
|
if (Java::DiscardException(env) || am == nullptr)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
|
|
@ -27,12 +27,21 @@ class AudioManager;
|
||||||
|
|
||||||
class Context : public Java::GlobalObject {
|
class Context : public Java::GlobalObject {
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* Global initialisation. Looks up the methods of the
|
||||||
|
* Context Java class.
|
||||||
|
*/
|
||||||
|
static void Initialise(JNIEnv *env) noexcept;
|
||||||
|
|
||||||
Context(JNIEnv *env, jobject obj) noexcept
|
Context(JNIEnv *env, jobject obj) noexcept
|
||||||
:Java::GlobalObject(env, obj) {}
|
:Java::GlobalObject(env, obj) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param type the subdirectory name; may be nullptr
|
||||||
|
*/
|
||||||
[[gnu::pure]]
|
[[gnu::pure]]
|
||||||
AllocatedPath GetExternalFilesDir(JNIEnv *env,
|
AllocatedPath GetExternalFilesDir(JNIEnv *env,
|
||||||
const char *type) noexcept;
|
const char *type=nullptr) noexcept;
|
||||||
|
|
||||||
[[gnu::pure]]
|
[[gnu::pure]]
|
||||||
AllocatedPath GetCacheDir(JNIEnv *env) const noexcept;
|
AllocatedPath GetCacheDir(JNIEnv *env) const noexcept;
|
||||||
|
|
|
@ -25,13 +25,13 @@
|
||||||
#include "fs/AllocatedPath.hxx"
|
#include "fs/AllocatedPath.hxx"
|
||||||
|
|
||||||
namespace Environment {
|
namespace Environment {
|
||||||
static Java::TrivialClass cls;
|
|
||||||
static jmethodID getExternalStorageDirectory_method;
|
static Java::TrivialClass cls;
|
||||||
static jmethodID getExternalStoragePublicDirectory_method;
|
static jmethodID getExternalStorageDirectory_method;
|
||||||
}
|
static jmethodID getExternalStoragePublicDirectory_method;
|
||||||
|
|
||||||
void
|
void
|
||||||
Environment::Initialise(JNIEnv *env) noexcept
|
Initialise(JNIEnv *env) noexcept
|
||||||
{
|
{
|
||||||
cls.Find(env, "android/os/Environment");
|
cls.Find(env, "android/os/Environment");
|
||||||
|
|
||||||
|
@ -45,16 +45,14 @@ Environment::Initialise(JNIEnv *env) noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Environment::Deinitialise(JNIEnv *env) noexcept
|
Deinitialise(JNIEnv *env) noexcept
|
||||||
{
|
{
|
||||||
cls.Clear(env);
|
cls.Clear(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
AllocatedPath
|
AllocatedPath
|
||||||
Environment::getExternalStorageDirectory() noexcept
|
getExternalStorageDirectory(JNIEnv *env) noexcept
|
||||||
{
|
{
|
||||||
JNIEnv *env = Java::GetEnv();
|
|
||||||
|
|
||||||
jobject file =
|
jobject file =
|
||||||
env->CallStaticObjectMethod(cls,
|
env->CallStaticObjectMethod(cls,
|
||||||
getExternalStorageDirectory_method);
|
getExternalStorageDirectory_method);
|
||||||
|
@ -65,20 +63,20 @@ Environment::getExternalStorageDirectory() noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
AllocatedPath
|
AllocatedPath
|
||||||
Environment::getExternalStoragePublicDirectory(const char *type) noexcept
|
getExternalStoragePublicDirectory(JNIEnv *env, const char *type) noexcept
|
||||||
{
|
{
|
||||||
if (getExternalStoragePublicDirectory_method == nullptr)
|
if (getExternalStoragePublicDirectory_method == nullptr)
|
||||||
/* needs API level 8 */
|
/* needs API level 8 */
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
JNIEnv *env = Java::GetEnv();
|
|
||||||
|
|
||||||
Java::String type2(env, type);
|
Java::String type2(env, type);
|
||||||
jobject file = env->CallStaticObjectMethod(Environment::cls,
|
jobject file = env->CallStaticObjectMethod(cls,
|
||||||
Environment::getExternalStoragePublicDirectory_method,
|
getExternalStoragePublicDirectory_method,
|
||||||
type2.Get());
|
type2.Get());
|
||||||
if (file == nullptr)
|
if (file == nullptr)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return Java::File::ToAbsolutePath(env, file);
|
return Java::File::ToAbsolutePath(env, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace Environment
|
||||||
|
|
|
@ -17,27 +17,29 @@
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef MPD_ANDROID_ENVIRONMENT_HXX
|
#pragma once
|
||||||
#define MPD_ANDROID_ENVIRONMENT_HXX
|
|
||||||
|
|
||||||
#include "util/Compiler.h"
|
|
||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
class AllocatedPath;
|
class AllocatedPath;
|
||||||
|
|
||||||
namespace Environment {
|
namespace Environment {
|
||||||
void Initialise(JNIEnv *env) noexcept;
|
|
||||||
void Deinitialise(JNIEnv *env) noexcept;
|
|
||||||
|
|
||||||
/**
|
void
|
||||||
* Determine the mount point of the external SD card.
|
Initialise(JNIEnv *env) noexcept;
|
||||||
*/
|
|
||||||
[[gnu::pure]]
|
|
||||||
AllocatedPath getExternalStorageDirectory() noexcept;
|
|
||||||
|
|
||||||
[[gnu::pure]]
|
void
|
||||||
AllocatedPath getExternalStoragePublicDirectory(const char *type) noexcept;
|
Deinitialise(JNIEnv *env) noexcept;
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
/**
|
||||||
|
* Determine the mount point of the external SD card.
|
||||||
|
*/
|
||||||
|
[[gnu::pure]]
|
||||||
|
AllocatedPath
|
||||||
|
getExternalStorageDirectory(JNIEnv *env) noexcept;
|
||||||
|
|
||||||
|
[[gnu::pure]]
|
||||||
|
AllocatedPath
|
||||||
|
getExternalStoragePublicDirectory(JNIEnv *env, const char *type) noexcept;
|
||||||
|
|
||||||
|
} // namespace Environment
|
||||||
|
|
|
@ -254,7 +254,8 @@ GetUserMusicDir() noexcept
|
||||||
#elif defined(USE_XDG)
|
#elif defined(USE_XDG)
|
||||||
return GetUserDir("XDG_MUSIC_DIR");
|
return GetUserDir("XDG_MUSIC_DIR");
|
||||||
#elif defined(ANDROID)
|
#elif defined(ANDROID)
|
||||||
return Environment::getExternalStoragePublicDirectory("Music");
|
return Environment::getExternalStoragePublicDirectory(Java::GetEnv(),
|
||||||
|
"Music");
|
||||||
#else
|
#else
|
||||||
return nullptr;
|
return nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -92,6 +92,16 @@ public:
|
||||||
|
|
||||||
String(JNIEnv *_env, std::string_view _value) noexcept;
|
String(JNIEnv *_env, std::string_view _value) noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This constructor allows passing a nullptr value, which maps
|
||||||
|
* to a "null" in Java.
|
||||||
|
*/
|
||||||
|
static String Optional(JNIEnv *_env, const char *_value) noexcept {
|
||||||
|
return _value != nullptr
|
||||||
|
? String{_env, _value}
|
||||||
|
: String{};
|
||||||
|
}
|
||||||
|
|
||||||
static StringUTFChars GetUTFChars(JNIEnv *env, jstring s) noexcept {
|
static StringUTFChars GetUTFChars(JNIEnv *env, jstring s) noexcept {
|
||||||
return {env, s, env->GetStringUTFChars(s, nullptr)};
|
return {env, s, env->GetStringUTFChars(s, nullptr)};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue