Implemented a slideup panel with media controls and info (#320)
* The slideup panel is only displayed when something is playing. It starts collapsed showing the media poster and title of what is currently playing. * Media controls implemented are volume, progress, shuffle, repeat and play/pause for all items. Next and previous are only available when a music item is playing. * In collapsed mode the panel will display the mute button only if Kodi is muted. The mute button in expanded mode is always visible. * Panel is enabled by default. Users can disable the panel in Settings * Implemented listening to Player.OnPropertyChanged notifications to update shuffle and repeat button states.
This commit is contained in:
parent
7186874471
commit
cb430aa20d
|
@ -42,6 +42,7 @@ Credits
|
|||
- [PagerSlidingTabStrip](https://github.com/astuetz/PagerSlidingTabStrip)
|
||||
- [FloatingActionButton](https://github.com/makovkastar/FloatingActionButton)
|
||||
- [ExpandableTextView](https://github.com/Blogcat/Android-ExpandableTextView)
|
||||
- [AndroidSlidingUpPanel](https://github.com/umano/AndroidSlidingUpPanel)
|
||||
|
||||
Links
|
||||
-----
|
||||
|
|
|
@ -125,6 +125,7 @@ dependencies {
|
|||
compile 'com.astuetz:pagerslidingtabstrip:1.0.1'
|
||||
compile 'com.melnykov:floatingactionbutton:1.3.0'
|
||||
compile 'at.blogc:expandabletextview:1.0.3'
|
||||
compile 'com.sothree.slidinguppanel:library:3.3.1'
|
||||
|
||||
androidTestCompile 'com.android.support.test:runner:0.5'
|
||||
androidTestCompile 'com.android.support.test:rules:0.5'
|
||||
|
|
|
@ -77,6 +77,10 @@ public class Settings {
|
|||
public static final String KEY_PREF_SHOW_NOTIFICATION = "pref_show_notification";
|
||||
public static final boolean DEFAULT_PREF_SHOW_NOTIFICATION = false;
|
||||
|
||||
// Show now playing panel
|
||||
public static final String KEY_PREF_SHOW_NOW_PLAYING_PANEL = "pref_show_nowplayingpanel";
|
||||
public static final boolean DEFAULT_PREF_SHOW_NOW_PLAYING_PANEL = true;
|
||||
|
||||
// Pause during calls
|
||||
public static final String KEY_PREF_PAUSE_DURING_CALLS = "pref_pause_during_calls";
|
||||
public static final boolean DEFAULT_PREF_PAUSE_DURING_CALLS = false;
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.xbmc.kore.jsonrpc.HostConnection;
|
|||
import org.xbmc.kore.jsonrpc.method.JSONRPC;
|
||||
import org.xbmc.kore.jsonrpc.method.Player;
|
||||
import org.xbmc.kore.jsonrpc.notification.Application;
|
||||
import org.xbmc.kore.jsonrpc.notification.Player.NotificationsData;
|
||||
import org.xbmc.kore.jsonrpc.notification.Input;
|
||||
import org.xbmc.kore.jsonrpc.notification.System;
|
||||
import org.xbmc.kore.jsonrpc.type.ApplicationType;
|
||||
|
@ -73,6 +74,8 @@ public class HostConnectionObserver
|
|||
PLAYER_IS_PAUSED = 3,
|
||||
PLAYER_IS_STOPPED = 4;
|
||||
|
||||
public void playerOnPropertyChanged(NotificationsData notificationsData);
|
||||
|
||||
/**
|
||||
* Notifies that something is playing
|
||||
* @param getActivePlayerResult Active player obtained by a call to {@link org.xbmc.kore.jsonrpc.method.Player.GetActivePlayers}
|
||||
|
@ -178,15 +181,17 @@ public class HostConnectionObserver
|
|||
final int PING_AFTER_ERROR_CHECK_INTERVAL = 2000,
|
||||
PING_AFTER_SUCCESS_CHECK_INTERVAL = 10000;
|
||||
// If no one is listening to this, just exit
|
||||
if (playerEventsObservers.isEmpty()) return;
|
||||
if (playerEventsObservers.isEmpty() && applicationEventsObservers.isEmpty()) return;
|
||||
|
||||
JSONRPC.Ping ping = new JSONRPC.Ping();
|
||||
ping.execute(connection, new ApiCallback<String>() {
|
||||
@Override
|
||||
public void onSuccess(String result) {
|
||||
// Ok, we've got a ping, if we were in a error or uninitialized state, update
|
||||
if ((hostState.lastCallResult == PlayerEventsObserver.PLAYER_NO_RESULT) ||
|
||||
(hostState.lastCallResult == PlayerEventsObserver.PLAYER_CONNECTION_ERROR)) {
|
||||
// Ok, we've got a ping, if there are playerEventsObservers and
|
||||
// we were in a error or uninitialized state, update
|
||||
if ((! playerEventsObservers.isEmpty()) &&
|
||||
((hostState.lastCallResult == PlayerEventsObserver.PLAYER_NO_RESULT) ||
|
||||
(hostState.lastCallResult == PlayerEventsObserver.PLAYER_CONNECTION_ERROR))) {
|
||||
checkWhatsPlaying();
|
||||
}
|
||||
checkerHandler.postDelayed(tcpCheckerRunnable, PING_AFTER_SUCCESS_CHECK_INTERVAL);
|
||||
|
@ -244,9 +249,7 @@ public class HostConnectionObserver
|
|||
if (this.connection == null)
|
||||
return;
|
||||
|
||||
// Save this observer and a new handle to notify him
|
||||
playerEventsObservers.add(observer);
|
||||
// observerHandlerMap.put(observer, new Handler());
|
||||
|
||||
if (replyImmediately) replyWithLastResult(observer);
|
||||
|
||||
|
@ -316,6 +319,7 @@ public class HostConnectionObserver
|
|||
// as a connection observer, which we will pass to the "real" observer
|
||||
if (connection.getProtocol() == HostConnection.PROTOCOL_TCP) {
|
||||
connection.registerApplicationNotificationsObserver(this, checkerHandler);
|
||||
checkerHandler.post(tcpCheckerRunnable);
|
||||
} else {
|
||||
checkerHandler.post(httpApplicationCheckerRunnable);
|
||||
}
|
||||
|
@ -337,7 +341,7 @@ public class HostConnectionObserver
|
|||
// No more observers, so unregister us from the host connection, or stop
|
||||
// the http checker thread
|
||||
if (connection.getProtocol() == HostConnection.PROTOCOL_TCP) {
|
||||
connection.unregisterApplicationotificationsObserver(this);
|
||||
connection.unregisterApplicationNotificationsObserver(this);
|
||||
} else {
|
||||
checkerHandler.removeCallbacks(httpApplicationCheckerRunnable);
|
||||
}
|
||||
|
@ -364,6 +368,14 @@ public class HostConnectionObserver
|
|||
hostState.lastCallResult = PlayerEventsObserver.PLAYER_NO_RESULT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPropertyChanged(org.xbmc.kore.jsonrpc.notification.Player.OnPropertyChanged notification) {
|
||||
List<PlayerEventsObserver> allObservers = new ArrayList<>(playerEventsObservers);
|
||||
for (final PlayerEventsObserver observer : allObservers) {
|
||||
observer.playerOnPropertyChanged(notification.data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link HostConnection.PlayerNotificationsObserver} interface methods
|
||||
*/
|
||||
|
@ -746,7 +758,7 @@ public class HostConnectionObserver
|
|||
/**
|
||||
* Replies to the observer with the last result we got.
|
||||
* If we have no result, nothing will be called on the observer interface.
|
||||
* @param observer Obserser to call with last result
|
||||
* @param observer Observer to call with last result
|
||||
*/
|
||||
public void replyWithLastResult(PlayerEventsObserver observer) {
|
||||
switch (hostState.lastCallResult) {
|
||||
|
|
|
@ -42,7 +42,6 @@ import java.io.BufferedWriter;
|
|||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.Proxy;
|
||||
import java.net.Socket;
|
||||
|
@ -70,6 +69,7 @@ public class HostConnection {
|
|||
* Interface that an observer must implement to be notified of player notifications
|
||||
*/
|
||||
public interface PlayerNotificationsObserver {
|
||||
public void onPropertyChanged(Player.OnPropertyChanged notification);
|
||||
public void onPlay(Player.OnPlay notification);
|
||||
public void onPause(Player.OnPause notification);
|
||||
public void onSpeedChanged(Player.OnSpeedChanged notification);
|
||||
|
@ -274,7 +274,7 @@ public class HostConnection {
|
|||
* Unregisters and observer from the input notifications
|
||||
* @param observer The {@link InputNotificationsObserver}
|
||||
*/
|
||||
public void unregisterApplicationotificationsObserver(ApplicationNotificationsObserver observer) {
|
||||
public void unregisterApplicationNotificationsObserver(ApplicationNotificationsObserver observer) {
|
||||
applicationNotificationsObservers.remove(observer);
|
||||
}
|
||||
|
||||
|
@ -661,6 +661,18 @@ public class HostConnection {
|
|||
}
|
||||
});
|
||||
}
|
||||
} else if (notificationName.equals(Player.OnPropertyChanged.NOTIFICATION_NAME)) {
|
||||
final Player.OnPropertyChanged apiNotification = new Player.OnPropertyChanged(params);
|
||||
for (final PlayerNotificationsObserver observer :
|
||||
playerNotificationsObservers.keySet()) {
|
||||
Handler handler = playerNotificationsObservers.get(observer);
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
observer.onPropertyChanged(apiNotification);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (notificationName.equals(System.OnQuit.NOTIFICATION_NAME)) {
|
||||
final System.OnQuit apiNotification = new System.OnQuit(params);
|
||||
for (final SystemNotificationsObserver observer :
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package org.xbmc.kore.jsonrpc.notification;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.BooleanNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.xbmc.kore.jsonrpc.ApiNotification;
|
||||
import org.xbmc.kore.jsonrpc.type.GlobalType;
|
||||
|
@ -26,6 +27,23 @@ import org.xbmc.kore.utils.JsonUtils;
|
|||
*/
|
||||
public class Player {
|
||||
|
||||
/**
|
||||
* Player.OnPropertyChanged notification
|
||||
* Player properties have changed. Such as repeat type and shuffle mode
|
||||
*/
|
||||
public static class OnPropertyChanged extends ApiNotification {
|
||||
public static final String NOTIFICATION_NAME = "Player.OnPropertyChanged";
|
||||
|
||||
public final NotificationsData data;
|
||||
|
||||
public OnPropertyChanged(ObjectNode node) {
|
||||
super(node);
|
||||
data = new NotificationsData(node.get(NotificationsData.DATA_NODE));
|
||||
}
|
||||
|
||||
public String getNotificationName() { return NOTIFICATION_NAME; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Player.OnPause notification
|
||||
* Playback of a media item has been paused. If there is no ID available extra information will be provided.
|
||||
|
@ -179,15 +197,44 @@ public class Player {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notification data for player properties
|
||||
*/
|
||||
public static class NotificationsProperty {
|
||||
public static final String PROPERTY_NODE = "property";
|
||||
|
||||
public final Boolean shuffled;
|
||||
public final String repeatMode;
|
||||
|
||||
public NotificationsProperty(JsonNode node) {
|
||||
JsonNode shuffledNode = node.get("shuffled");
|
||||
if (shuffledNode != null)
|
||||
shuffled = shuffledNode.asBoolean();
|
||||
else
|
||||
shuffled = null;
|
||||
|
||||
repeatMode = JsonUtils.stringFromJsonNode(node, "repeat");
|
||||
}
|
||||
}
|
||||
|
||||
public static class NotificationsData {
|
||||
public static final String DATA_NODE = "data";
|
||||
|
||||
public final NotificationsPlayer player;
|
||||
public final NotificationsItem item;
|
||||
public final NotificationsProperty property;
|
||||
|
||||
public NotificationsData(JsonNode node) {
|
||||
item = new NotificationsItem((ObjectNode)node.get(NotificationsItem.ITEM_NODE));
|
||||
player = new NotificationsPlayer((ObjectNode)node.get(NotificationsPlayer.PLAYER_NODE));
|
||||
JsonNode jsonNode = node.get(NotificationsItem.ITEM_NODE);
|
||||
item = (jsonNode != null) ? new NotificationsItem(jsonNode) : null;
|
||||
|
||||
jsonNode = node.get(NotificationsPlayer.PLAYER_NODE);
|
||||
player = (jsonNode != null)
|
||||
? new NotificationsPlayer(jsonNode)
|
||||
: null;
|
||||
|
||||
jsonNode = node.get(NotificationsProperty.PROPERTY_NODE);
|
||||
property = (jsonNode != null) ? new NotificationsProperty(jsonNode) : null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import android.support.v4.content.ContextCompat;
|
|||
import org.xbmc.kore.Settings;
|
||||
import org.xbmc.kore.host.HostConnectionObserver;
|
||||
import org.xbmc.kore.host.HostManager;
|
||||
import org.xbmc.kore.jsonrpc.notification.Player;
|
||||
import org.xbmc.kore.jsonrpc.type.ListType;
|
||||
import org.xbmc.kore.jsonrpc.type.PlayerType;
|
||||
import org.xbmc.kore.utils.LogUtils;
|
||||
|
@ -150,6 +151,11 @@ public class ConnectionObserversManagerService extends Service
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playerOnPropertyChanged(Player.NotificationsData notificationsData) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* HostConnectionObserver.PlayerEventsObserver interface callbacks
|
||||
*/
|
||||
|
|
|
@ -17,10 +17,8 @@ package org.xbmc.kore.service;
|
|||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
|
@ -36,6 +34,7 @@ import com.squareup.picasso.Target;
|
|||
import org.xbmc.kore.R;
|
||||
import org.xbmc.kore.host.HostConnectionObserver;
|
||||
import org.xbmc.kore.host.HostManager;
|
||||
import org.xbmc.kore.jsonrpc.notification.Player;
|
||||
import org.xbmc.kore.jsonrpc.type.ListType;
|
||||
import org.xbmc.kore.jsonrpc.type.PlayerType;
|
||||
import org.xbmc.kore.ui.sections.remote.RemoteActivity;
|
||||
|
@ -68,6 +67,11 @@ public class NotificationObserver
|
|||
mRemoteStartPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playerOnPropertyChanged(Player.NotificationsData notificationsData) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* HostConnectionObserver.PlayerEventsObserver interface callbacks
|
||||
*/
|
||||
|
|
|
@ -74,6 +74,11 @@ public class PauseCallObserver extends PhoneStateListener
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playerOnPropertyChanged(org.xbmc.kore.jsonrpc.notification.Player.NotificationsData notificationsData) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playerOnPlay(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||
PlayerType.PropertyValue getPropertiesResult,
|
||||
|
|
|
@ -17,43 +17,88 @@ package org.xbmc.kore.ui;
|
|||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.support.v4.widget.DrawerLayout;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.text.TextUtils;
|
||||
import android.transition.TransitionInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.sothree.slidinguppanel.SlidingUpPanelLayout;
|
||||
|
||||
import org.xbmc.kore.R;
|
||||
import org.xbmc.kore.Settings;
|
||||
import org.xbmc.kore.host.HostConnectionObserver;
|
||||
import org.xbmc.kore.host.HostManager;
|
||||
import org.xbmc.kore.jsonrpc.ApiCallback;
|
||||
import org.xbmc.kore.jsonrpc.ApiMethod;
|
||||
import org.xbmc.kore.jsonrpc.method.Application;
|
||||
import org.xbmc.kore.jsonrpc.method.Player;
|
||||
import org.xbmc.kore.jsonrpc.type.ListType;
|
||||
import org.xbmc.kore.jsonrpc.type.PlayerType;
|
||||
import org.xbmc.kore.ui.generic.NavigationDrawerFragment;
|
||||
import org.xbmc.kore.ui.sections.remote.RemoteActivity;
|
||||
import org.xbmc.kore.ui.widgets.MediaProgressIndicator;
|
||||
import org.xbmc.kore.ui.widgets.NowPlayingPanel;
|
||||
import org.xbmc.kore.ui.widgets.VolumeLevelIndicator;
|
||||
import org.xbmc.kore.utils.LogUtils;
|
||||
import org.xbmc.kore.utils.SharedElementTransition;
|
||||
import org.xbmc.kore.utils.UIUtils;
|
||||
import org.xbmc.kore.utils.Utils;
|
||||
|
||||
public abstract class BaseMediaActivity extends AppCompatActivity {
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.InjectView;
|
||||
|
||||
public abstract class BaseMediaActivity extends BaseActivity
|
||||
implements HostConnectionObserver.ApplicationEventsObserver,
|
||||
HostConnectionObserver.PlayerEventsObserver,
|
||||
NowPlayingPanel.OnPanelButtonsClickListener,
|
||||
MediaProgressIndicator.OnProgressChangeListener {
|
||||
private static final String TAG = LogUtils.makeLogTag(BaseMediaActivity.class);
|
||||
|
||||
private static final String NAVICON_ISARROW = "navstate";
|
||||
private static final String ACTIONBAR_TITLE = "actionbartitle";
|
||||
|
||||
@InjectView(R.id.now_playing_panel) NowPlayingPanel nowPlayingPanel;
|
||||
|
||||
private NavigationDrawerFragment navigationDrawerFragment;
|
||||
private SharedElementTransition sharedElementTransition = new SharedElementTransition();
|
||||
|
||||
private boolean drawerIndicatorIsArrow;
|
||||
private int currentActivePlayerId = -1;
|
||||
|
||||
private HostManager hostManager;
|
||||
private HostConnectionObserver hostConnectionObserver;
|
||||
|
||||
private boolean showNowPlayingPanel;
|
||||
|
||||
protected abstract String getActionBarTitle();
|
||||
protected abstract Fragment createFragment();
|
||||
|
||||
/**
|
||||
* Default callback for methods that don't return anything
|
||||
*/
|
||||
private ApiCallback<String> defaultStringActionCallback = ApiMethod.getDefaultActionCallback();
|
||||
private Handler callbackHandler = new Handler();
|
||||
private ApiCallback<Integer> defaultIntActionCallback = ApiMethod.getDefaultActionCallback();
|
||||
|
||||
private Runnable hidePanelRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
nowPlayingPanel.setPanelState(SlidingUpPanelLayout.PanelState.HIDDEN);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
@TargetApi(21)
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -61,13 +106,10 @@ public abstract class BaseMediaActivity extends AppCompatActivity {
|
|||
if (Utils.isLollipopOrLater()) {
|
||||
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
|
||||
}
|
||||
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
setTheme(UIUtils.getThemeResourceId(
|
||||
prefs.getString(Settings.KEY_PREF_THEME, Settings.DEFAULT_PREF_THEME)));
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_generic_media);
|
||||
ButterKnife.inject(this);
|
||||
|
||||
// Set up the drawer.
|
||||
navigationDrawerFragment = (NavigationDrawerFragment)getSupportFragmentManager()
|
||||
|
@ -113,6 +155,8 @@ public abstract class BaseMediaActivity extends AppCompatActivity {
|
|||
if (Utils.isLollipopOrLater()) {
|
||||
sharedElementTransition.setupExitTransition(this, fragment);
|
||||
}
|
||||
|
||||
hostManager = HostManager.getInstance(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -129,6 +173,38 @@ public abstract class BaseMediaActivity extends AppCompatActivity {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
showNowPlayingPanel = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.getBoolean(Settings.KEY_PREF_SHOW_NOW_PLAYING_PANEL,
|
||||
Settings.DEFAULT_PREF_SHOW_NOW_PLAYING_PANEL);
|
||||
|
||||
|
||||
if(showNowPlayingPanel) {
|
||||
setupNowPlayingPanel();
|
||||
} else {
|
||||
//Hide it in case we were displaying the panel and user disabled showing
|
||||
//the panel in Settings
|
||||
nowPlayingPanel.setPanelState(SlidingUpPanelLayout.PanelState.HIDDEN);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
if(!showNowPlayingPanel)
|
||||
return;
|
||||
|
||||
hostConnectionObserver = hostManager.getHostConnectionObserver();
|
||||
if (hostConnectionObserver == null)
|
||||
return;
|
||||
|
||||
hostConnectionObserver.unregisterApplicationObserver(this);
|
||||
hostConnectionObserver.unregisterPlayerObserver(this);
|
||||
}
|
||||
|
||||
public boolean getDrawerIndicatorIsArrow() {
|
||||
return drawerIndicatorIsArrow;
|
||||
}
|
||||
|
@ -187,4 +263,272 @@ public abstract class BaseMediaActivity extends AppCompatActivity {
|
|||
.addToBackStack(null)
|
||||
.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applicationOnVolumeChanged(int volume, boolean muted) {
|
||||
nowPlayingPanel.setVolume(volume, muted);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playerOnPropertyChanged(org.xbmc.kore.jsonrpc.notification.Player.NotificationsData notificationsData) {
|
||||
if (notificationsData.property.shuffled != null)
|
||||
nowPlayingPanel.setShuffled(notificationsData.property.shuffled);
|
||||
|
||||
if (notificationsData.property.repeatMode != null )
|
||||
nowPlayingPanel.setRepeatMode(notificationsData.property.repeatMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playerOnPlay(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||
PlayerType.PropertyValue getPropertiesResult,
|
||||
ListType.ItemsAll getItemResult) {
|
||||
currentActivePlayerId = getActivePlayerResult.playerid;
|
||||
|
||||
updateNowPlayingPanel(getPropertiesResult, getItemResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playerOnPause(PlayerType.GetActivePlayersReturnType getActivePlayerResult, PlayerType.PropertyValue getPropertiesResult, ListType.ItemsAll getItemResult) {
|
||||
currentActivePlayerId = getActivePlayerResult.playerid;
|
||||
|
||||
updateNowPlayingPanel(getPropertiesResult, getItemResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playerOnStop() {
|
||||
//We delay hiding the panel to prevent hiding the panel when playing
|
||||
// the next item in a playlist
|
||||
callbackHandler.removeCallbacks(hidePanelRunnable);
|
||||
callbackHandler.postDelayed(hidePanelRunnable, 1000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playerOnConnectionError(int errorCode, String description) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playerNoResultsYet() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void observerOnStopObserving() {
|
||||
nowPlayingPanel.setPanelState(SlidingUpPanelLayout.PanelState.HIDDEN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void systemOnQuit() {
|
||||
nowPlayingPanel.setPanelState(SlidingUpPanelLayout.PanelState.HIDDEN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inputOnInputRequested(String title, String type, String value) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(int progress) {
|
||||
PlayerType.PositionTime positionTime = new PlayerType.PositionTime(progress);
|
||||
Player.Seek seekAction = new Player.Seek(currentActivePlayerId, positionTime);
|
||||
seekAction.execute(HostManager.getInstance(this).getConnection(), new ApiCallback<PlayerType.SeekReturnType>() {
|
||||
@Override
|
||||
public void onSuccess(PlayerType.SeekReturnType result) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(int errorCode, String description) {
|
||||
LogUtils.LOGE(TAG, "Got an error calling Player.Seek. Error code: " + errorCode + ", description: " + description);
|
||||
}
|
||||
}, new Handler());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayClicked() {
|
||||
Player.PlayPause action = new Player.PlayPause(currentActivePlayerId);
|
||||
action.execute(hostManager.getConnection(), defaultIntActionCallback, callbackHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPreviousClicked() {
|
||||
Player.GoTo action = new Player.GoTo(currentActivePlayerId, Player.GoTo.PREVIOUS);
|
||||
action.execute(hostManager.getConnection(), defaultStringActionCallback, callbackHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNextClicked() {
|
||||
Player.GoTo action = new Player.GoTo(currentActivePlayerId, Player.GoTo.NEXT);
|
||||
action.execute(hostManager.getConnection(), defaultStringActionCallback, callbackHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVolumeMuteClicked() {
|
||||
Application.SetMute action = new Application.SetMute();
|
||||
action.execute(hostManager.getConnection(), new ApiCallback<Boolean>() {
|
||||
@Override
|
||||
public void onSuccess(Boolean result) {
|
||||
//We depend on the listener to correct the mute button state
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(int errorCode, String description) { }
|
||||
}, new Handler());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShuffleClicked() {
|
||||
Player.SetShuffle action = new Player.SetShuffle(currentActivePlayerId);
|
||||
action.execute(hostManager.getConnection(), new ApiCallback<String>() {
|
||||
@Override
|
||||
public void onSuccess(String result) {
|
||||
//We depend on the listener to correct the mute button state
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(int errorCode, String description) { }
|
||||
}, callbackHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRepeatClicked() {
|
||||
Player.SetRepeat action = new Player.SetRepeat(currentActivePlayerId, PlayerType.Repeat.CYCLE);
|
||||
action.execute(hostManager.getConnection(), new ApiCallback<String>() {
|
||||
@Override
|
||||
public void onSuccess(String result) {
|
||||
//We depend on the listener to correct the mute button state
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(int errorCode, String description) { }
|
||||
}, callbackHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVolumeMutedIndicatorClicked() {
|
||||
Application.SetMute action = new Application.SetMute();
|
||||
action.execute(hostManager.getConnection(), new ApiCallback<Boolean>() {
|
||||
@Override
|
||||
public void onSuccess(Boolean result) {
|
||||
//We depend on the listener to correct the mute button state
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(int errorCode, String description) { }
|
||||
}, new Handler());
|
||||
}
|
||||
|
||||
private void setupNowPlayingPanel() {
|
||||
nowPlayingPanel.setOnVolumeChangeListener(new VolumeLevelIndicator.OnVolumeChangeListener() {
|
||||
@Override
|
||||
public void onVolumeChanged(int volume) {
|
||||
new Application.SetVolume(volume)
|
||||
.execute(hostManager.getConnection(), defaultIntActionCallback, new Handler());
|
||||
}
|
||||
});
|
||||
|
||||
nowPlayingPanel.setOnPanelButtonsClickListener(this);
|
||||
nowPlayingPanel.setOnProgressChangeListener(this);
|
||||
|
||||
hostConnectionObserver = hostManager.getHostConnectionObserver();
|
||||
if (hostConnectionObserver == null)
|
||||
return;
|
||||
|
||||
hostConnectionObserver.registerApplicationObserver(this, true);
|
||||
hostConnectionObserver.registerPlayerObserver(this, true);
|
||||
|
||||
hostConnectionObserver.forceRefreshResults();
|
||||
}
|
||||
|
||||
private void updateNowPlayingPanel(PlayerType.PropertyValue getPropertiesResult,
|
||||
ListType.ItemsAll getItemResult) {
|
||||
String title;
|
||||
String poster;
|
||||
String details = null;
|
||||
|
||||
callbackHandler.removeCallbacks(hidePanelRunnable);
|
||||
|
||||
// Only set state to collapsed if panel is currently hidden. This prevents collapsing
|
||||
// the panel when the user expanded the panel and started playing the item from a paused
|
||||
// state
|
||||
if (nowPlayingPanel.getPanelState() == SlidingUpPanelLayout.PanelState.HIDDEN) {
|
||||
nowPlayingPanel.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED);
|
||||
}
|
||||
|
||||
nowPlayingPanel.setMediaProgress(getPropertiesResult.time, getPropertiesResult.totaltime);
|
||||
|
||||
nowPlayingPanel.setPlayButton(getPropertiesResult.speed > 0);
|
||||
nowPlayingPanel.setShuffled(getPropertiesResult.shuffled);
|
||||
nowPlayingPanel.setRepeatMode(getPropertiesResult.repeat);
|
||||
nowPlayingPanel.setSpeed(getPropertiesResult.speed);
|
||||
|
||||
switch (getItemResult.type) {
|
||||
case ListType.ItemsAll.TYPE_MOVIE:
|
||||
title = getItemResult.title;
|
||||
details = getItemResult.tagline;
|
||||
poster = TextUtils.isEmpty(getItemResult.thumbnail) ? getItemResult.fanart
|
||||
: getItemResult.thumbnail;
|
||||
break;
|
||||
case ListType.ItemsAll.TYPE_EPISODE:
|
||||
title = getItemResult.title;
|
||||
String seasonEpisode = String.format(getString(R.string.season_episode),
|
||||
getItemResult.season, getItemResult.episode);
|
||||
details = String.format("%s | %s", getItemResult.showtitle, seasonEpisode);
|
||||
poster = TextUtils.isEmpty(getItemResult.art.poster) ? getItemResult.art.fanart
|
||||
: getItemResult.art.poster;
|
||||
break;
|
||||
case ListType.ItemsAll.TYPE_SONG:
|
||||
title = getItemResult.title;
|
||||
details = getItemResult.displayartist + " | " + getItemResult.album;
|
||||
poster = TextUtils.isEmpty(getItemResult.thumbnail) ? getItemResult.fanart
|
||||
: getItemResult.thumbnail;
|
||||
break;
|
||||
case ListType.ItemsAll.TYPE_MUSIC_VIDEO:
|
||||
title = getItemResult.title;
|
||||
details = Utils.listStringConcat(getItemResult.artist, ", ") + " | " + getItemResult.album;
|
||||
poster = TextUtils.isEmpty(getItemResult.thumbnail) ? getItemResult.fanart
|
||||
: getItemResult.thumbnail;
|
||||
break;
|
||||
case ListType.ItemsAll.TYPE_CHANNEL:
|
||||
title = getItemResult.label;
|
||||
details = getItemResult.title;
|
||||
poster = TextUtils.isEmpty(getItemResult.thumbnail) ? getItemResult.fanart
|
||||
: getItemResult.thumbnail;
|
||||
break;
|
||||
default:
|
||||
title = getItemResult.label;
|
||||
poster = TextUtils.isEmpty(getItemResult.thumbnail) ? getItemResult.fanart
|
||||
: getItemResult.thumbnail;
|
||||
break;
|
||||
}
|
||||
|
||||
if (title.contentEquals(nowPlayingPanel.getTitle()))
|
||||
return; // Still showing same item as previous call
|
||||
|
||||
nowPlayingPanel.setTitle(title);
|
||||
|
||||
if (details != null) {
|
||||
nowPlayingPanel.setDetails(details);
|
||||
}
|
||||
|
||||
if ((getItemResult.type.contentEquals(ListType.ItemsAll.TYPE_MUSIC_VIDEO)) ||
|
||||
(getItemResult.type.contentEquals(ListType.ItemsAll.TYPE_SONG))) {
|
||||
nowPlayingPanel.setNextPrevVisibility(View.VISIBLE);
|
||||
} else {
|
||||
nowPlayingPanel.setNextPrevVisibility(View.GONE);
|
||||
}
|
||||
|
||||
Resources resources = getResources();
|
||||
int posterWidth = resources.getDimensionPixelOffset(R.dimen.notification_art_slim_width);
|
||||
int posterHeight = resources.getDimensionPixelOffset(R.dimen.notification_art_slim_height);
|
||||
|
||||
// If not video, change aspect ration of poster to a square
|
||||
boolean isVideo = (getItemResult.type.equals(ListType.ItemsAll.TYPE_MOVIE)) ||
|
||||
(getItemResult.type.equals(ListType.ItemsAll.TYPE_EPISODE));
|
||||
|
||||
nowPlayingPanel.setSquarePoster(!isVideo);
|
||||
|
||||
UIUtils.loadImageWithCharacterAvatar(this, hostManager, poster, title,
|
||||
nowPlayingPanel.getPoster(),
|
||||
(isVideo) ? posterWidth : posterHeight, posterHeight);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,7 +84,6 @@ public class NowPlayingFragment extends Fragment
|
|||
*/
|
||||
public interface NowPlayingListener {
|
||||
public void SwitchToRemotePanel();
|
||||
public void onShuffleClicked();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -126,19 +125,17 @@ public class NowPlayingFragment extends Fragment
|
|||
private int currentSubtitleIndex = -1;
|
||||
private int currentAudiostreamIndex = -1;
|
||||
|
||||
private ApiCallback<Integer> defaultIntActionCallback = ApiMethod.getDefaultActionCallback();
|
||||
private ApiCallback<Boolean> defaultBooleanActionCallback = ApiMethod.getDefaultActionCallback();
|
||||
|
||||
/**
|
||||
* Injectable views
|
||||
*/
|
||||
@InjectView(R.id.play) ImageButton playButton;
|
||||
@InjectView(R.id.stop) ImageButton stopButton;
|
||||
@InjectView(R.id.previous) ImageButton previousButton;
|
||||
@InjectView(R.id.next) ImageButton nextButton;
|
||||
@InjectView(R.id.rewind) ImageButton rewindButton;
|
||||
@InjectView(R.id.fast_forward) ImageButton fastForwardButton;
|
||||
|
||||
@InjectView(R.id.repeat) RepeatModeButton repeatButton;
|
||||
@InjectView(R.id.volume_mute) HighlightButton volumeMuteButton;
|
||||
@InjectView(R.id.shuffle) HighlightButton shuffleButton;
|
||||
@InjectView(R.id.repeat) RepeatModeButton repeatButton;
|
||||
@InjectView(R.id.overflow) ImageButton overflowButton;
|
||||
|
||||
@InjectView(R.id.info_panel) RelativeLayout infoPanel;
|
||||
|
@ -156,7 +153,6 @@ public class NowPlayingFragment extends Fragment
|
|||
|
||||
@InjectView(R.id.volume_level_indicator) VolumeLevelIndicator volumeLevelIndicator;
|
||||
|
||||
@InjectView(R.id.media_details) RelativeLayout mediaDetailsPanel;
|
||||
@InjectView(R.id.rating) TextView mediaRating;
|
||||
@InjectView(R.id.max_rating) TextView mediaMaxRating;
|
||||
@InjectView(R.id.year) TextView mediaYear;
|
||||
|
@ -189,6 +185,16 @@ public class NowPlayingFragment extends Fragment
|
|||
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_now_playing, container, false);
|
||||
ButterKnife.inject(this, root);
|
||||
|
||||
volumeLevelIndicator.setOnVolumeChangeListener(new VolumeLevelIndicator.OnVolumeChangeListener() {
|
||||
@Override
|
||||
public void onVolumeChanged(int volume) {
|
||||
new Application.SetVolume(volume)
|
||||
.execute(hostManager.getConnection(), defaultIntActionCallback, new Handler());
|
||||
}
|
||||
});
|
||||
|
||||
mediaProgressIndicator.setOnProgressChangeListener(this);
|
||||
|
||||
// Setup dim the fanart when scroll changes
|
||||
// Full dim on 4 * iconSize dp
|
||||
Resources resources = getActivity().getResources();
|
||||
|
@ -242,8 +248,6 @@ public class NowPlayingFragment extends Fragment
|
|||
* Default callback for methods that don't return anything
|
||||
*/
|
||||
private ApiCallback<String> defaultStringActionCallback = ApiMethod.getDefaultActionCallback();
|
||||
private ApiCallback<Integer> defaultIntActionCallback = ApiMethod.getDefaultActionCallback();
|
||||
private ApiCallback<Boolean> defaultBooleanActionCallback = ApiMethod.getDefaultActionCallback();
|
||||
|
||||
/**
|
||||
* Callback for methods that change the play speed
|
||||
|
@ -252,7 +256,7 @@ public class NowPlayingFragment extends Fragment
|
|||
@Override
|
||||
public void onSuccess(Integer result) {
|
||||
if (!isAdded()) return;
|
||||
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, result);
|
||||
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, result == 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -272,7 +276,7 @@ public class NowPlayingFragment extends Fragment
|
|||
public void onStopClicked(View v) {
|
||||
Player.Stop action = new Player.Stop(currentActivePlayerId);
|
||||
action.execute(hostManager.getConnection(), defaultStringActionCallback, callbackHandler);
|
||||
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, 0);
|
||||
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, false);
|
||||
}
|
||||
|
||||
@OnClick(R.id.fast_forward)
|
||||
|
@ -299,17 +303,8 @@ public class NowPlayingFragment extends Fragment
|
|||
action.execute(hostManager.getConnection(), defaultStringActionCallback, callbackHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calllbacks for media button toolbar
|
||||
*/
|
||||
@OnClick(R.id.volume_mute)
|
||||
public void onVolumeMuteClicked(View v) {
|
||||
// We boldly set the mute button to the desired state before actually setting
|
||||
// the mute state on the host. We do this to make it clear to the user that the button
|
||||
// was pressed.
|
||||
HostConnectionObserver.HostState hostState = hostConnectionObserver.getHostState();
|
||||
UIUtils.highlightImageView(getActivity(), volumeMuteButton, !hostState.isVolumeMuted());
|
||||
|
||||
Application.SetMute action = new Application.SetMute();
|
||||
action.execute(hostManager.getConnection(), defaultBooleanActionCallback, new Handler());
|
||||
}
|
||||
|
@ -323,7 +318,6 @@ public class NowPlayingFragment extends Fragment
|
|||
if (!isAdded()) return;
|
||||
// Force a refresh
|
||||
hostConnectionObserver.forceRefreshResults();
|
||||
nowPlayingListener.onShuffleClicked();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -539,20 +533,12 @@ public class NowPlayingFragment extends Fragment
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(int progress) {
|
||||
PlayerType.PositionTime positionTime = new PlayerType.PositionTime(progress);
|
||||
Player.Seek seekAction = new Player.Seek(currentActivePlayerId, positionTime);
|
||||
seekAction.execute(hostManager.getConnection(), new ApiCallback<PlayerType.SeekReturnType>() {
|
||||
@Override
|
||||
public void onSuccess(PlayerType.SeekReturnType result) {
|
||||
// Ignore
|
||||
}
|
||||
public void playerOnPropertyChanged(org.xbmc.kore.jsonrpc.notification.Player.NotificationsData notificationsData) {
|
||||
if (notificationsData.property.shuffled != null)
|
||||
shuffleButton.setHighlight(notificationsData.property.shuffled);
|
||||
|
||||
@Override
|
||||
public void onError(int errorCode, String description) {
|
||||
LogUtils.LOGD(TAG, "Got an error calling Player.Seek. Error code: " + errorCode + ", description: " + description);
|
||||
}
|
||||
}, callbackHandler);
|
||||
if (notificationsData.property.repeatMode != null)
|
||||
UIUtils.setRepeatButton(repeatButton, notificationsData.property.repeatMode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -561,19 +547,19 @@ public class NowPlayingFragment extends Fragment
|
|||
public void playerOnPlay(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||
PlayerType.PropertyValue getPropertiesResult,
|
||||
ListType.ItemsAll getItemResult) {
|
||||
setNowPlayingInfo(getPropertiesResult, getItemResult);
|
||||
setNowPlayingInfo(getActivePlayerResult, getPropertiesResult, getItemResult);
|
||||
currentActivePlayerId = getActivePlayerResult.playerid;
|
||||
// Switch icon
|
||||
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, getPropertiesResult.speed);
|
||||
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, getPropertiesResult.speed == 1);
|
||||
}
|
||||
|
||||
public void playerOnPause(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||
PlayerType.PropertyValue getPropertiesResult,
|
||||
ListType.ItemsAll getItemResult) {
|
||||
setNowPlayingInfo(getPropertiesResult, getItemResult);
|
||||
setNowPlayingInfo(getActivePlayerResult, getPropertiesResult, getItemResult);
|
||||
currentActivePlayerId = getActivePlayerResult.playerid;
|
||||
// Switch icon
|
||||
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, getPropertiesResult.speed);
|
||||
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, getPropertiesResult.speed == 1);
|
||||
}
|
||||
|
||||
public void playerOnStop() {
|
||||
|
@ -619,18 +605,38 @@ public class NowPlayingFragment extends Fragment
|
|||
@Override
|
||||
public void applicationOnVolumeChanged(int volume, boolean muted) {
|
||||
volumeLevelIndicator.setVolume(muted, volume);
|
||||
UIUtils.highlightImageView(getActivity(), volumeMuteButton, muted);
|
||||
volumeMuteButton.setHighlight(muted);
|
||||
}
|
||||
|
||||
// Ignore this
|
||||
public void inputOnInputRequested(String title, String type, String value) {}
|
||||
public void observerOnStopObserving() {}
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(int progress) {
|
||||
PlayerType.PositionTime positionTime = new PlayerType.PositionTime(progress);
|
||||
Player.Seek seekAction = new Player.Seek(currentActivePlayerId, positionTime);
|
||||
seekAction.execute(HostManager.getInstance(getContext()).getConnection(), new ApiCallback<PlayerType.SeekReturnType>() {
|
||||
@Override
|
||||
public void onSuccess(PlayerType.SeekReturnType result) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(int errorCode, String description) {
|
||||
LogUtils.LOGD("MediaSeekBar", "Got an error calling Player.Seek. Error code: " + errorCode + ", description: " + description);
|
||||
}
|
||||
}, new Handler());
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whats playing information
|
||||
* @param getItemResult Return from method {@link org.xbmc.kore.jsonrpc.method.Player.GetItem}
|
||||
*/
|
||||
private void setNowPlayingInfo(PlayerType.PropertyValue getPropertiesResult,
|
||||
private void setNowPlayingInfo(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||
PlayerType.PropertyValue getPropertiesResult,
|
||||
final ListType.ItemsAll getItemResult) {
|
||||
final String title, underTitle, art, poster, genreSeason, year,
|
||||
descriptionPlot, votes, maxRating;
|
||||
|
@ -776,7 +782,6 @@ public class NowPlayingFragment extends Fragment
|
|||
}
|
||||
|
||||
UIUtils.setRepeatButton(repeatButton, getPropertiesResult.repeat);
|
||||
|
||||
shuffleButton.setHighlight(getPropertiesResult.shuffled);
|
||||
|
||||
Resources resources = getActivity().getResources();
|
||||
|
|
|
@ -194,6 +194,12 @@ public class PlaylistFragment extends Fragment
|
|||
private PlayerType.PropertyValue lastGetPropertiesResult;
|
||||
private List<ListType.ItemsAll> lastGetPlaylistItemsResult = null;
|
||||
|
||||
@Override
|
||||
public void playerOnPropertyChanged(org.xbmc.kore.jsonrpc.notification.Player.NotificationsData notificationsData) {
|
||||
if (notificationsData.property.shuffled != null)
|
||||
setupPlaylistInfo(lastGetActivePlayerResult, lastGetPropertiesResult, lastGetItemResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* HostConnectionObserver.PlayerEventsObserver interface callbacks
|
||||
*/
|
||||
|
|
|
@ -610,6 +610,12 @@ public class RemoteActivity extends BaseActivity
|
|||
* HostConnectionObserver.PlayerEventsObserver interface callbacks
|
||||
*/
|
||||
private String lastImageUrl = null;
|
||||
|
||||
@Override
|
||||
public void playerOnPropertyChanged(org.xbmc.kore.jsonrpc.notification.Player.NotificationsData notificationsData) {
|
||||
|
||||
}
|
||||
|
||||
public void playerOnPlay(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||
PlayerType.PropertyValue getPropertiesResult,
|
||||
ListType.ItemsAll getItemResult) {
|
||||
|
@ -690,11 +696,6 @@ public class RemoteActivity extends BaseActivity
|
|||
viewPager.setCurrentItem(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShuffleClicked() {
|
||||
refreshPlaylist();
|
||||
}
|
||||
|
||||
private void refreshPlaylist() {
|
||||
String tag = "android:switcher:" + viewPager.getId() + ":" + PLAYLIST_FRAGMENT_ID;
|
||||
PlaylistFragment playlistFragment = (PlaylistFragment)getSupportFragmentManager()
|
||||
|
|
|
@ -552,13 +552,18 @@ public class RemoteFragment extends Fragment
|
|||
@Override
|
||||
public void onSuccess(Integer result) {
|
||||
if (!isAdded()) return;
|
||||
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, result);
|
||||
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, result == 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(int errorCode, String description) { }
|
||||
};
|
||||
|
||||
@Override
|
||||
public void playerOnPropertyChanged(org.xbmc.kore.jsonrpc.notification.Player.NotificationsData notificationsData) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* HostConnectionObserver.PlayerEventsObserver interface callbacks
|
||||
*/
|
||||
|
@ -569,7 +574,7 @@ public class RemoteFragment extends Fragment
|
|||
currentActivePlayerId = getActivePlayerResult.playerid;
|
||||
currentNowPlayingItemType = getItemResult.type;
|
||||
// Switch icon
|
||||
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, getPropertiesResult.speed);
|
||||
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, getPropertiesResult.speed == 1);
|
||||
}
|
||||
|
||||
public void playerOnPause(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||
|
@ -579,7 +584,7 @@ public class RemoteFragment extends Fragment
|
|||
currentActivePlayerId = getActivePlayerResult.playerid;
|
||||
currentNowPlayingItemType = getItemResult.type;
|
||||
// Switch icon
|
||||
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, getPropertiesResult.speed);
|
||||
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, getPropertiesResult.speed == 1);
|
||||
}
|
||||
|
||||
public void playerOnStop() {
|
||||
|
|
|
@ -18,6 +18,8 @@ package org.xbmc.kore.ui.widgets;
|
|||
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.widget.LinearLayout;
|
||||
|
@ -25,6 +27,7 @@ import android.widget.SeekBar;
|
|||
import android.widget.TextView;
|
||||
|
||||
import org.xbmc.kore.R;
|
||||
import org.xbmc.kore.utils.LogUtils;
|
||||
import org.xbmc.kore.utils.UIUtils;
|
||||
|
||||
public class MediaProgressIndicator extends LinearLayout {
|
||||
|
@ -94,6 +97,24 @@ public class MediaProgressIndicator extends LinearLayout {
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Parcelable onSaveInstanceState() {
|
||||
SavedState savedState = new SavedState(super.onSaveInstanceState());
|
||||
savedState.progress = progress;
|
||||
savedState.maxProgress = maxProgress;
|
||||
return savedState;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Parcelable state) {
|
||||
SavedState savedState = (SavedState) state;
|
||||
super.onRestoreInstanceState(savedState.getSuperState());
|
||||
progress = savedState.progress;
|
||||
maxProgress = savedState.maxProgress;
|
||||
setProgress(progress);
|
||||
setMaxProgress(maxProgress);
|
||||
}
|
||||
|
||||
private Runnable seekBarUpdater = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -119,6 +140,10 @@ public class MediaProgressIndicator extends LinearLayout {
|
|||
progressTextView.setText(UIUtils.formatTime(progress));
|
||||
}
|
||||
|
||||
public int getProgress() {
|
||||
return progress;
|
||||
}
|
||||
|
||||
public void setMaxProgress(int max) {
|
||||
maxProgress = max;
|
||||
seekBar.setMax(max);
|
||||
|
@ -140,4 +165,37 @@ public class MediaProgressIndicator extends LinearLayout {
|
|||
if (speed > 0)
|
||||
seekBar.postDelayed(seekBarUpdater, SEEK_BAR_UPDATE_INTERVAL);
|
||||
}
|
||||
|
||||
private static class SavedState extends BaseSavedState {
|
||||
int progress;
|
||||
int maxProgress;
|
||||
|
||||
SavedState(Parcelable superState) {
|
||||
super(superState);
|
||||
}
|
||||
|
||||
private SavedState(Parcel in) {
|
||||
super(in);
|
||||
progress = in.readInt();
|
||||
maxProgress = in.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
super.writeToParcel(out, flags);
|
||||
out.writeInt(progress);
|
||||
out.writeInt(maxProgress);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<SavedState> CREATOR
|
||||
= new Parcelable.Creator<SavedState>() {
|
||||
public SavedState createFromParcel(Parcel in) {
|
||||
return new SavedState(in);
|
||||
}
|
||||
|
||||
public SavedState[] newArray(int size) {
|
||||
return new SavedState[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
* Copyright 2017 Martijn Brekhof. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.xbmc.kore.ui.widgets;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.sothree.slidinguppanel.SlidingUpPanelLayout;
|
||||
|
||||
import org.xbmc.kore.R;
|
||||
import org.xbmc.kore.jsonrpc.type.GlobalType;
|
||||
import org.xbmc.kore.utils.UIUtils;
|
||||
|
||||
public class NowPlayingPanel extends SlidingUpPanelLayout {
|
||||
|
||||
public interface OnPanelButtonsClickListener {
|
||||
void onPlayClicked();
|
||||
void onPreviousClicked();
|
||||
void onNextClicked();
|
||||
void onVolumeMuteClicked();
|
||||
void onShuffleClicked();
|
||||
void onRepeatClicked();
|
||||
void onVolumeMutedIndicatorClicked();
|
||||
}
|
||||
|
||||
private OnPanelButtonsClickListener onPanelButtonsClickListener;
|
||||
|
||||
private TextView title;
|
||||
private TextView details;
|
||||
private ImageView poster;
|
||||
private ImageButton previousButton;
|
||||
private ImageButton nextButton;
|
||||
private ImageButton playButton;
|
||||
private MediaProgressIndicator mediaProgressIndicator;
|
||||
private VolumeLevelIndicator volumeLevelIndicator;
|
||||
private HighlightButton volumeMuteButton;
|
||||
private HighlightButton volumeMutedIndicatorButton;
|
||||
private RepeatModeButton repeatModeButton;
|
||||
private HighlightButton shuffleButton;
|
||||
|
||||
public NowPlayingPanel(Context context) {
|
||||
super(context);
|
||||
initializeView(context);
|
||||
}
|
||||
public NowPlayingPanel(Context context, AttributeSet attributeSet) {
|
||||
super(context, attributeSet);
|
||||
initializeView(context);
|
||||
}
|
||||
|
||||
public NowPlayingPanel(Context context, AttributeSet attributeSet, int defStyle) {
|
||||
super(context, attributeSet, defStyle);
|
||||
initializeView(context);
|
||||
}
|
||||
|
||||
private void initializeView(Context context) {
|
||||
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
inflater.inflate(R.layout.now_playing_panel, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
|
||||
title = (TextView) findViewById(R.id.npp_title);
|
||||
details = (TextView) findViewById(R.id.npp_details);
|
||||
poster = (ImageView) findViewById(R.id.npp_poster);
|
||||
previousButton = (ImageButton) findViewById(R.id.npp_previous);
|
||||
nextButton = (ImageButton) findViewById(R.id.npp_next);
|
||||
playButton = (ImageButton) findViewById(R.id.npp_play);
|
||||
mediaProgressIndicator = (MediaProgressIndicator) findViewById(R.id.npp_progress_indicator);
|
||||
volumeLevelIndicator = (VolumeLevelIndicator) findViewById(R.id.npp_volume_level_indicator);
|
||||
volumeMuteButton = (HighlightButton) findViewById(R.id.npp_volume_mute);
|
||||
repeatModeButton = (RepeatModeButton) findViewById(R.id.npp_repeat);
|
||||
shuffleButton = (HighlightButton) findViewById(R.id.npp_shuffle);
|
||||
volumeMutedIndicatorButton = (HighlightButton) findViewById(R.id.npp_volume_muted_indicator);
|
||||
|
||||
setupButtonClickListeners();
|
||||
}
|
||||
|
||||
public void setOnPanelButtonsClickListener(OnPanelButtonsClickListener listener) {
|
||||
onPanelButtonsClickListener = listener;
|
||||
}
|
||||
|
||||
public void setOnVolumeChangeListener(VolumeLevelIndicator.OnVolumeChangeListener listener) {
|
||||
volumeLevelIndicator.setOnVolumeChangeListener(listener);
|
||||
}
|
||||
|
||||
public void setOnProgressChangeListener(MediaProgressIndicator.OnProgressChangeListener listener) {
|
||||
mediaProgressIndicator.setOnProgressChangeListener(listener);
|
||||
}
|
||||
|
||||
public void setVolume(int volume, boolean muted) {
|
||||
volumeLevelIndicator.setVolume(muted, volume);
|
||||
|
||||
if (muted) {
|
||||
volumeMutedIndicatorButton.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
volumeMutedIndicatorButton.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
volumeMutedIndicatorButton.setHighlight(muted);
|
||||
volumeMuteButton.setHighlight(muted);
|
||||
}
|
||||
|
||||
public void setRepeatMode(String repeatMode) {
|
||||
UIUtils.setRepeatButton(repeatModeButton, repeatMode);
|
||||
}
|
||||
|
||||
public void setShuffled(boolean shuffled) {
|
||||
shuffleButton.setHighlight(shuffled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state of the play button
|
||||
* @param play true if playing, false if paused
|
||||
*/
|
||||
public void setPlayButton(boolean play) {
|
||||
UIUtils.setPlayPauseButtonIcon(getContext(), playButton, play);
|
||||
}
|
||||
|
||||
public void setMediaProgress(GlobalType.Time time, GlobalType.Time totalTime) {
|
||||
mediaProgressIndicator.setMaxProgress(totalTime.ToSeconds());
|
||||
mediaProgressIndicator.setProgress(time.ToSeconds());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the progression indicator used for media progression
|
||||
* @return
|
||||
*/
|
||||
public MediaProgressIndicator getMediaProgress() {
|
||||
return mediaProgressIndicator;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param speed
|
||||
*/
|
||||
public void setSpeed(int speed) {
|
||||
mediaProgressIndicator.setSpeed(speed);
|
||||
}
|
||||
|
||||
public CharSequence getTitle() {
|
||||
return title.getText();
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title.setText(title);
|
||||
}
|
||||
|
||||
public void setDetails(String details) {
|
||||
this.details.setText(details);
|
||||
}
|
||||
|
||||
public void setNextPrevVisibility(int visibility) {
|
||||
nextButton.setVisibility(visibility);
|
||||
previousButton.setVisibility(visibility);
|
||||
}
|
||||
|
||||
public void setSquarePoster(boolean square) {
|
||||
if (square) {
|
||||
ViewGroup.LayoutParams layoutParams = poster.getLayoutParams();
|
||||
layoutParams.width = layoutParams.height;
|
||||
poster.setLayoutParams(layoutParams);
|
||||
}
|
||||
}
|
||||
|
||||
public ImageView getPoster() {
|
||||
return poster;
|
||||
}
|
||||
|
||||
private void setupButtonClickListeners() {
|
||||
playButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
handleButtonClickEvent(v);
|
||||
}
|
||||
});
|
||||
|
||||
previousButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
handleButtonClickEvent(v);
|
||||
}
|
||||
});
|
||||
|
||||
nextButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
handleButtonClickEvent(v);
|
||||
}
|
||||
});
|
||||
|
||||
volumeMuteButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
handleButtonClickEvent(v);
|
||||
}
|
||||
});
|
||||
|
||||
shuffleButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
handleButtonClickEvent(v);
|
||||
}
|
||||
});
|
||||
|
||||
repeatModeButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
handleButtonClickEvent(v);
|
||||
}
|
||||
});
|
||||
|
||||
volumeMutedIndicatorButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
handleButtonClickEvent(v);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleButtonClickEvent(View view) {
|
||||
if (onPanelButtonsClickListener == null)
|
||||
return;
|
||||
|
||||
switch (view.getId()) {
|
||||
case R.id.npp_previous:
|
||||
onPanelButtonsClickListener.onPreviousClicked();
|
||||
break;
|
||||
case R.id.npp_next:
|
||||
onPanelButtonsClickListener.onNextClicked();
|
||||
break;
|
||||
case R.id.npp_play:
|
||||
onPanelButtonsClickListener.onPlayClicked();
|
||||
break;
|
||||
case R.id.npp_volume_mute:
|
||||
onPanelButtonsClickListener.onVolumeMuteClicked();
|
||||
break;
|
||||
case R.id.npp_repeat:
|
||||
onPanelButtonsClickListener.onRepeatClicked();
|
||||
break;
|
||||
case R.id.npp_shuffle:
|
||||
onPanelButtonsClickListener.onShuffleClicked();
|
||||
break;
|
||||
case R.id.npp_volume_muted_indicator:
|
||||
onPanelButtonsClickListener.onVolumeMutedIndicatorClicked();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,8 +33,8 @@ public class LogUtils {
|
|||
|
||||
// TODO: Remove this later
|
||||
private static final List<String> doNotLogTags = Arrays.asList(
|
||||
HostConnection.TAG,
|
||||
HostConnectionObserver.TAG
|
||||
// HostConnection.TAG,
|
||||
// HostConnectionObserver.TAG
|
||||
);
|
||||
|
||||
public static String makeLogTag(String str) {
|
||||
|
|
|
@ -93,6 +93,21 @@ public class UIUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the time format from {@link #formatTime(int, int, int)} to seconds
|
||||
* @param time
|
||||
* @return
|
||||
*/
|
||||
public static int timeToSeconds(String time) {
|
||||
String[] items = time.split(":");
|
||||
if (items.length > 2) {
|
||||
return (Integer.parseInt(items[0]) * 3600) + (Integer.parseInt(items[1]) * 60) +
|
||||
(Integer.parseInt(items[2]));
|
||||
} else {
|
||||
return (Integer.parseInt(items[0]) * 60) + (Integer.parseInt(items[1]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a file size, ISO prefixes
|
||||
*/
|
||||
|
@ -198,9 +213,9 @@ public class UIUtils {
|
|||
char charAvatar = TextUtils.isEmpty(str) ?
|
||||
' ' : str.charAt(0);
|
||||
int avatarColorsIdx = TextUtils.isEmpty(str) ? 0 :
|
||||
Math.max(Character.getNumericValue(str.charAt(0)) +
|
||||
Character.getNumericValue(str.charAt(str.length() - 1)) +
|
||||
str.length(), 0) % characterAvatarColors.length();
|
||||
Math.max(Character.getNumericValue(str.charAt(0)) +
|
||||
Character.getNumericValue(str.charAt(str.length() - 1)) +
|
||||
str.length(), 0) % characterAvatarColors.length();
|
||||
int color = characterAvatarColors.getColor(avatarColorsIdx, 0xff000000);
|
||||
// avatarColorsIdx = randomGenerator.nextInt(characterAvatarColors.length());
|
||||
return new CharacterDrawable(charAvatar, color);
|
||||
|
@ -210,12 +225,12 @@ public class UIUtils {
|
|||
static int iconPauseResId = R.drawable.ic_pause_white_24dp,
|
||||
iconPlayResId = R.drawable.ic_play_arrow_white_24dp;
|
||||
/**
|
||||
* Sets play/pause button icon on a ImageView, based on speed
|
||||
* Sets play/pause button icon on a ImageView
|
||||
* @param context Activity
|
||||
* @param view ImageView/ImageButton
|
||||
* @param speed Current player speed
|
||||
* @param play true if playing, false if paused
|
||||
*/
|
||||
public static void setPlayPauseButtonIcon(Context context, ImageView view, int speed) {
|
||||
public static void setPlayPauseButtonIcon(Context context, ImageView view, boolean play) {
|
||||
|
||||
if (!playPauseIconsLoaded) {
|
||||
TypedArray styledAttributes = context.obtainStyledAttributes(new int[]{R.attr.iconPause, R.attr.iconPlay});
|
||||
|
@ -225,7 +240,7 @@ public class UIUtils {
|
|||
playPauseIconsLoaded = true;
|
||||
}
|
||||
|
||||
view.setImageResource((speed == 1) ? iconPauseResId: iconPlayResId);
|
||||
view.setImageResource(play ? iconPauseResId : iconPlayResId );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -28,10 +28,17 @@
|
|||
|
||||
<include layout="@layout/toolbar_default" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/fragment_container"
|
||||
<org.xbmc.kore.ui.widgets.NowPlayingPanel
|
||||
xmlns:sothree="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/now_playing_panel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="bottom"
|
||||
sothree:umanoPanelHeight="@dimen/notification_art_slim_height"
|
||||
sothree:umanoShadowHeight="4dp"
|
||||
sothree:umanoFadeColor="#00000000"
|
||||
sothree:umanoInitialState="hidden"/>
|
||||
</LinearLayout>
|
||||
|
||||
<fragment android:id="@+id/navigation_drawer"
|
||||
|
|
|
@ -169,12 +169,14 @@
|
|||
style="@style/Widget.Button.Borderless"
|
||||
android:src="?attr/iconVolumeMute"
|
||||
android:contentDescription="@string/volume_mute"/>
|
||||
|
||||
<org.xbmc.kore.ui.widgets.VolumeLevelIndicator
|
||||
android:id="@+id/volume_level_indicator"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="2"
|
||||
android:orientation="vertical"/>
|
||||
android:orientation="vertical" />
|
||||
|
||||
<org.xbmc.kore.ui.widgets.RepeatModeButton
|
||||
android:id="@+id/repeat"
|
||||
android:layout_width="0dp"
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2017 Martijn Brekhof. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/fragment_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
<LinearLayout android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:background="?attr/colorPrimaryDark">
|
||||
|
||||
<ImageView android:id="@+id/npp_poster"
|
||||
android:layout_width="@dimen/notification_art_slim_width"
|
||||
android:layout_height="@dimen/notification_art_slim_height"
|
||||
android:layout_weight="0"
|
||||
android:scaleType="centerInside"
|
||||
android:contentDescription="@string/poster"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/notification_art_slim_height"
|
||||
android:orientation="vertical"
|
||||
android:layout_weight="1">
|
||||
<TextView
|
||||
android:id="@+id/npp_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="@dimen/small_padding"
|
||||
android:paddingRight="@dimen/small_padding"
|
||||
android:textAppearance="@style/TextAppearance.Notification.Title"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"
|
||||
android:gravity="center_vertical"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/npp_details"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="@dimen/small_padding"
|
||||
android:paddingRight="@dimen/small_padding"
|
||||
android:textAppearance="@style/TextAppearance.Notification.Details"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"
|
||||
android:gravity="center_vertical"/>
|
||||
</LinearLayout>
|
||||
<org.xbmc.kore.ui.widgets.HighlightButton
|
||||
android:id="@+id/npp_volume_muted_indicator"
|
||||
android:layout_width="@dimen/notification_art_slim_height"
|
||||
android:layout_height="@dimen/notification_art_slim_height"
|
||||
style="@style/Widget.Button.Borderless"
|
||||
android:src="?attr/iconVolumeMute"
|
||||
android:contentDescription="@string/volume_mute"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/npp_previous"
|
||||
android:layout_width="@dimen/notification_art_slim_height"
|
||||
android:layout_height="@dimen/notification_art_slim_height"
|
||||
style="@style/Widget.Button.Borderless"
|
||||
android:src="?attr/iconPrevious"
|
||||
android:contentDescription="@string/previous"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/npp_play"
|
||||
android:layout_width="@dimen/notification_art_slim_height"
|
||||
android:layout_height="@dimen/notification_art_slim_height"
|
||||
style="@style/Widget.Button.Borderless"
|
||||
android:src="?attr/iconPlay"
|
||||
android:contentDescription="@string/play"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/npp_next"
|
||||
android:layout_width="@dimen/notification_art_slim_height"
|
||||
android:layout_height="@dimen/notification_art_slim_height"
|
||||
style="@style/Widget.Button.Borderless"
|
||||
android:src="?attr/iconNext"
|
||||
android:contentDescription="@string/next"/>
|
||||
</LinearLayout>
|
||||
<org.xbmc.kore.ui.widgets.MediaProgressIndicator
|
||||
android:id="@+id/npp_progress_indicator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="@dimen/default_padding"
|
||||
android:paddingRight="@dimen/small_padding"
|
||||
android:paddingLeft="@dimen/small_padding"
|
||||
android:orientation="horizontal"
|
||||
android:background="?attr/contentBackgroundColor"
|
||||
style="@style/TextAppearance.Media.Progress"/>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/buttonbar_height"
|
||||
android:orientation="horizontal"
|
||||
style="@style/ButtonBar"
|
||||
android:background="?attr/contentBackgroundColor">
|
||||
|
||||
<org.xbmc.kore.ui.widgets.HighlightButton
|
||||
android:id="@+id/npp_volume_mute"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="match_parent"
|
||||
style="@style/Widget.Button.Borderless"
|
||||
android:src="?attr/iconVolumeMute"
|
||||
android:contentDescription="@string/volume_mute"/>
|
||||
<org.xbmc.kore.ui.widgets.VolumeLevelIndicator
|
||||
android:id="@+id/npp_volume_level_indicator"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="2"
|
||||
android:orientation="vertical">
|
||||
</org.xbmc.kore.ui.widgets.VolumeLevelIndicator>
|
||||
|
||||
<org.xbmc.kore.ui.widgets.RepeatModeButton
|
||||
android:id="@+id/npp_repeat"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="match_parent"
|
||||
style="@style/Widget.Button.Borderless"
|
||||
android:src="?attr/iconRepeat"
|
||||
android:contentDescription="@string/repeat"/>
|
||||
<org.xbmc.kore.ui.widgets.HighlightButton
|
||||
android:id="@+id/npp_shuffle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="match_parent"
|
||||
style="@style/Widget.Button.Borderless"
|
||||
android:src="?attr/iconShuffle"
|
||||
android:contentDescription="@string/shuffle"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</merge>
|
|
@ -128,7 +128,8 @@
|
|||
<dimen name="notification_expanded_height">128dp</dimen>
|
||||
<dimen name="notification_art_default_height">64dp</dimen>
|
||||
<dimen name="notification_art_default_width">64dp</dimen>
|
||||
<dimen name="notification_art_slim_width">44dp</dimen>
|
||||
<dimen name="notification_art_slim_width">31dp</dimen>
|
||||
<dimen name="notification_art_slim_height">44dp</dimen>
|
||||
<dimen name="notification_expanded_art_default_height">128dp</dimen>
|
||||
<dimen name="notification_expanded_art_default_width">128dp</dimen>
|
||||
<dimen name="notification_expanded_art_slim_width">88dp</dimen>
|
||||
|
|
|
@ -346,6 +346,8 @@
|
|||
<string name="keep_remote_above_lockscreen">Show over lockscreen</string>
|
||||
<string name="pref_keep_screen_on">Stay awake</string>
|
||||
<string name="show_notification">Show notification while playing</string>
|
||||
<string name="show_now_playing_panel">Show now playing panel while playing</string>
|
||||
<string name="show_now_playing_panel_summary">Displays an expandable panel at the bottom of the screen when media is playing or paused</string>
|
||||
<string name="pause_during_calls">Pause during phone call</string>
|
||||
<string name="use_hardware_volume_keys">Use device volume keys</string>
|
||||
<string name="vibrate_on_remote">Vibrate on touch</string>
|
||||
|
|
|
@ -64,6 +64,12 @@
|
|||
android:title="@string/show_notification"
|
||||
android:defaultValue="false"/>
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="pref_show_nowplayingpanel"
|
||||
android:title="@string/show_now_playing_panel"
|
||||
android:summary="@string/show_now_playing_panel_summary"
|
||||
android:defaultValue="true"/>
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:key="pref_pause_during_calls"
|
||||
android:title="@string/pause_during_calls"
|
||||
|
|
Loading…
Reference in New Issue