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:
Max Kellermann 2022-08-18 18:23:12 +02:00
commit 4f041694d3
9 changed files with 118 additions and 63 deletions

4
NEWS
View File

@ -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

View File

@ -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.

View File

@ -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());
} }

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)};
} }