Added buffering indicator to the android client, and also tweaked a

couple transcoder settings keys. Also updated some variable names --
we're not decoding, we're encoding!
This commit is contained in:
casey langen 2017-05-05 19:08:50 -07:00
parent 45811b90a0
commit 0aff544712
14 changed files with 135 additions and 68 deletions

View File

@ -41,18 +41,18 @@ namespace defaults {
static const int websocket_server_port = 7905; static const int websocket_server_port = 7905;
static const int http_server_port = 7906; static const int http_server_port = 7906;
static const std::string password = ""; static const std::string password = "";
static const int http_server_transcoder_cache_count = 50; static const int transcoder_cache_count = 50;
static const bool http_server_transcoder_synchronous = false; static const bool transcoder_synchronous = false;
static const bool http_server_transcoder_synchronous_fallback = true; static const bool transcoder_synchronous_fallback = true;
} }
namespace prefs { namespace prefs {
static const std::string websocket_server_port = "websocket_server_port"; static const std::string websocket_server_port = "websocket_server_port";
static const std::string http_server_enabled = "http_server_enabled"; static const std::string http_server_enabled = "http_server_enabled";
static const std::string http_server_port = "http_server_port"; static const std::string http_server_port = "http_server_port";
static const std::string http_server_transcoder_cache_count = "http_server_transcoder_cache_count"; static const std::string transcoder_cache_count = "transcoder_cache_count";
static const std::string http_server_transcoder_synchronous = "http_server_transcoder_synchronous"; static const std::string transcoder_synchronous = "transcoder_synchronous";
static const std::string http_server_transcoder_synchronous_fallback = "http_server_transcoder_synchronous_fallback"; static const std::string transcoder_synchronous_fallback = "transcoder_synchronous_fallback";
} }
namespace message { namespace message {

View File

@ -328,8 +328,8 @@ int HttpServer::HandleRequest(
} }
if (false && server->context.prefs->GetBool( if (false && server->context.prefs->GetBool(
prefs::http_server_transcoder_synchronous_fallback.c_str(), prefs::transcoder_synchronous_fallback.c_str(),
defaults::http_server_transcoder_synchronous_fallback)) defaults::transcoder_synchronous_fallback))
{ {
/* if we're allowed, fall back to synchronous transcoding. we'll block /* if we're allowed, fall back to synchronous transcoding. we'll block
here until the entire file has been converted and cached */ here until the entire file has been converted and cached */

View File

@ -46,17 +46,15 @@ static std::string cachePath(Context& context) {
context.environment->GetPath(PathType::PathData, buf, sizeof(buf)); context.environment->GetPath(PathType::PathData, buf, sizeof(buf));
std::string path = std::string(buf) + "/transcode/"; std::string path = std::string(buf) + "/transcode/";
if (!boost::filesystem::exists(path)) { if (!exists(path)) {
boost::filesystem::create_directories(path); create_directories(path);
} }
return path; return path;
} }
static void iterateTranscodeCache(Context& context, std::function<void(boost::filesystem::path)> cb) { static void iterateTranscodeCache(Context& context, std::function<void(path)> cb) {
if (cb) { if (cb) {
using namespace boost::filesystem;
directory_iterator end; directory_iterator end;
directory_iterator file(cachePath(context)); directory_iterator file(cachePath(context));
@ -70,32 +68,32 @@ static void iterateTranscodeCache(Context& context, std::function<void(boost::fi
} }
void Transcoder::RemoveTempTranscodeFiles(Context& context) { void Transcoder::RemoveTempTranscodeFiles(Context& context) {
iterateTranscodeCache(context, [](boost::filesystem::path p) { iterateTranscodeCache(context, [](path p) {
if (p.extension().string() == ".tmp") { if (p.extension().string() == ".tmp") {
boost::system::error_code ec; boost::system::error_code ec;
boost::filesystem::remove(p, ec); remove(p, ec);
} }
}); });
} }
void Transcoder::PruneTranscodeCache(Context& context) { void Transcoder::PruneTranscodeCache(Context& context) {
std::map<time_t, boost::filesystem::path> sorted; std::map<time_t, path> sorted;
boost::system::error_code ec; boost::system::error_code ec;
iterateTranscodeCache(context, [&sorted, &ec](boost::filesystem::path p) { iterateTranscodeCache(context, [&sorted, &ec](path p) {
sorted[boost::filesystem::last_write_time(p, ec)] = p; sorted[last_write_time(p, ec)] = p;
}); });
int maxSize = context.prefs->GetInt( int maxSize = context.prefs->GetInt(
prefs::http_server_transcoder_cache_count.c_str(), prefs::transcoder_cache_count.c_str(),
defaults::http_server_transcoder_cache_count); defaults::transcoder_cache_count);
int extra = (int) sorted.size() - (maxSize - 1); int extra = (int) sorted.size() - (maxSize - 1);
auto it = sorted.begin(); auto it = sorted.begin();
while (extra > 0 && it != sorted.end()) { while (extra > 0 && it != sorted.end()) {
auto p = it->second; auto p = it->second;
boost::system::error_code ec; boost::system::error_code ec;
if (boost::filesystem::remove(p, ec)) { if (remove(p, ec)) {
--extra; --extra;
} }
} }
@ -116,15 +114,15 @@ static void getTempAndFinalFilename(
do { do {
tempFn = finalFn + "." + std::to_string(rand()) + ".tmp"; tempFn = finalFn + "." + std::to_string(rand()) + ".tmp";
} while (boost::filesystem::exists(tempFn)); } while (exists(tempFn));
} }
IDataStream* Transcoder::Transcode( IDataStream* Transcoder::Transcode(
Context& context, const std::string& uri, size_t bitrate) Context& context, const std::string& uri, size_t bitrate)
{ {
if (context.prefs->GetBool( if (context.prefs->GetBool(
prefs::http_server_transcoder_synchronous.c_str(), prefs::transcoder_synchronous.c_str(),
defaults::http_server_transcoder_synchronous)) defaults::transcoder_synchronous))
{ {
return TranscodeAndWait(context, uri, bitrate); return TranscodeAndWait(context, uri, bitrate);
} }

View File

@ -176,43 +176,43 @@ PositionType TranscodingDataStream::Read(void *buffer, PositionType bytesToRead)
size_t numSamples = pcmBuffer->Samples() / pcmBuffer->Channels(); size_t numSamples = pcmBuffer->Samples() / pcmBuffer->Channels();
size_t requiredBytes = (size_t) (1.25 * (float)numSamples + 7200.0); size_t requiredBytes = (size_t) (1.25 * (float)numSamples + 7200.0);
decodedBytes.realloc(requiredBytes); encodedBytes.realloc(requiredBytes);
/* decode PCM -> MP3 */ /* encode PCM -> MP3 */
int decodeCount = int encodeCount =
lame_encode_buffer_interleaved_ieee_float( lame_encode_buffer_interleaved_ieee_float(
lame, lame,
pcmBuffer->BufferPointer(), pcmBuffer->BufferPointer(),
numSamples, numSamples,
decodedBytes.data, encodedBytes.data,
decodedBytes.length); encodedBytes.length);
if (decodeCount < 0) { if (encodeCount < 0) {
goto internal_error; goto internal_error;
} }
decodedBytes.length = (size_t) decodeCount; encodedBytes.length = (size_t)encodeCount;
/* if we got something, let's write it to the output buffer */ /* if we got something, let's write it to the output buffer */
if (decodedBytes.length) { if (encodedBytes.length) {
size_t toWrite = std::min( size_t toWrite = std::min(
decodedBytes.length, encodedBytes.length,
(size_t)(bytesToRead - bytesWritten)); (size_t)(bytesToRead - bytesWritten));
memcpy(dst + bytesWritten, decodedBytes.data, toWrite); memcpy(dst + bytesWritten, encodedBytes.data, toWrite);
if (this->outFile) { if (this->outFile) {
fwrite(decodedBytes.data, 1, toWrite, this->outFile); fwrite(encodedBytes.data, 1, toWrite, this->outFile);
} }
decodedBytes.inc(toWrite); encodedBytes.inc(toWrite);
bytesWritten += toWrite; bytesWritten += toWrite;
/* if we have decoded bytes still available, that means the /* if we have decoded bytes still available, that means the
output buffer is exhausted. swap it into the spillover buffer output buffer is exhausted. swap it into the spillover buffer
so it can be finalized the next time through. */ so it can be finalized the next time through. */
if (decodedBytes.avail()) { if (encodedBytes.avail()) {
spillover.swap(decodedBytes); spillover.swap(encodedBytes);
this->position += bytesWritten; this->position += bytesWritten;
return bytesWritten; return bytesWritten;
} }
@ -228,17 +228,17 @@ PositionType TranscodingDataStream::Read(void *buffer, PositionType bytesToRead)
/* finalize */ /* finalize */
if (bytesWritten == 0) { if (bytesWritten == 0) {
decodedBytes.reset(); encodedBytes.reset();
size_t count = lame_encode_flush( size_t count = lame_encode_flush(
lame, lame,
decodedBytes.data, encodedBytes.data,
decodedBytes.length); encodedBytes.length);
memcpy(dst + bytesWritten, decodedBytes.data, count); memcpy(dst + bytesWritten, encodedBytes.data, count);
if (this->outFile) { if (this->outFile) {
fwrite(decodedBytes.data, 1, count, this->outFile); fwrite(encodedBytes.data, 1, count, this->outFile);
fclose(this->outFile); fclose(this->outFile);
this->outFile = nullptr; this->outFile = nullptr;

View File

@ -128,7 +128,7 @@ class TranscodingDataStream : public musik::core::sdk::IDataStream {
size_t offset, length, rawLength; size_t offset, length, rawLength;
}; };
ByteBuffer decodedBytes; ByteBuffer encodedBytes;
ByteBuffer spillover; ByteBuffer spillover;
size_t bitrate; size_t bitrate;
bool eof; bool eof;

View File

@ -159,9 +159,9 @@ extern "C" DLL_EXPORT void SetPreferences(musik::core::sdk::IPreferences* prefs)
prefs->GetInt(prefs::http_server_port.c_str(), defaults::http_server_port); prefs->GetInt(prefs::http_server_port.c_str(), defaults::http_server_port);
prefs->GetBool(prefs::http_server_enabled.c_str(), true); prefs->GetBool(prefs::http_server_enabled.c_str(), true);
prefs->GetString(key::password.c_str(), nullptr, 0, defaults::password.c_str()); prefs->GetString(key::password.c_str(), nullptr, 0, defaults::password.c_str());
prefs->GetInt(prefs::http_server_transcoder_cache_count.c_str(), defaults::http_server_transcoder_cache_count); prefs->GetInt(prefs::transcoder_cache_count.c_str(), defaults::transcoder_cache_count);
prefs->GetBool(prefs::http_server_transcoder_synchronous.c_str(), defaults::http_server_transcoder_synchronous); prefs->GetBool(prefs::transcoder_synchronous.c_str(), defaults::transcoder_synchronous);
prefs->GetBool(prefs::http_server_transcoder_synchronous_fallback.c_str(), defaults::http_server_transcoder_synchronous_fallback); prefs->GetBool(prefs::transcoder_synchronous_fallback.c_str(), defaults::transcoder_synchronous_fallback);
prefs->Save(); prefs->Save();
} }

View File

@ -44,6 +44,7 @@
#include <core/debug.h> #include <core/debug.h>
static const std::string TAG = "LocalLibrary"; static const std::string TAG = "LocalLibrary";
static bool scheduleSyncDueToDbUpgrade = false;
using namespace musik::core; using namespace musik::core;
using namespace musik::core::library; using namespace musik::core::library;
@ -90,6 +91,10 @@ LocalLibrary::LocalLibrary(std::string name,int id)
this->GetLibraryDirectory(), this->GetLibraryDirectory(),
this->GetDatabaseFilename()); this->GetDatabaseFilename());
if (scheduleSyncDueToDbUpgrade) {
this->indexer->Schedule(IIndexer::SyncType::Local);
}
this->thread = new std::thread(std::bind(&LocalLibrary::ThreadProc, this)); this->thread = new std::thread(std::bind(&LocalLibrary::ThreadProc, this));
} }
@ -290,6 +295,8 @@ static void upgradeV2ToV3(db::Connection& db) {
"name TEXT default ''," "name TEXT default '',"
"thumbnail_id INTEGER default 0," "thumbnail_id INTEGER default 0,"
"sort_order INTEGER DEFAULT 0)"); "sort_order INTEGER DEFAULT 0)");
scheduleSyncDueToDbUpgrade = true;
} }
static void setVersion(db::Connection& db, int version) { static void setVersion(db::Connection& db, int version) {

View File

@ -59,6 +59,7 @@ public class MainActivity extends WebSocketActivityBase {
private TextView title, artist, album, playPause, volume; private TextView title, artist, album, playPause, volume;
private TextView titleWithArt, artistAndAlbumWithArt, volumeWithArt; private TextView titleWithArt, artistAndAlbumWithArt, volumeWithArt;
private TextView notPlayingOrDisconnected; private TextView notPlayingOrDisconnected;
private View buffering, bufferingWithArt;
private View connected; private View connected;
private CheckBox shuffleCb, muteCb, repeatCb; private CheckBox shuffleCb, muteCb, repeatCb;
private View disconnectedOverlay; private View disconnectedOverlay;
@ -182,10 +183,12 @@ public class MainActivity extends WebSocketActivityBase {
this.artist = (TextView) findViewById(R.id.track_artist); this.artist = (TextView) findViewById(R.id.track_artist);
this.album = (TextView) findViewById(R.id.track_album); this.album = (TextView) findViewById(R.id.track_album);
this.volume = (TextView) findViewById(R.id.volume); this.volume = (TextView) findViewById(R.id.volume);
this.buffering = findViewById(R.id.buffering);
this.titleWithArt = (TextView) findViewById(R.id.with_art_track_title); this.titleWithArt = (TextView) findViewById(R.id.with_art_track_title);
this.artistAndAlbumWithArt = (TextView) findViewById(R.id.with_art_artist_and_album); this.artistAndAlbumWithArt = (TextView) findViewById(R.id.with_art_artist_and_album);
this.volumeWithArt = (TextView) findViewById(R.id.with_art_volume); this.volumeWithArt = (TextView) findViewById(R.id.with_art_volume);
this.bufferingWithArt = findViewById(R.id.with_art_buffering);
this.playPause = (TextView) findViewById(R.id.button_play_pause); this.playPause = (TextView) findViewById(R.id.button_play_pause);
this.shuffleCb = (CheckBox) findViewById(R.id.check_shuffle); this.shuffleCb = (CheckBox) findViewById(R.id.check_shuffle);
@ -263,8 +266,13 @@ public class MainActivity extends WebSocketActivityBase {
} }
private void rebindAlbumArtistWithArtTextView() { private void rebindAlbumArtistWithArtTextView() {
final String artist = playback.getTrackString(Metadata.Track.ARTIST, getString(R.string.unknown_artist)); final boolean buffering = playback.getPlaybackState() == PlaybackState.Buffering;
final String album = playback.getTrackString(Metadata.Track.ALBUM, getString(R.string.unknown_album));
final String artist = playback.getTrackString(
Metadata.Track.ARTIST, getString(buffering ? R.string.buffering : R.string.unknown_artist));
final String album = playback.getTrackString(
Metadata.Track.ALBUM, getString(buffering ? R.string.buffering : R.string.unknown_album));
final ForegroundColorSpan albumColor = final ForegroundColorSpan albumColor =
new ForegroundColorSpan(getResources().getColor(R.color.theme_orange)); new ForegroundColorSpan(getResources().getColor(R.color.theme_orange));
@ -328,6 +336,8 @@ public class MainActivity extends WebSocketActivityBase {
final boolean stopped = (playback.getPlaybackState() == PlaybackState.Stopped); final boolean stopped = (playback.getPlaybackState() == PlaybackState.Stopped);
notPlayingOrDisconnected.setVisibility(stopped ? View.VISIBLE : View.GONE); notPlayingOrDisconnected.setVisibility(stopped ? View.VISIBLE : View.GONE);
boolean buffering = playback.getPlaybackState() == PlaybackState.Buffering;
final boolean stateIsValidForArtwork = !stopped && connected && playback.getQueueCount() > 0; final boolean stateIsValidForArtwork = !stopped && connected && playback.getQueueCount() > 0;
this.connected.setVisibility((connected && stopped) ? View.VISIBLE : View.GONE); this.connected.setVisibility((connected && stopped) ? View.VISIBLE : View.GONE);
@ -350,15 +360,18 @@ public class MainActivity extends WebSocketActivityBase {
final String title = playback.getTrackString(Metadata.Track.TITLE, ""); final String title = playback.getTrackString(Metadata.Track.TITLE, "");
final String volume = getString(R.string.status_volume, Math.round(playback.getVolume() * 100)); final String volume = getString(R.string.status_volume, Math.round(playback.getVolume() * 100));
this.title.setText(Strings.empty(title) ? getString(R.string.unknown_title) : title); this.title.setText(Strings.empty(title) ? getString(buffering ? R.string.buffering : R.string.unknown_title) : title);
this.artist.setText(Strings.empty(artist) ? getString(R.string.unknown_artist) : artist); this.artist.setText(Strings.empty(artist) ? getString(buffering ? R.string.buffering : R.string.unknown_artist) : artist);
this.album.setText(Strings.empty(album) ? getString(R.string.unknown_album) : album); this.album.setText(Strings.empty(album) ? getString(buffering ? R.string.buffering : R.string.unknown_album) : album);
this.volume.setText(volume); this.volume.setText(volume);
this.rebindAlbumArtistWithArtTextView(); this.rebindAlbumArtistWithArtTextView();
this.titleWithArt.setText(Strings.empty(title) ? getString(R.string.unknown_title) : title); this.titleWithArt.setText(Strings.empty(title) ? getString(buffering ? R.string.buffering : R.string.unknown_title) : title);
this.volumeWithArt.setText(volume); this.volumeWithArt.setText(volume);
this.buffering.setVisibility(buffering ? View.VISIBLE : View.GONE);
this.bufferingWithArt.setVisibility(buffering ? View.VISIBLE : View.GONE);
final RepeatMode repeatMode = playback.getRepeatMode(); final RepeatMode repeatMode = playback.getRepeatMode();
final boolean repeatChecked = (repeatMode != RepeatMode.None); final boolean repeatChecked = (repeatMode != RepeatMode.None);
repeatCb.setText(REPEAT_TO_STRING_ID.get(repeatMode)); repeatCb.setText(REPEAT_TO_STRING_ID.get(repeatMode));
@ -381,7 +394,6 @@ public class MainActivity extends WebSocketActivityBase {
this.albumArtModel.destroy(); this.albumArtModel.destroy();
this.albumArtModel = new AlbumArtModel(title, artist, album, albumArtRetrieved); this.albumArtModel = new AlbumArtModel(title, artist, album, albumArtRetrieved);
} }
updateAlbumArt(); updateAlbumArt();
} }
} }

View File

@ -764,6 +764,7 @@ public class StreamingPlaybackService implements PlaybackService {
.doOnComplete(() -> { .doOnComplete(() -> {
if (StreamingPlaybackService.this.params == params) { if (StreamingPlaybackService.this.params == params) {
StreamingPlaybackService.this.context = context; StreamingPlaybackService.this.context = context;
notifyEventListeners();
onPlayQueueLoaded(); onPlayQueueLoaded();
} }
}) })

View File

@ -24,7 +24,7 @@ public class TransportFragment extends Fragment {
return new TransportFragment(); return new TransportFragment();
} }
private View rootView; private View rootView, buffering;
private TextView title, playPause; private TextView title, playPause;
private PlaybackService playback; private PlaybackService playback;
private OnModelChangedListener modelChangedListener; private OnModelChangedListener modelChangedListener;
@ -60,6 +60,7 @@ public class TransportFragment extends Fragment {
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
rebindUi();
this.playback.connect(this.playbackListener); this.playback.connect(this.playbackListener);
} }
@ -73,8 +74,10 @@ public class TransportFragment extends Fragment {
private void bindEventHandlers() { private void bindEventHandlers() {
this.title = (TextView) this.rootView.findViewById(R.id.track_title); this.title = (TextView) this.rootView.findViewById(R.id.track_title);
this.buffering = this.rootView.findViewById(R.id.buffering);
this.title.setOnClickListener((View view) -> { final View titleBar = this.rootView.findViewById(R.id.title_bar);
titleBar.setOnClickListener((View view) -> {
if (playback.getPlaybackState() != PlaybackState.Stopped) { if (playback.getPlaybackState() != PlaybackState.Stopped) {
final Intent intent = PlayQueueActivity final Intent intent = PlayQueueActivity
.getStartIntent(getActivity(), playback.getQueuePosition()) .getStartIntent(getActivity(), playback.getQueuePosition())
@ -109,7 +112,10 @@ public class TransportFragment extends Fragment {
PlaybackState state = playback.getPlaybackState(); PlaybackState state = playback.getPlaybackState();
final boolean playing = (state == PlaybackState.Playing); final boolean playing = (state == PlaybackState.Playing);
final boolean buffering = (state == PlaybackState.Buffering);
this.playPause.setText(playing ? R.string.button_pause : R.string.button_play); this.playPause.setText(playing ? R.string.button_pause : R.string.button_play);
this.buffering.setVisibility(buffering ? View.VISIBLE : View.GONE);
if (state == PlaybackState.Stopped) { if (state == PlaybackState.Stopped) {
title.setTextColor(getActivity().getResources().getColor(R.color.theme_disabled_foreground)); title.setTextColor(getActivity().getResources().getColor(R.color.theme_disabled_foreground));
@ -117,7 +123,9 @@ public class TransportFragment extends Fragment {
} }
else { else {
title.setTextColor(getActivity().getResources().getColor(R.color.theme_green)); title.setTextColor(getActivity().getResources().getColor(R.color.theme_green));
title.setText(playback.getTrackString(Metadata.Track.TITLE, "(unknown title)"));
final String defaultValue = getString(buffering ? R.string.buffering : R.string.unknown_title);
title.setText(playback.getTrackString(Metadata.Track.TITLE, defaultValue));
} }
} }

View File

@ -60,6 +60,14 @@
android:paddingTop="2dp" android:paddingTop="2dp"
android:paddingBottom="2dp"> android:paddingBottom="2dp">
<ProgressBar
android:id="@+id/with_art_buffering"
style="?android:attr/progressBarStyleSmall"
android:layout_marginTop="2dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
<TextView <TextView
style="@style/LightDropShadow" style="@style/LightDropShadow"
android:id="@+id/with_art_track_title" android:id="@+id/with_art_track_title"
@ -103,6 +111,14 @@
android:paddingTop="6dp" android:paddingTop="6dp"
android:paddingBottom="6dp"> android:paddingBottom="6dp">
<ProgressBar
android:id="@+id/buffering"
style="?android:attr/progressBarStyleSmall"
android:layout_marginTop="2dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
<TextView <TextView
android:id="@+id/track_title" android:id="@+id/track_title"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@ -51,15 +51,42 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="2dp"/> android:layout_height="2dp"/>
<TextView <FrameLayout
style="@style/TransportPlaying" android:id="@+id/title_bar"
android:id="@+id/track_title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:background="@drawable/category_button"
android:maxLines="1" android:paddingLeft="4dp"
android:ellipsize="end" android:paddingRight="4dp">
android:textColor="@color/theme_disabled_foreground"
android:text="@string/transport_not_playing"/> <LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal">
<ProgressBar
android:id="@+id/buffering"
style="?android:attr/progressBarStyleSmall"
android:paddingRight="6dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
<TextView
style="@style/TransportPlaying"
android:id="@+id/track_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:maxLines="1"
android:ellipsize="end"
android:textColor="@color/theme_disabled_foreground"
android:text="@string/transport_not_playing"/>
</LinearLayout>
</FrameLayout>
</LinearLayout> </LinearLayout>

View File

@ -68,4 +68,5 @@
<string name="unknown_artist">[unknown artist]</string> <string name="unknown_artist">[unknown artist]</string>
<string name="unknown_album">[unknown album]</string> <string name="unknown_album">[unknown album]</string>
<string name="unknown_title">[unknown title]</string> <string name="unknown_title">[unknown title]</string>
<string name="buffering">buffering</string>
</resources> </resources>

View File

@ -18,13 +18,10 @@
<style name="TransportPlaying"> <style name="TransportPlaying">
<item name="android:layout_margin">0dp</item> <item name="android:layout_margin">0dp</item>
<item name="android:paddingLeft">4dp</item>
<item name="android:paddingRight">4dp</item>
<item name="android:paddingTop">6dp</item> <item name="android:paddingTop">6dp</item>
<item name="android:paddingBottom">6dp</item> <item name="android:paddingBottom">6dp</item>
<item name="android:gravity">center</item> <item name="android:gravity">center</item>
<item name="android:textColor">@color/theme_foreground</item> <item name="android:textColor">@color/theme_foreground</item>
<item name="android:background">@drawable/category_button</item>
<item name="android:clickable">true</item> <item name="android:clickable">true</item>
</style> </style>