mirror of
https://github.com/clangen/musikcube.git
synced 2025-01-29 21:32:41 +00:00
Refactored and isolated all of the gross metadata and album art handling
code from MainActivity into MainMetadataView.
This commit is contained in:
parent
9cf6cffef5
commit
3f36c8e61b
@ -3,36 +3,17 @@ package io.casey.musikcube.remote;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextPaint;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewPropertyAnimator;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
|
||||
import com.bumptech.glide.request.RequestListener;
|
||||
import com.bumptech.glide.request.target.Target;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.casey.musikcube.remote.playback.Metadata;
|
||||
import io.casey.musikcube.remote.playback.PlaybackService;
|
||||
import io.casey.musikcube.remote.playback.PlaybackState;
|
||||
import io.casey.musikcube.remote.playback.RepeatMode;
|
||||
@ -43,10 +24,9 @@ import io.casey.musikcube.remote.ui.activity.SettingsActivity;
|
||||
import io.casey.musikcube.remote.ui.activity.TrackListActivity;
|
||||
import io.casey.musikcube.remote.ui.activity.WebSocketActivityBase;
|
||||
import io.casey.musikcube.remote.ui.fragment.InvalidPasswordDialogFragment;
|
||||
import io.casey.musikcube.remote.ui.model.AlbumArtModel;
|
||||
import io.casey.musikcube.remote.ui.util.Views;
|
||||
import io.casey.musikcube.remote.ui.view.LongPressTextView;
|
||||
import io.casey.musikcube.remote.util.Strings;
|
||||
import io.casey.musikcube.remote.ui.view.MainMetadataView;
|
||||
import io.casey.musikcube.remote.websocket.Messages;
|
||||
import io.casey.musikcube.remote.websocket.Prefs;
|
||||
import io.casey.musikcube.remote.websocket.SocketMessage;
|
||||
@ -59,25 +39,12 @@ public class MainActivity extends WebSocketActivityBase {
|
||||
|
||||
private SharedPreferences prefs;
|
||||
private PlaybackService playback;
|
||||
private Handler handler = new Handler();
|
||||
|
||||
private TextView title, artist, album, playPause, volume;
|
||||
private TextView titleWithArt, artistAndAlbumWithArt, volumeWithArt;
|
||||
private View mainTrackMetadataWithAlbumArt, mainTrackMetadataNoAlbumArt;
|
||||
private TextView notPlayingOrDisconnected;
|
||||
private View buffering, bufferingWithArt;
|
||||
private View connected;
|
||||
private MainMetadataView metadataView;
|
||||
private TextView playPause;
|
||||
private View connectedNotPlaying, disconnectedButton;
|
||||
private CheckBox shuffleCb, muteCb, repeatCb;
|
||||
private View disconnectedOverlay;
|
||||
private ImageView albumArtImageView;
|
||||
|
||||
private ViewPropertyAnimator metadataAnim1, metadataAnim2;
|
||||
|
||||
/* ugh, artwork related */
|
||||
private enum DisplayMode { Artwork, NoArtwork, Stopped }
|
||||
private AlbumArtModel albumArtModel = AlbumArtModel.empty();
|
||||
private DisplayMode lastDisplayMode = DisplayMode.Stopped;
|
||||
private String lastArtworkUrl = null;
|
||||
|
||||
static {
|
||||
REPEAT_TO_STRING_ID = new HashMap<>();
|
||||
@ -110,6 +77,7 @@ public class MainActivity extends WebSocketActivityBase {
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
metadataView.onPause();
|
||||
unbindCheckboxEventListeners();
|
||||
}
|
||||
|
||||
@ -117,6 +85,7 @@ public class MainActivity extends WebSocketActivityBase {
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
this.playback = getPlaybackService();
|
||||
metadataView.onResume(playback);
|
||||
bindCheckBoxEventListeners();
|
||||
rebindUi();
|
||||
}
|
||||
@ -185,32 +154,14 @@ public class MainActivity extends WebSocketActivityBase {
|
||||
}
|
||||
|
||||
private void bindEventListeners() {
|
||||
this.title = (TextView) findViewById(R.id.track_title);
|
||||
this.artist = (TextView) findViewById(R.id.track_artist);
|
||||
this.album = (TextView) findViewById(R.id.track_album);
|
||||
this.volume = (TextView) findViewById(R.id.volume);
|
||||
this.buffering = findViewById(R.id.buffering);
|
||||
|
||||
this.titleWithArt = (TextView) findViewById(R.id.with_art_track_title);
|
||||
this.artistAndAlbumWithArt = (TextView) findViewById(R.id.with_art_artist_and_album);
|
||||
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.metadataView = (MainMetadataView) findViewById(R.id.main_metadata_view);
|
||||
this.shuffleCb = (CheckBox) findViewById(R.id.check_shuffle);
|
||||
this.muteCb = (CheckBox) findViewById(R.id.check_mute);
|
||||
this.repeatCb = (CheckBox) findViewById(R.id.check_repeat);
|
||||
this.mainTrackMetadataWithAlbumArt = findViewById(R.id.main_track_metadata_with_art);
|
||||
this.mainTrackMetadataNoAlbumArt = findViewById(R.id.main_track_metadata_without_art);
|
||||
this.notPlayingOrDisconnected = (TextView) findViewById(R.id.main_not_playing);
|
||||
this.albumArtImageView = (ImageView) findViewById(R.id.album_art);
|
||||
this.connected = findViewById(R.id.connected);
|
||||
|
||||
this.connectedNotPlaying = findViewById(R.id.connected_not_playing);
|
||||
this.disconnectedButton = findViewById(R.id.disconnected);
|
||||
this.disconnectedOverlay = findViewById(R.id.disconnected_overlay);
|
||||
|
||||
/* these will get faded in as appropriate */
|
||||
this.mainTrackMetadataNoAlbumArt.setAlpha(0.0f);
|
||||
this.mainTrackMetadataWithAlbumArt.setAlpha(0.0f);
|
||||
this.playPause = (TextView) findViewById(R.id.button_play_pause);
|
||||
|
||||
findViewById(R.id.button_prev).setOnClickListener((View view) -> playback.prev());
|
||||
|
||||
@ -237,10 +188,8 @@ public class MainActivity extends WebSocketActivityBase {
|
||||
final LongPressTextView volumeDown = (LongPressTextView) findViewById(R.id.button_vol_down);
|
||||
volumeDown.setOnTickListener((View view) -> playback.volumeDown());
|
||||
|
||||
notPlayingOrDisconnected.setOnClickListener((view) -> {
|
||||
if (wss.getState() != WebSocketService.State.Connected) {
|
||||
wss.reconnect();
|
||||
}
|
||||
disconnectedButton.setOnClickListener((view) -> {
|
||||
wss.reconnect();
|
||||
});
|
||||
|
||||
findViewById(R.id.button_artists).setOnClickListener((View view) -> {
|
||||
@ -266,61 +215,6 @@ public class MainActivity extends WebSocketActivityBase {
|
||||
disconnectedOverlay.setOnClickListener((view) -> {
|
||||
/* swallow input so user can't click on things while disconnected */
|
||||
});
|
||||
|
||||
this.album.setOnClickListener((view) -> navigateToCurrentAlbum());
|
||||
this.artist.setOnClickListener((view) -> navigateToCurrentArtist());
|
||||
}
|
||||
|
||||
private void rebindAlbumArtistWithArtTextView() {
|
||||
final boolean buffering = playback.getPlaybackState() == PlaybackState.Buffering;
|
||||
|
||||
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 =
|
||||
new ForegroundColorSpan(getResources().getColor(R.color.theme_orange));
|
||||
|
||||
final ForegroundColorSpan artistColor =
|
||||
new ForegroundColorSpan(getResources().getColor(R.color.theme_yellow));
|
||||
|
||||
final SpannableStringBuilder builder =
|
||||
new SpannableStringBuilder().append(album).append(" - ").append(artist);
|
||||
|
||||
final ClickableSpan albumClickable = new ClickableSpan() {
|
||||
@Override
|
||||
public void onClick(View widget) {
|
||||
navigateToCurrentAlbum();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDrawState(TextPaint ds) {
|
||||
}
|
||||
};
|
||||
|
||||
final ClickableSpan artistClickable = new ClickableSpan() {
|
||||
@Override
|
||||
public void onClick(View widget) {
|
||||
navigateToCurrentArtist();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDrawState(TextPaint ds) {
|
||||
}
|
||||
};
|
||||
|
||||
int artistOffset = album.length() + 3;
|
||||
|
||||
builder.setSpan(albumColor, 0, album.length(), 0);
|
||||
builder.setSpan(albumClickable, 0, album.length(), 0);
|
||||
builder.setSpan(artistColor, artistOffset, artistOffset + artist.length(), 0);
|
||||
builder.setSpan(artistClickable, artistOffset, artistOffset + artist.length(), 0);
|
||||
|
||||
this.artistAndAlbumWithArt.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
this.artistAndAlbumWithArt.setHighlightColor(Color.TRANSPARENT);
|
||||
this.artistAndAlbumWithArt.setText(builder);
|
||||
}
|
||||
|
||||
private void rebindUi() {
|
||||
@ -328,238 +222,56 @@ public class MainActivity extends WebSocketActivityBase {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
/* state management for UI stuff is starting to get out of hand. we should
|
||||
refactor things pretty soon before they're completely out of control */
|
||||
|
||||
final boolean streaming = prefs.getBoolean(
|
||||
Prefs.Key.STREAMING_PLAYBACK, Prefs.Default.STREAMING_PLAYBACK);
|
||||
|
||||
final WebSocketService.State state = wss.getState();
|
||||
|
||||
final boolean connected = state == WebSocketService.State.Connected;
|
||||
|
||||
final boolean streaming = prefs.getBoolean(Prefs.Key.STREAMING_PLAYBACK, Prefs.Default.STREAMING_PLAYBACK);
|
||||
final boolean connected = (wss.getState() == WebSocketService.State.Connected);
|
||||
final boolean stopped = (playback.getPlaybackState() == PlaybackState.Stopped);
|
||||
final boolean playing = (playback.getPlaybackState() == PlaybackState.Playing);
|
||||
final boolean showMetadataView = !stopped && connected && playback.getQueueCount() > 0;
|
||||
|
||||
/* bottom section: transport controls */
|
||||
this.playPause.setText(playing ? R.string.button_pause : R.string.button_play);
|
||||
|
||||
final boolean stopped = (playback.getPlaybackState() == PlaybackState.Stopped);
|
||||
notPlayingOrDisconnected.setVisibility(!connected || stopped ? View.VISIBLE : View.GONE);
|
||||
|
||||
boolean buffering = playback.getPlaybackState() == PlaybackState.Buffering;
|
||||
|
||||
final boolean stateIsValidForArtwork = !stopped && connected && playback.getQueueCount() > 0;
|
||||
|
||||
this.connected.setVisibility((connected && stopped) ? View.VISIBLE : View.GONE);
|
||||
this.connectedNotPlaying.setVisibility((connected && stopped) ? View.VISIBLE : View.GONE);
|
||||
this.disconnectedOverlay.setVisibility(connected ? View.GONE : View.VISIBLE);
|
||||
|
||||
/* setup our state as if we have no album art -- because we don't know if we have any
|
||||
yet! the album art load process (if enabled) will ensure the correct metadata block
|
||||
is displayed in the correct location */
|
||||
if (!stateIsValidForArtwork) {
|
||||
setMetadataDisplayMode(DisplayMode.Stopped);
|
||||
notPlayingOrDisconnected.setText(connected ? R.string.transport_not_playing : R.string.status_disconnected);
|
||||
notPlayingOrDisconnected.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else {
|
||||
notPlayingOrDisconnected.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
final String artist = playback.getTrackString(Metadata.Track.ARTIST, "");
|
||||
final String album = playback.getTrackString(Metadata.Track.ALBUM, "");
|
||||
final String title = playback.getTrackString(Metadata.Track.TITLE, "");
|
||||
final String volume = getString(R.string.status_volume, Math.round(playback.getVolume() * 100));
|
||||
|
||||
this.title.setText(Strings.empty(title) ? getString(buffering ? R.string.buffering : R.string.unknown_title) : title);
|
||||
this.artist.setText(Strings.empty(artist) ? getString(buffering ? R.string.buffering : R.string.unknown_artist) : artist);
|
||||
this.album.setText(Strings.empty(album) ? getString(buffering ? R.string.buffering : R.string.unknown_album) : album);
|
||||
this.volume.setText(volume);
|
||||
|
||||
this.rebindAlbumArtistWithArtTextView();
|
||||
this.titleWithArt.setText(Strings.empty(title) ? getString(buffering ? R.string.buffering : R.string.unknown_title) : title);
|
||||
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 boolean repeatChecked = (repeatMode != RepeatMode.None);
|
||||
repeatCb.setText(REPEAT_TO_STRING_ID.get(repeatMode));
|
||||
Views.setCheckWithoutEvent(repeatCb, repeatChecked, this.repeatListener);
|
||||
|
||||
this.shuffleCb.setText(streaming ? R.string.button_random : R.string.button_shuffle);
|
||||
|
||||
Views.setCheckWithoutEvent(this.shuffleCb, playback.isShuffled(), this.shuffleListener);
|
||||
|
||||
Views.setCheckWithoutEvent(this.muteCb, playback.isMuted(), this.muteListener);
|
||||
|
||||
boolean albumArtEnabledInSettings = this.prefs.getBoolean(
|
||||
Prefs.Key.ALBUM_ART_ENABLED, Prefs.Default.ALBUM_ART_ENABLED);
|
||||
/* middle section: connected, disconnected, and metadata views */
|
||||
connectedNotPlaying.setVisibility(View.GONE);
|
||||
disconnectedButton.setVisibility(View.GONE);
|
||||
|
||||
if (stateIsValidForArtwork) {
|
||||
if (!albumArtEnabledInSettings || Strings.empty(artist) || Strings.empty(album)) {
|
||||
this.albumArtModel = AlbumArtModel.empty();
|
||||
setMetadataDisplayMode(DisplayMode.NoArtwork);
|
||||
}
|
||||
else {
|
||||
if (!this.albumArtModel.is(artist, album)) {
|
||||
this.albumArtModel.destroy();
|
||||
if (!showMetadataView) {
|
||||
metadataView.hide();
|
||||
|
||||
this.albumArtModel = new AlbumArtModel(
|
||||
title, artist, album, AlbumArtModel.Size.Mega, albumArtRetrieved);
|
||||
}
|
||||
updateAlbumArt();
|
||||
if (!connected) {
|
||||
disconnectedButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else if (stopped) {
|
||||
connectedNotPlaying.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
else {
|
||||
metadataView.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
private void clearUi() {
|
||||
albumArtModel = AlbumArtModel.empty();
|
||||
updateAlbumArt();
|
||||
metadataView.clear();
|
||||
rebindUi();
|
||||
}
|
||||
|
||||
private void setMetadataDisplayMode(DisplayMode mode) {
|
||||
lastDisplayMode = mode;
|
||||
|
||||
if (metadataAnim1 != null) {
|
||||
metadataAnim1.cancel();
|
||||
metadataAnim2.cancel();
|
||||
}
|
||||
|
||||
if (mode == DisplayMode.Stopped) {
|
||||
albumArtImageView.setImageDrawable(null);
|
||||
metadataAnim1 = Views.animateAlpha(mainTrackMetadataWithAlbumArt, 0.0f);
|
||||
metadataAnim2 = Views.animateAlpha(mainTrackMetadataNoAlbumArt, 0.0f);
|
||||
}
|
||||
else if (mode == DisplayMode.Artwork) {
|
||||
metadataAnim1 = Views.animateAlpha(mainTrackMetadataWithAlbumArt, 1.0f);
|
||||
metadataAnim2 = Views.animateAlpha(mainTrackMetadataNoAlbumArt, 0.0f);
|
||||
}
|
||||
else {
|
||||
albumArtImageView.setImageDrawable(null);
|
||||
metadataAnim1 = Views.animateAlpha(mainTrackMetadataWithAlbumArt, 0.0f);
|
||||
metadataAnim2 = Views.animateAlpha(mainTrackMetadataNoAlbumArt, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
private void preloadNextImage() {
|
||||
final SocketMessage request = SocketMessage.Builder
|
||||
.request(Messages.Request.QueryPlayQueueTracks)
|
||||
.addOption(Messages.Key.OFFSET, this.playback.getQueuePosition() + 1)
|
||||
.addOption(Messages.Key.LIMIT, 1)
|
||||
.build();
|
||||
|
||||
this.wss.send(request, this.getWebSocketServiceClient(), (response) -> {
|
||||
final JSONArray data = response.getJsonArrayOption(Messages.Key.DATA, new JSONArray());
|
||||
if (data.length() > 0) {
|
||||
JSONObject track = data.optJSONObject(0);
|
||||
final String artist = track.optString(Metadata.Track.ARTIST, "");
|
||||
final String album = track.optString(Metadata.Track.ALBUM, "");
|
||||
|
||||
if (!albumArtModel.is(artist, album)) {
|
||||
new AlbumArtModel("", artist, album, AlbumArtModel.Size.Mega, (info, url) -> {
|
||||
int width = albumArtImageView.getWidth();
|
||||
int height = albumArtImageView.getHeight();
|
||||
Glide.with(MainActivity.this).load(url).downloadOnly(width, height);
|
||||
}).fetch();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateAlbumArt() {
|
||||
if (playback.getPlaybackState() == PlaybackState.Stopped) {
|
||||
setMetadataDisplayMode(DisplayMode.NoArtwork);
|
||||
}
|
||||
|
||||
final String url = albumArtModel.getUrl();
|
||||
|
||||
if (Strings.empty(url)) {
|
||||
this.lastArtworkUrl = null;
|
||||
albumArtModel.fetch();
|
||||
setMetadataDisplayMode(DisplayMode.NoArtwork);
|
||||
}
|
||||
else if (!url.equals(lastArtworkUrl) || lastDisplayMode == DisplayMode.Stopped) {
|
||||
final int loadId = albumArtModel.getId();
|
||||
this.lastArtworkUrl = url;
|
||||
|
||||
Glide.with(this)
|
||||
.load(url)
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||
.listener(new RequestListener<String, GlideDrawable>() {
|
||||
@Override
|
||||
public boolean onException(Exception e,
|
||||
String model,
|
||||
Target<GlideDrawable> target,
|
||||
boolean isFirstResource)
|
||||
{
|
||||
setMetadataDisplayMode(DisplayMode.NoArtwork);
|
||||
lastArtworkUrl = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onResourceReady(GlideDrawable resource,
|
||||
String model,
|
||||
Target<GlideDrawable> target,
|
||||
boolean isFromMemoryCache,
|
||||
boolean isFirstResource)
|
||||
{
|
||||
if (!isPaused()) {
|
||||
preloadNextImage();
|
||||
}
|
||||
|
||||
/* if the loadId doesn't match the current id, then the image was
|
||||
loaded for a different song. throw it away. */
|
||||
if (albumArtModel.getId() != loadId) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
setMetadataDisplayMode(DisplayMode.Artwork);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
})
|
||||
.into(albumArtImageView);
|
||||
}
|
||||
else {
|
||||
setMetadataDisplayMode(lastDisplayMode);
|
||||
}
|
||||
}
|
||||
|
||||
private void navigateToCurrentArtist() {
|
||||
final long artistId = playback.getTrackLong(Metadata.Track.ARTIST_ID, -1);
|
||||
if (artistId != -1) {
|
||||
final String artistName = playback.getTrackString(Metadata.Track.ARTIST, "");
|
||||
startActivity(AlbumBrowseActivity.getStartIntent(
|
||||
MainActivity.this, Messages.Category.ARTIST, artistId, artistName));
|
||||
}
|
||||
}
|
||||
|
||||
private void navigateToCurrentAlbum() {
|
||||
final long albumId = playback.getTrackLong(Metadata.Track.ALBUM_ID, -1);
|
||||
if (albumId != -1) {
|
||||
final String albumName = playback.getTrackString(Metadata.Track.ALBUM, "");
|
||||
startActivity(TrackListActivity.getStartIntent(
|
||||
MainActivity.this, Messages.Category.ALBUM, albumId, albumName));
|
||||
}
|
||||
}
|
||||
|
||||
private void navigateToPlayQueue() {
|
||||
startActivity(PlayQueueActivity.getStartIntent(MainActivity.this, playback.getQueuePosition()));
|
||||
}
|
||||
|
||||
private AlbumArtModel.AlbumArtCallback albumArtRetrieved = (model, url) -> {
|
||||
handler.post(() -> {
|
||||
if (model == albumArtModel) {
|
||||
if (Strings.empty(model.getUrl())) {
|
||||
setMetadataDisplayMode(DisplayMode.NoArtwork);
|
||||
}
|
||||
else {
|
||||
updateAlbumArt();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
private CheckBox.OnCheckedChangeListener muteListener =
|
||||
(CompoundButton compoundButton, boolean b) -> {
|
||||
if (b != playback.isMuted()) {
|
||||
|
@ -379,7 +379,7 @@ public class SystemService extends Service {
|
||||
return true;
|
||||
|
||||
case ACTION_NOTIFICATION_STOP:
|
||||
this.playback.pause();
|
||||
this.playback.stop();
|
||||
SystemService.shutdown();
|
||||
return true;
|
||||
}
|
||||
|
@ -0,0 +1,379 @@
|
||||
package io.casey.musikcube.remote.ui.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Color;
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.AttrRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextPaint;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewPropertyAnimator;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
|
||||
import com.bumptech.glide.request.RequestListener;
|
||||
import com.bumptech.glide.request.target.Target;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import io.casey.musikcube.remote.R;
|
||||
import io.casey.musikcube.remote.playback.Metadata;
|
||||
import io.casey.musikcube.remote.playback.PlaybackService;
|
||||
import io.casey.musikcube.remote.playback.PlaybackState;
|
||||
import io.casey.musikcube.remote.ui.activity.AlbumBrowseActivity;
|
||||
import io.casey.musikcube.remote.ui.activity.TrackListActivity;
|
||||
import io.casey.musikcube.remote.ui.model.AlbumArtModel;
|
||||
import io.casey.musikcube.remote.ui.util.Views;
|
||||
import io.casey.musikcube.remote.util.Strings;
|
||||
import io.casey.musikcube.remote.websocket.Messages;
|
||||
import io.casey.musikcube.remote.websocket.Prefs;
|
||||
import io.casey.musikcube.remote.websocket.SocketMessage;
|
||||
import io.casey.musikcube.remote.websocket.WebSocketService;
|
||||
|
||||
public class MainMetadataView extends FrameLayout {
|
||||
private Handler handler = new Handler();
|
||||
private PlaybackService playback;
|
||||
private WebSocketService wss = null;
|
||||
private SharedPreferences prefs;
|
||||
|
||||
private boolean isPaused = true;
|
||||
|
||||
private TextView title, artist, album, volume;
|
||||
private TextView titleWithArt, artistAndAlbumWithArt, volumeWithArt;
|
||||
private View mainTrackMetadataWithAlbumArt, mainTrackMetadataNoAlbumArt;
|
||||
private View buffering, bufferingWithArt;
|
||||
private ImageView albumArtImageView;
|
||||
|
||||
private ViewPropertyAnimator metadataAnim1, metadataAnim2;
|
||||
private enum DisplayMode { Artwork, NoArtwork, Stopped }
|
||||
private AlbumArtModel albumArtModel = AlbumArtModel.empty();
|
||||
private DisplayMode lastDisplayMode = DisplayMode.Stopped;
|
||||
private String lastArtworkUrl = null;
|
||||
|
||||
public MainMetadataView(@NonNull Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public MainMetadataView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public MainMetadataView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init();
|
||||
}
|
||||
|
||||
public void onResume(final PlaybackService playback) {
|
||||
this.wss.addClient(wssClient);
|
||||
this.playback = playback;
|
||||
isPaused = false;
|
||||
}
|
||||
|
||||
public void onPause() {
|
||||
this.wss.removeClient(wssClient);
|
||||
isPaused = true;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
albumArtModel = AlbumArtModel.empty();
|
||||
updateAlbumArt();
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
setVisibility(View.VISIBLE);
|
||||
|
||||
final boolean buffering = playback.getPlaybackState() == PlaybackState.Buffering;
|
||||
|
||||
final String artist = playback.getTrackString(Metadata.Track.ARTIST, "");
|
||||
final String album = playback.getTrackString(Metadata.Track.ALBUM, "");
|
||||
final String title = playback.getTrackString(Metadata.Track.TITLE, "");
|
||||
final String volume = getString(R.string.status_volume, Math.round(playback.getVolume() * 100));
|
||||
|
||||
this.title.setText(Strings.empty(title) ? getString(buffering ? R.string.buffering : R.string.unknown_title) : title);
|
||||
this.artist.setText(Strings.empty(artist) ? getString(buffering ? R.string.buffering : R.string.unknown_artist) : artist);
|
||||
this.album.setText(Strings.empty(album) ? getString(buffering ? R.string.buffering : R.string.unknown_album) : album);
|
||||
this.volume.setText(volume);
|
||||
|
||||
this.rebindAlbumArtistWithArtTextView();
|
||||
this.titleWithArt.setText(Strings.empty(title) ? getString(buffering ? R.string.buffering : R.string.unknown_title) : title);
|
||||
this.volumeWithArt.setText(volume);
|
||||
|
||||
this.buffering.setVisibility(buffering ? View.VISIBLE : View.GONE);
|
||||
this.bufferingWithArt.setVisibility(buffering ? View.VISIBLE : View.GONE);
|
||||
|
||||
boolean albumArtEnabledInSettings = this.prefs.getBoolean(
|
||||
Prefs.Key.ALBUM_ART_ENABLED, Prefs.Default.ALBUM_ART_ENABLED);
|
||||
|
||||
if (!albumArtEnabledInSettings || Strings.empty(artist) || Strings.empty(album)) {
|
||||
this.albumArtModel = AlbumArtModel.empty();
|
||||
setMetadataDisplayMode(DisplayMode.NoArtwork);
|
||||
}
|
||||
else {
|
||||
if (!this.albumArtModel.is(artist, album)) {
|
||||
this.albumArtModel.destroy();
|
||||
|
||||
this.albumArtModel = new AlbumArtModel(
|
||||
title, artist, album, AlbumArtModel.Size.Mega, albumArtRetrieved);
|
||||
}
|
||||
updateAlbumArt();
|
||||
}
|
||||
}
|
||||
|
||||
private String getString(int resId) {
|
||||
return getContext().getString(resId);
|
||||
}
|
||||
|
||||
private String getString(int resId, Object... args) {
|
||||
return getContext().getString(resId, args);
|
||||
}
|
||||
|
||||
private void setMetadataDisplayMode(DisplayMode mode) {
|
||||
lastDisplayMode = mode;
|
||||
|
||||
if (metadataAnim1 != null) {
|
||||
metadataAnim1.cancel();
|
||||
metadataAnim2.cancel();
|
||||
}
|
||||
|
||||
if (mode == DisplayMode.Stopped) {
|
||||
albumArtImageView.setImageDrawable(null);
|
||||
metadataAnim1 = Views.animateAlpha(mainTrackMetadataWithAlbumArt, 0.0f);
|
||||
metadataAnim2 = Views.animateAlpha(mainTrackMetadataNoAlbumArt, 0.0f);
|
||||
}
|
||||
else if (mode == DisplayMode.Artwork) {
|
||||
metadataAnim1 = Views.animateAlpha(mainTrackMetadataWithAlbumArt, 1.0f);
|
||||
metadataAnim2 = Views.animateAlpha(mainTrackMetadataNoAlbumArt, 0.0f);
|
||||
}
|
||||
else {
|
||||
albumArtImageView.setImageDrawable(null);
|
||||
metadataAnim1 = Views.animateAlpha(mainTrackMetadataWithAlbumArt, 0.0f);
|
||||
metadataAnim2 = Views.animateAlpha(mainTrackMetadataNoAlbumArt, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
private void rebindAlbumArtistWithArtTextView() {
|
||||
final boolean buffering = playback.getPlaybackState() == PlaybackState.Buffering;
|
||||
|
||||
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 =
|
||||
new ForegroundColorSpan(getResources().getColor(R.color.theme_orange));
|
||||
|
||||
final ForegroundColorSpan artistColor =
|
||||
new ForegroundColorSpan(getResources().getColor(R.color.theme_yellow));
|
||||
|
||||
final SpannableStringBuilder builder =
|
||||
new SpannableStringBuilder().append(album).append(" - ").append(artist);
|
||||
|
||||
final ClickableSpan albumClickable = new ClickableSpan() {
|
||||
@Override
|
||||
public void onClick(View widget) {
|
||||
navigateToCurrentAlbum();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDrawState(TextPaint ds) {
|
||||
}
|
||||
};
|
||||
|
||||
final ClickableSpan artistClickable = new ClickableSpan() {
|
||||
@Override
|
||||
public void onClick(View widget) {
|
||||
navigateToCurrentArtist();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDrawState(TextPaint ds) {
|
||||
}
|
||||
};
|
||||
|
||||
int artistOffset = album.length() + 3;
|
||||
|
||||
builder.setSpan(albumColor, 0, album.length(), 0);
|
||||
builder.setSpan(albumClickable, 0, album.length(), 0);
|
||||
builder.setSpan(artistColor, artistOffset, artistOffset + artist.length(), 0);
|
||||
builder.setSpan(artistClickable, artistOffset, artistOffset + artist.length(), 0);
|
||||
|
||||
this.artistAndAlbumWithArt.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
this.artistAndAlbumWithArt.setHighlightColor(Color.TRANSPARENT);
|
||||
this.artistAndAlbumWithArt.setText(builder);
|
||||
}
|
||||
|
||||
private void updateAlbumArt() {
|
||||
if (playback.getPlaybackState() == PlaybackState.Stopped) {
|
||||
setMetadataDisplayMode(DisplayMode.NoArtwork);
|
||||
}
|
||||
|
||||
final String url = albumArtModel.getUrl();
|
||||
|
||||
if (Strings.empty(url)) {
|
||||
this.lastArtworkUrl = null;
|
||||
albumArtModel.fetch();
|
||||
setMetadataDisplayMode(DisplayMode.NoArtwork);
|
||||
}
|
||||
else if (!url.equals(lastArtworkUrl) || lastDisplayMode == DisplayMode.Stopped) {
|
||||
final int loadId = albumArtModel.getId();
|
||||
this.lastArtworkUrl = url;
|
||||
|
||||
Glide.with(getContext())
|
||||
.load(url)
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||
.listener(new RequestListener<String, GlideDrawable>() {
|
||||
@Override
|
||||
public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean first) {
|
||||
setMetadataDisplayMode(DisplayMode.NoArtwork);
|
||||
lastArtworkUrl = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean memory, boolean first) {
|
||||
if (!isPaused) {
|
||||
preloadNextImage();
|
||||
}
|
||||
|
||||
/* if the loadId doesn't match the current id, then the image was
|
||||
loaded for a different song. throw it away. */
|
||||
if (albumArtModel.getId() != loadId) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
setMetadataDisplayMode(DisplayMode.Artwork);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
})
|
||||
.into(albumArtImageView);
|
||||
}
|
||||
else {
|
||||
setMetadataDisplayMode(lastDisplayMode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void preloadNextImage() {
|
||||
final SocketMessage request = SocketMessage.Builder
|
||||
.request(Messages.Request.QueryPlayQueueTracks)
|
||||
.addOption(Messages.Key.OFFSET, this.playback.getQueuePosition() + 1)
|
||||
.addOption(Messages.Key.LIMIT, 1)
|
||||
.build();
|
||||
|
||||
this.wss.send(request, wssClient, (response) -> {
|
||||
final JSONArray data = response.getJsonArrayOption(Messages.Key.DATA, new JSONArray());
|
||||
if (data.length() > 0) {
|
||||
JSONObject track = data.optJSONObject(0);
|
||||
final String artist = track.optString(Metadata.Track.ARTIST, "");
|
||||
final String album = track.optString(Metadata.Track.ALBUM, "");
|
||||
|
||||
if (!albumArtModel.is(artist, album)) {
|
||||
new AlbumArtModel("", artist, album, AlbumArtModel.Size.Mega, (info, url) -> {
|
||||
int width = albumArtImageView.getWidth();
|
||||
int height = albumArtImageView.getHeight();
|
||||
Glide.with(getContext()).load(url).downloadOnly(width, height);
|
||||
}).fetch();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void init() {
|
||||
this.prefs = getContext().getSharedPreferences(Prefs.NAME, Context.MODE_PRIVATE);
|
||||
this.wss = WebSocketService.getInstance(getContext());
|
||||
|
||||
final View child = LayoutInflater.from(getContext())
|
||||
.inflate(R.layout.main_metadata, this, false);
|
||||
|
||||
addView(child, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
|
||||
this.title = (TextView) findViewById(R.id.track_title);
|
||||
this.artist = (TextView) findViewById(R.id.track_artist);
|
||||
this.album = (TextView) findViewById(R.id.track_album);
|
||||
this.volume = (TextView) findViewById(R.id.volume);
|
||||
this.buffering = findViewById(R.id.buffering);
|
||||
|
||||
this.titleWithArt = (TextView) findViewById(R.id.with_art_track_title);
|
||||
this.artistAndAlbumWithArt = (TextView) findViewById(R.id.with_art_artist_and_album);
|
||||
this.volumeWithArt = (TextView) findViewById(R.id.with_art_volume);
|
||||
this.bufferingWithArt = findViewById(R.id.with_art_buffering);
|
||||
|
||||
this.mainTrackMetadataWithAlbumArt = findViewById(R.id.main_track_metadata_with_art);
|
||||
this.mainTrackMetadataNoAlbumArt = findViewById(R.id.main_track_metadata_without_art);
|
||||
this.albumArtImageView = (ImageView) findViewById(R.id.album_art);
|
||||
|
||||
/* these will get faded in as appropriate */
|
||||
this.mainTrackMetadataNoAlbumArt.setAlpha(0.0f);
|
||||
this.mainTrackMetadataWithAlbumArt.setAlpha(0.0f);
|
||||
|
||||
this.album.setOnClickListener((view) -> navigateToCurrentAlbum());
|
||||
this.artist.setOnClickListener((view) -> navigateToCurrentArtist());
|
||||
}
|
||||
|
||||
private void navigateToCurrentArtist() {
|
||||
final long artistId = playback.getTrackLong(Metadata.Track.ARTIST_ID, -1);
|
||||
if (artistId != -1) {
|
||||
final String artistName = playback.getTrackString(Metadata.Track.ARTIST, "");
|
||||
getContext().startActivity(AlbumBrowseActivity.getStartIntent(
|
||||
getContext(), Messages.Category.ARTIST, artistId, artistName));
|
||||
}
|
||||
}
|
||||
|
||||
private void navigateToCurrentAlbum() {
|
||||
final long albumId = playback.getTrackLong(Metadata.Track.ALBUM_ID, -1);
|
||||
if (albumId != -1) {
|
||||
final String albumName = playback.getTrackString(Metadata.Track.ALBUM, "");
|
||||
getContext().startActivity(TrackListActivity.getStartIntent(
|
||||
getContext(), Messages.Category.ALBUM, albumId, albumName));
|
||||
}
|
||||
}
|
||||
|
||||
private WebSocketService.Client wssClient = new WebSocketService.Client() {
|
||||
@Override
|
||||
public void onStateChanged(WebSocketService.State newState, WebSocketService.State oldState) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageReceived(SocketMessage message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInvalidPassword() {
|
||||
}
|
||||
};
|
||||
|
||||
private AlbumArtModel.AlbumArtCallback albumArtRetrieved = (model, url) -> {
|
||||
handler.post(() -> {
|
||||
if (model == albumArtModel) {
|
||||
if (Strings.empty(model.getUrl())) {
|
||||
setMetadataDisplayMode(DisplayMode.NoArtwork);
|
||||
}
|
||||
else {
|
||||
updateAlbumArt();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
@ -9,157 +9,51 @@
|
||||
tools:context="io.casey.musikcube.remote.MainActivity">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/metadata_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_above="@+id/play_controls">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/album_art"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/connected_not_playing"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/connected"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:textColor="@color/theme_green"
|
||||
android:text="connected"/>
|
||||
android:text="@string/button_connected"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/main_not_playing"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:textSize="@dimen/text_size_large"
|
||||
android:textColor="@color/theme_disabled_foreground"
|
||||
android:padding="8dp"
|
||||
android:clickable="true"
|
||||
android:background="@drawable/not_playing_button"
|
||||
android:text="@string/transport_not_playing"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/main_track_metadata_with_art"
|
||||
android:background="@color/main_playback_metadata"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="24dp"
|
||||
android:paddingRight="24dp"
|
||||
android:paddingTop="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
|
||||
style="@style/LightDropShadow"
|
||||
android:id="@+id/with_art_track_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="center"
|
||||
android:textColor="@color/theme_green"
|
||||
android:text="-"/>
|
||||
|
||||
<TextView
|
||||
style="@style/LightDropShadow"
|
||||
android:id="@+id/with_art_artist_and_album"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="center"
|
||||
android:textColor="@color/theme_yellow"
|
||||
android:text="-"/>
|
||||
|
||||
<TextView
|
||||
style="@style/LightDropShadow"
|
||||
android:id="@+id/with_art_volume"
|
||||
android:textSize="@dimen/text_size_small"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:text=""/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/main_track_metadata_without_art"
|
||||
android:layout_width="match_parent"
|
||||
<TextView
|
||||
android:id="@+id/disconnected"
|
||||
android:visibility="gone"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="24dp"
|
||||
android:paddingRight="24dp"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingBottom="6dp">
|
||||
android:textSize="@dimen/text_size_large"
|
||||
android:textColor="@color/theme_disabled_foreground"
|
||||
android:padding="8dp"
|
||||
android:clickable="true"
|
||||
android:background="@drawable/not_playing_button"
|
||||
android:text="@string/status_disconnected"/>
|
||||
|
||||
<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
|
||||
android:id="@+id/track_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="center"
|
||||
android:textColor="@color/theme_green"
|
||||
android:textSize="@dimen/text_size_xxlarge"
|
||||
android:text="-"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/track_album"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="center"
|
||||
android:textColor="@color/theme_orange"
|
||||
android:textSize="@dimen/text_size_xlarge"
|
||||
android:text="-"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/track_artist"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="2dp"
|
||||
android:textColor="@color/theme_yellow"
|
||||
android:textSize="@dimen/text_size_large"
|
||||
android:text="-"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/volume"
|
||||
android:textSize="@dimen/text_size_small"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="center"
|
||||
android:text=""/>
|
||||
</LinearLayout>
|
||||
<io.casey.musikcube.remote.ui.view.MainMetadataView
|
||||
android:id="@+id/main_metadata_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
|
127
src/musikdroid/app/src/main/res/layout/main_metadata.xml
Normal file
127
src/musikdroid/app/src/main/res/layout/main_metadata.xml
Normal file
@ -0,0 +1,127 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/metadata_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/album_art"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/main_track_metadata_with_art"
|
||||
android:background="@color/main_playback_metadata"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="24dp"
|
||||
android:paddingRight="24dp"
|
||||
android:paddingTop="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
|
||||
style="@style/LightDropShadow"
|
||||
android:id="@+id/with_art_track_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="center"
|
||||
android:textColor="@color/theme_green"
|
||||
android:text="-"/>
|
||||
|
||||
<TextView
|
||||
style="@style/LightDropShadow"
|
||||
android:id="@+id/with_art_artist_and_album"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="center"
|
||||
android:textColor="@color/theme_yellow"
|
||||
android:text="-"/>
|
||||
|
||||
<TextView
|
||||
style="@style/LightDropShadow"
|
||||
android:id="@+id/with_art_volume"
|
||||
android:textSize="@dimen/text_size_small"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:text=""/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/main_track_metadata_without_art"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="24dp"
|
||||
android:paddingRight="24dp"
|
||||
android:paddingTop="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
|
||||
android:id="@+id/track_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="center"
|
||||
android:textColor="@color/theme_green"
|
||||
android:textSize="@dimen/text_size_xxlarge"
|
||||
android:text="-"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/track_album"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="center"
|
||||
android:textColor="@color/theme_orange"
|
||||
android:textSize="@dimen/text_size_xlarge"
|
||||
android:text="-"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/track_artist"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="2dp"
|
||||
android:textColor="@color/theme_yellow"
|
||||
android:textSize="@dimen/text_size_large"
|
||||
android:text="-"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/volume"
|
||||
android:textSize="@dimen/text_size_small"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="center"
|
||||
android:text=""/>
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
@ -41,6 +41,7 @@
|
||||
<string name="status_disconnected">disconnected</string>
|
||||
<string name="status_connected">connected to %1$s</string>
|
||||
<string name="status_volume">volume %1d%%</string>
|
||||
<string name="button_connected">connected</string>
|
||||
<string name="edit_connection_info">connection info:</string>
|
||||
<string name="edit_connection_hostname">ip address or hostname</string>
|
||||
<string name="edit_connection_port">main server port (default 7905)</string>
|
||||
|
Loading…
x
Reference in New Issue
Block a user