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)
|
- [PagerSlidingTabStrip](https://github.com/astuetz/PagerSlidingTabStrip)
|
||||||
- [FloatingActionButton](https://github.com/makovkastar/FloatingActionButton)
|
- [FloatingActionButton](https://github.com/makovkastar/FloatingActionButton)
|
||||||
- [ExpandableTextView](https://github.com/Blogcat/Android-ExpandableTextView)
|
- [ExpandableTextView](https://github.com/Blogcat/Android-ExpandableTextView)
|
||||||
|
- [AndroidSlidingUpPanel](https://github.com/umano/AndroidSlidingUpPanel)
|
||||||
|
|
||||||
Links
|
Links
|
||||||
-----
|
-----
|
||||||
|
|
|
@ -125,6 +125,7 @@ dependencies {
|
||||||
compile 'com.astuetz:pagerslidingtabstrip:1.0.1'
|
compile 'com.astuetz:pagerslidingtabstrip:1.0.1'
|
||||||
compile 'com.melnykov:floatingactionbutton:1.3.0'
|
compile 'com.melnykov:floatingactionbutton:1.3.0'
|
||||||
compile 'at.blogc:expandabletextview:1.0.3'
|
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:runner:0.5'
|
||||||
androidTestCompile 'com.android.support.test:rules: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 String KEY_PREF_SHOW_NOTIFICATION = "pref_show_notification";
|
||||||
public static final boolean DEFAULT_PREF_SHOW_NOTIFICATION = false;
|
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
|
// Pause during calls
|
||||||
public static final String KEY_PREF_PAUSE_DURING_CALLS = "pref_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;
|
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.JSONRPC;
|
||||||
import org.xbmc.kore.jsonrpc.method.Player;
|
import org.xbmc.kore.jsonrpc.method.Player;
|
||||||
import org.xbmc.kore.jsonrpc.notification.Application;
|
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.Input;
|
||||||
import org.xbmc.kore.jsonrpc.notification.System;
|
import org.xbmc.kore.jsonrpc.notification.System;
|
||||||
import org.xbmc.kore.jsonrpc.type.ApplicationType;
|
import org.xbmc.kore.jsonrpc.type.ApplicationType;
|
||||||
|
@ -73,6 +74,8 @@ public class HostConnectionObserver
|
||||||
PLAYER_IS_PAUSED = 3,
|
PLAYER_IS_PAUSED = 3,
|
||||||
PLAYER_IS_STOPPED = 4;
|
PLAYER_IS_STOPPED = 4;
|
||||||
|
|
||||||
|
public void playerOnPropertyChanged(NotificationsData notificationsData);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies that something is playing
|
* Notifies that something is playing
|
||||||
* @param getActivePlayerResult Active player obtained by a call to {@link org.xbmc.kore.jsonrpc.method.Player.GetActivePlayers}
|
* @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,
|
final int PING_AFTER_ERROR_CHECK_INTERVAL = 2000,
|
||||||
PING_AFTER_SUCCESS_CHECK_INTERVAL = 10000;
|
PING_AFTER_SUCCESS_CHECK_INTERVAL = 10000;
|
||||||
// If no one is listening to this, just exit
|
// 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();
|
JSONRPC.Ping ping = new JSONRPC.Ping();
|
||||||
ping.execute(connection, new ApiCallback<String>() {
|
ping.execute(connection, new ApiCallback<String>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(String result) {
|
public void onSuccess(String result) {
|
||||||
// Ok, we've got a ping, if we were in a error or uninitialized state, update
|
// Ok, we've got a ping, if there are playerEventsObservers and
|
||||||
if ((hostState.lastCallResult == PlayerEventsObserver.PLAYER_NO_RESULT) ||
|
// we were in a error or uninitialized state, update
|
||||||
(hostState.lastCallResult == PlayerEventsObserver.PLAYER_CONNECTION_ERROR)) {
|
if ((! playerEventsObservers.isEmpty()) &&
|
||||||
|
((hostState.lastCallResult == PlayerEventsObserver.PLAYER_NO_RESULT) ||
|
||||||
|
(hostState.lastCallResult == PlayerEventsObserver.PLAYER_CONNECTION_ERROR))) {
|
||||||
checkWhatsPlaying();
|
checkWhatsPlaying();
|
||||||
}
|
}
|
||||||
checkerHandler.postDelayed(tcpCheckerRunnable, PING_AFTER_SUCCESS_CHECK_INTERVAL);
|
checkerHandler.postDelayed(tcpCheckerRunnable, PING_AFTER_SUCCESS_CHECK_INTERVAL);
|
||||||
|
@ -244,9 +249,7 @@ public class HostConnectionObserver
|
||||||
if (this.connection == null)
|
if (this.connection == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Save this observer and a new handle to notify him
|
|
||||||
playerEventsObservers.add(observer);
|
playerEventsObservers.add(observer);
|
||||||
// observerHandlerMap.put(observer, new Handler());
|
|
||||||
|
|
||||||
if (replyImmediately) replyWithLastResult(observer);
|
if (replyImmediately) replyWithLastResult(observer);
|
||||||
|
|
||||||
|
@ -316,6 +319,7 @@ public class HostConnectionObserver
|
||||||
// as a connection observer, which we will pass to the "real" observer
|
// as a connection observer, which we will pass to the "real" observer
|
||||||
if (connection.getProtocol() == HostConnection.PROTOCOL_TCP) {
|
if (connection.getProtocol() == HostConnection.PROTOCOL_TCP) {
|
||||||
connection.registerApplicationNotificationsObserver(this, checkerHandler);
|
connection.registerApplicationNotificationsObserver(this, checkerHandler);
|
||||||
|
checkerHandler.post(tcpCheckerRunnable);
|
||||||
} else {
|
} else {
|
||||||
checkerHandler.post(httpApplicationCheckerRunnable);
|
checkerHandler.post(httpApplicationCheckerRunnable);
|
||||||
}
|
}
|
||||||
|
@ -337,7 +341,7 @@ public class HostConnectionObserver
|
||||||
// No more observers, so unregister us from the host connection, or stop
|
// No more observers, so unregister us from the host connection, or stop
|
||||||
// the http checker thread
|
// the http checker thread
|
||||||
if (connection.getProtocol() == HostConnection.PROTOCOL_TCP) {
|
if (connection.getProtocol() == HostConnection.PROTOCOL_TCP) {
|
||||||
connection.unregisterApplicationotificationsObserver(this);
|
connection.unregisterApplicationNotificationsObserver(this);
|
||||||
} else {
|
} else {
|
||||||
checkerHandler.removeCallbacks(httpApplicationCheckerRunnable);
|
checkerHandler.removeCallbacks(httpApplicationCheckerRunnable);
|
||||||
}
|
}
|
||||||
|
@ -364,6 +368,14 @@ public class HostConnectionObserver
|
||||||
hostState.lastCallResult = PlayerEventsObserver.PLAYER_NO_RESULT;
|
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
|
* The {@link HostConnection.PlayerNotificationsObserver} interface methods
|
||||||
*/
|
*/
|
||||||
|
@ -746,7 +758,7 @@ public class HostConnectionObserver
|
||||||
/**
|
/**
|
||||||
* Replies to the observer with the last result we got.
|
* Replies to the observer with the last result we got.
|
||||||
* If we have no result, nothing will be called on the observer interface.
|
* 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) {
|
public void replyWithLastResult(PlayerEventsObserver observer) {
|
||||||
switch (hostState.lastCallResult) {
|
switch (hostState.lastCallResult) {
|
||||||
|
|
|
@ -42,7 +42,6 @@ import java.io.BufferedWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.ProtocolException;
|
import java.net.ProtocolException;
|
||||||
import java.net.Proxy;
|
import java.net.Proxy;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
@ -70,6 +69,7 @@ public class HostConnection {
|
||||||
* Interface that an observer must implement to be notified of player notifications
|
* Interface that an observer must implement to be notified of player notifications
|
||||||
*/
|
*/
|
||||||
public interface PlayerNotificationsObserver {
|
public interface PlayerNotificationsObserver {
|
||||||
|
public void onPropertyChanged(Player.OnPropertyChanged notification);
|
||||||
public void onPlay(Player.OnPlay notification);
|
public void onPlay(Player.OnPlay notification);
|
||||||
public void onPause(Player.OnPause notification);
|
public void onPause(Player.OnPause notification);
|
||||||
public void onSpeedChanged(Player.OnSpeedChanged notification);
|
public void onSpeedChanged(Player.OnSpeedChanged notification);
|
||||||
|
@ -274,7 +274,7 @@ public class HostConnection {
|
||||||
* Unregisters and observer from the input notifications
|
* Unregisters and observer from the input notifications
|
||||||
* @param observer The {@link InputNotificationsObserver}
|
* @param observer The {@link InputNotificationsObserver}
|
||||||
*/
|
*/
|
||||||
public void unregisterApplicationotificationsObserver(ApplicationNotificationsObserver observer) {
|
public void unregisterApplicationNotificationsObserver(ApplicationNotificationsObserver observer) {
|
||||||
applicationNotificationsObservers.remove(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)) {
|
} else if (notificationName.equals(System.OnQuit.NOTIFICATION_NAME)) {
|
||||||
final System.OnQuit apiNotification = new System.OnQuit(params);
|
final System.OnQuit apiNotification = new System.OnQuit(params);
|
||||||
for (final SystemNotificationsObserver observer :
|
for (final SystemNotificationsObserver observer :
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package org.xbmc.kore.jsonrpc.notification;
|
package org.xbmc.kore.jsonrpc.notification;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.BooleanNode;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import org.xbmc.kore.jsonrpc.ApiNotification;
|
import org.xbmc.kore.jsonrpc.ApiNotification;
|
||||||
import org.xbmc.kore.jsonrpc.type.GlobalType;
|
import org.xbmc.kore.jsonrpc.type.GlobalType;
|
||||||
|
@ -26,6 +27,23 @@ import org.xbmc.kore.utils.JsonUtils;
|
||||||
*/
|
*/
|
||||||
public class Player {
|
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
|
* Player.OnPause notification
|
||||||
* Playback of a media item has been paused. If there is no ID available extra information will be provided.
|
* 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 class NotificationsData {
|
||||||
public static final String DATA_NODE = "data";
|
public static final String DATA_NODE = "data";
|
||||||
|
|
||||||
public final NotificationsPlayer player;
|
public final NotificationsPlayer player;
|
||||||
public final NotificationsItem item;
|
public final NotificationsItem item;
|
||||||
|
public final NotificationsProperty property;
|
||||||
|
|
||||||
public NotificationsData(JsonNode node) {
|
public NotificationsData(JsonNode node) {
|
||||||
item = new NotificationsItem((ObjectNode)node.get(NotificationsItem.ITEM_NODE));
|
JsonNode jsonNode = node.get(NotificationsItem.ITEM_NODE);
|
||||||
player = new NotificationsPlayer((ObjectNode)node.get(NotificationsPlayer.PLAYER_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.Settings;
|
||||||
import org.xbmc.kore.host.HostConnectionObserver;
|
import org.xbmc.kore.host.HostConnectionObserver;
|
||||||
import org.xbmc.kore.host.HostManager;
|
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.ListType;
|
||||||
import org.xbmc.kore.jsonrpc.type.PlayerType;
|
import org.xbmc.kore.jsonrpc.type.PlayerType;
|
||||||
import org.xbmc.kore.utils.LogUtils;
|
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
|
* HostConnectionObserver.PlayerEventsObserver interface callbacks
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -17,10 +17,8 @@ package org.xbmc.kore.service;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.NotificationManager;
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
@ -36,6 +34,7 @@ import com.squareup.picasso.Target;
|
||||||
import org.xbmc.kore.R;
|
import org.xbmc.kore.R;
|
||||||
import org.xbmc.kore.host.HostConnectionObserver;
|
import org.xbmc.kore.host.HostConnectionObserver;
|
||||||
import org.xbmc.kore.host.HostManager;
|
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.ListType;
|
||||||
import org.xbmc.kore.jsonrpc.type.PlayerType;
|
import org.xbmc.kore.jsonrpc.type.PlayerType;
|
||||||
import org.xbmc.kore.ui.sections.remote.RemoteActivity;
|
import org.xbmc.kore.ui.sections.remote.RemoteActivity;
|
||||||
|
@ -68,6 +67,11 @@ public class NotificationObserver
|
||||||
mRemoteStartPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
|
mRemoteStartPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void playerOnPropertyChanged(Player.NotificationsData notificationsData) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HostConnectionObserver.PlayerEventsObserver interface callbacks
|
* 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
|
@Override
|
||||||
public void playerOnPlay(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
public void playerOnPlay(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||||
PlayerType.PropertyValue getPropertiesResult,
|
PlayerType.PropertyValue getPropertiesResult,
|
||||||
|
|
|
@ -17,43 +17,88 @@ package org.xbmc.kore.ui;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.res.Resources;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.app.FragmentTransaction;
|
import android.support.v4.app.FragmentTransaction;
|
||||||
import android.support.v4.widget.DrawerLayout;
|
import android.support.v4.widget.DrawerLayout;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.transition.TransitionInflater;
|
import android.transition.TransitionInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
|
||||||
|
import com.sothree.slidinguppanel.SlidingUpPanelLayout;
|
||||||
|
|
||||||
import org.xbmc.kore.R;
|
import org.xbmc.kore.R;
|
||||||
import org.xbmc.kore.Settings;
|
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.generic.NavigationDrawerFragment;
|
||||||
import org.xbmc.kore.ui.sections.remote.RemoteActivity;
|
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.LogUtils;
|
||||||
import org.xbmc.kore.utils.SharedElementTransition;
|
import org.xbmc.kore.utils.SharedElementTransition;
|
||||||
import org.xbmc.kore.utils.UIUtils;
|
import org.xbmc.kore.utils.UIUtils;
|
||||||
import org.xbmc.kore.utils.Utils;
|
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 TAG = LogUtils.makeLogTag(BaseMediaActivity.class);
|
||||||
|
|
||||||
private static final String NAVICON_ISARROW = "navstate";
|
private static final String NAVICON_ISARROW = "navstate";
|
||||||
private static final String ACTIONBAR_TITLE = "actionbartitle";
|
private static final String ACTIONBAR_TITLE = "actionbartitle";
|
||||||
|
|
||||||
|
@InjectView(R.id.now_playing_panel) NowPlayingPanel nowPlayingPanel;
|
||||||
|
|
||||||
private NavigationDrawerFragment navigationDrawerFragment;
|
private NavigationDrawerFragment navigationDrawerFragment;
|
||||||
private SharedElementTransition sharedElementTransition = new SharedElementTransition();
|
private SharedElementTransition sharedElementTransition = new SharedElementTransition();
|
||||||
|
|
||||||
private boolean drawerIndicatorIsArrow;
|
private boolean drawerIndicatorIsArrow;
|
||||||
|
private int currentActivePlayerId = -1;
|
||||||
|
|
||||||
|
private HostManager hostManager;
|
||||||
|
private HostConnectionObserver hostConnectionObserver;
|
||||||
|
|
||||||
|
private boolean showNowPlayingPanel;
|
||||||
|
|
||||||
protected abstract String getActionBarTitle();
|
protected abstract String getActionBarTitle();
|
||||||
protected abstract Fragment createFragment();
|
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
|
@Override
|
||||||
@TargetApi(21)
|
@TargetApi(21)
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
@ -61,13 +106,10 @@ public abstract class BaseMediaActivity extends AppCompatActivity {
|
||||||
if (Utils.isLollipopOrLater()) {
|
if (Utils.isLollipopOrLater()) {
|
||||||
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
|
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);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
setContentView(R.layout.activity_generic_media);
|
setContentView(R.layout.activity_generic_media);
|
||||||
|
ButterKnife.inject(this);
|
||||||
|
|
||||||
// Set up the drawer.
|
// Set up the drawer.
|
||||||
navigationDrawerFragment = (NavigationDrawerFragment)getSupportFragmentManager()
|
navigationDrawerFragment = (NavigationDrawerFragment)getSupportFragmentManager()
|
||||||
|
@ -113,6 +155,8 @@ public abstract class BaseMediaActivity extends AppCompatActivity {
|
||||||
if (Utils.isLollipopOrLater()) {
|
if (Utils.isLollipopOrLater()) {
|
||||||
sharedElementTransition.setupExitTransition(this, fragment);
|
sharedElementTransition.setupExitTransition(this, fragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hostManager = HostManager.getInstance(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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() {
|
public boolean getDrawerIndicatorIsArrow() {
|
||||||
return drawerIndicatorIsArrow;
|
return drawerIndicatorIsArrow;
|
||||||
}
|
}
|
||||||
|
@ -187,4 +263,272 @@ public abstract class BaseMediaActivity extends AppCompatActivity {
|
||||||
.addToBackStack(null)
|
.addToBackStack(null)
|
||||||
.commit();
|
.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 interface NowPlayingListener {
|
||||||
public void SwitchToRemotePanel();
|
public void SwitchToRemotePanel();
|
||||||
public void onShuffleClicked();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -126,19 +125,17 @@ public class NowPlayingFragment extends Fragment
|
||||||
private int currentSubtitleIndex = -1;
|
private int currentSubtitleIndex = -1;
|
||||||
private int currentAudiostreamIndex = -1;
|
private int currentAudiostreamIndex = -1;
|
||||||
|
|
||||||
|
private ApiCallback<Integer> defaultIntActionCallback = ApiMethod.getDefaultActionCallback();
|
||||||
|
private ApiCallback<Boolean> defaultBooleanActionCallback = ApiMethod.getDefaultActionCallback();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Injectable views
|
* Injectable views
|
||||||
*/
|
*/
|
||||||
@InjectView(R.id.play) ImageButton playButton;
|
@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.volume_mute) HighlightButton volumeMuteButton;
|
||||||
@InjectView(R.id.shuffle) HighlightButton shuffleButton;
|
@InjectView(R.id.shuffle) HighlightButton shuffleButton;
|
||||||
|
@InjectView(R.id.repeat) RepeatModeButton repeatButton;
|
||||||
@InjectView(R.id.overflow) ImageButton overflowButton;
|
@InjectView(R.id.overflow) ImageButton overflowButton;
|
||||||
|
|
||||||
@InjectView(R.id.info_panel) RelativeLayout infoPanel;
|
@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.volume_level_indicator) VolumeLevelIndicator volumeLevelIndicator;
|
||||||
|
|
||||||
@InjectView(R.id.media_details) RelativeLayout mediaDetailsPanel;
|
|
||||||
@InjectView(R.id.rating) TextView mediaRating;
|
@InjectView(R.id.rating) TextView mediaRating;
|
||||||
@InjectView(R.id.max_rating) TextView mediaMaxRating;
|
@InjectView(R.id.max_rating) TextView mediaMaxRating;
|
||||||
@InjectView(R.id.year) TextView mediaYear;
|
@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);
|
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_now_playing, container, false);
|
||||||
ButterKnife.inject(this, root);
|
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
|
// Setup dim the fanart when scroll changes
|
||||||
// Full dim on 4 * iconSize dp
|
// Full dim on 4 * iconSize dp
|
||||||
Resources resources = getActivity().getResources();
|
Resources resources = getActivity().getResources();
|
||||||
|
@ -242,8 +248,6 @@ public class NowPlayingFragment extends Fragment
|
||||||
* Default callback for methods that don't return anything
|
* Default callback for methods that don't return anything
|
||||||
*/
|
*/
|
||||||
private ApiCallback<String> defaultStringActionCallback = ApiMethod.getDefaultActionCallback();
|
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
|
* Callback for methods that change the play speed
|
||||||
|
@ -252,7 +256,7 @@ public class NowPlayingFragment extends Fragment
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Integer result) {
|
public void onSuccess(Integer result) {
|
||||||
if (!isAdded()) return;
|
if (!isAdded()) return;
|
||||||
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, result);
|
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, result == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -272,7 +276,7 @@ public class NowPlayingFragment extends Fragment
|
||||||
public void onStopClicked(View v) {
|
public void onStopClicked(View v) {
|
||||||
Player.Stop action = new Player.Stop(currentActivePlayerId);
|
Player.Stop action = new Player.Stop(currentActivePlayerId);
|
||||||
action.execute(hostManager.getConnection(), defaultStringActionCallback, callbackHandler);
|
action.execute(hostManager.getConnection(), defaultStringActionCallback, callbackHandler);
|
||||||
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, 0);
|
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnClick(R.id.fast_forward)
|
@OnClick(R.id.fast_forward)
|
||||||
|
@ -299,17 +303,8 @@ public class NowPlayingFragment extends Fragment
|
||||||
action.execute(hostManager.getConnection(), defaultStringActionCallback, callbackHandler);
|
action.execute(hostManager.getConnection(), defaultStringActionCallback, callbackHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Calllbacks for media button toolbar
|
|
||||||
*/
|
|
||||||
@OnClick(R.id.volume_mute)
|
@OnClick(R.id.volume_mute)
|
||||||
public void onVolumeMuteClicked(View v) {
|
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();
|
Application.SetMute action = new Application.SetMute();
|
||||||
action.execute(hostManager.getConnection(), defaultBooleanActionCallback, new Handler());
|
action.execute(hostManager.getConnection(), defaultBooleanActionCallback, new Handler());
|
||||||
}
|
}
|
||||||
|
@ -323,7 +318,6 @@ public class NowPlayingFragment extends Fragment
|
||||||
if (!isAdded()) return;
|
if (!isAdded()) return;
|
||||||
// Force a refresh
|
// Force a refresh
|
||||||
hostConnectionObserver.forceRefreshResults();
|
hostConnectionObserver.forceRefreshResults();
|
||||||
nowPlayingListener.onShuffleClicked();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -539,20 +533,12 @@ public class NowPlayingFragment extends Fragment
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onProgressChanged(int progress) {
|
public void playerOnPropertyChanged(org.xbmc.kore.jsonrpc.notification.Player.NotificationsData notificationsData) {
|
||||||
PlayerType.PositionTime positionTime = new PlayerType.PositionTime(progress);
|
if (notificationsData.property.shuffled != null)
|
||||||
Player.Seek seekAction = new Player.Seek(currentActivePlayerId, positionTime);
|
shuffleButton.setHighlight(notificationsData.property.shuffled);
|
||||||
seekAction.execute(hostManager.getConnection(), new ApiCallback<PlayerType.SeekReturnType>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(PlayerType.SeekReturnType result) {
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
if (notificationsData.property.repeatMode != null)
|
||||||
public void onError(int errorCode, String description) {
|
UIUtils.setRepeatButton(repeatButton, notificationsData.property.repeatMode);
|
||||||
LogUtils.LOGD(TAG, "Got an error calling Player.Seek. Error code: " + errorCode + ", description: " + description);
|
|
||||||
}
|
|
||||||
}, callbackHandler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -561,19 +547,19 @@ public class NowPlayingFragment extends Fragment
|
||||||
public void playerOnPlay(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
public void playerOnPlay(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||||
PlayerType.PropertyValue getPropertiesResult,
|
PlayerType.PropertyValue getPropertiesResult,
|
||||||
ListType.ItemsAll getItemResult) {
|
ListType.ItemsAll getItemResult) {
|
||||||
setNowPlayingInfo(getPropertiesResult, getItemResult);
|
setNowPlayingInfo(getActivePlayerResult, getPropertiesResult, getItemResult);
|
||||||
currentActivePlayerId = getActivePlayerResult.playerid;
|
currentActivePlayerId = getActivePlayerResult.playerid;
|
||||||
// Switch icon
|
// Switch icon
|
||||||
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, getPropertiesResult.speed);
|
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, getPropertiesResult.speed == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void playerOnPause(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
public void playerOnPause(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||||
PlayerType.PropertyValue getPropertiesResult,
|
PlayerType.PropertyValue getPropertiesResult,
|
||||||
ListType.ItemsAll getItemResult) {
|
ListType.ItemsAll getItemResult) {
|
||||||
setNowPlayingInfo(getPropertiesResult, getItemResult);
|
setNowPlayingInfo(getActivePlayerResult, getPropertiesResult, getItemResult);
|
||||||
currentActivePlayerId = getActivePlayerResult.playerid;
|
currentActivePlayerId = getActivePlayerResult.playerid;
|
||||||
// Switch icon
|
// Switch icon
|
||||||
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, getPropertiesResult.speed);
|
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, getPropertiesResult.speed == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void playerOnStop() {
|
public void playerOnStop() {
|
||||||
|
@ -619,18 +605,38 @@ public class NowPlayingFragment extends Fragment
|
||||||
@Override
|
@Override
|
||||||
public void applicationOnVolumeChanged(int volume, boolean muted) {
|
public void applicationOnVolumeChanged(int volume, boolean muted) {
|
||||||
volumeLevelIndicator.setVolume(muted, volume);
|
volumeLevelIndicator.setVolume(muted, volume);
|
||||||
UIUtils.highlightImageView(getActivity(), volumeMuteButton, muted);
|
volumeMuteButton.setHighlight(muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore this
|
// Ignore this
|
||||||
public void inputOnInputRequested(String title, String type, String value) {}
|
public void inputOnInputRequested(String title, String type, String value) {}
|
||||||
public void observerOnStopObserving() {}
|
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
|
* Sets whats playing information
|
||||||
* @param getItemResult Return from method {@link org.xbmc.kore.jsonrpc.method.Player.GetItem}
|
* @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 ListType.ItemsAll getItemResult) {
|
||||||
final String title, underTitle, art, poster, genreSeason, year,
|
final String title, underTitle, art, poster, genreSeason, year,
|
||||||
descriptionPlot, votes, maxRating;
|
descriptionPlot, votes, maxRating;
|
||||||
|
@ -776,7 +782,6 @@ public class NowPlayingFragment extends Fragment
|
||||||
}
|
}
|
||||||
|
|
||||||
UIUtils.setRepeatButton(repeatButton, getPropertiesResult.repeat);
|
UIUtils.setRepeatButton(repeatButton, getPropertiesResult.repeat);
|
||||||
|
|
||||||
shuffleButton.setHighlight(getPropertiesResult.shuffled);
|
shuffleButton.setHighlight(getPropertiesResult.shuffled);
|
||||||
|
|
||||||
Resources resources = getActivity().getResources();
|
Resources resources = getActivity().getResources();
|
||||||
|
|
|
@ -194,6 +194,12 @@ public class PlaylistFragment extends Fragment
|
||||||
private PlayerType.PropertyValue lastGetPropertiesResult;
|
private PlayerType.PropertyValue lastGetPropertiesResult;
|
||||||
private List<ListType.ItemsAll> lastGetPlaylistItemsResult = null;
|
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
|
* HostConnectionObserver.PlayerEventsObserver interface callbacks
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -610,6 +610,12 @@ public class RemoteActivity extends BaseActivity
|
||||||
* HostConnectionObserver.PlayerEventsObserver interface callbacks
|
* HostConnectionObserver.PlayerEventsObserver interface callbacks
|
||||||
*/
|
*/
|
||||||
private String lastImageUrl = null;
|
private String lastImageUrl = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void playerOnPropertyChanged(org.xbmc.kore.jsonrpc.notification.Player.NotificationsData notificationsData) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public void playerOnPlay(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
public void playerOnPlay(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||||
PlayerType.PropertyValue getPropertiesResult,
|
PlayerType.PropertyValue getPropertiesResult,
|
||||||
ListType.ItemsAll getItemResult) {
|
ListType.ItemsAll getItemResult) {
|
||||||
|
@ -690,11 +696,6 @@ public class RemoteActivity extends BaseActivity
|
||||||
viewPager.setCurrentItem(1);
|
viewPager.setCurrentItem(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onShuffleClicked() {
|
|
||||||
refreshPlaylist();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void refreshPlaylist() {
|
private void refreshPlaylist() {
|
||||||
String tag = "android:switcher:" + viewPager.getId() + ":" + PLAYLIST_FRAGMENT_ID;
|
String tag = "android:switcher:" + viewPager.getId() + ":" + PLAYLIST_FRAGMENT_ID;
|
||||||
PlaylistFragment playlistFragment = (PlaylistFragment)getSupportFragmentManager()
|
PlaylistFragment playlistFragment = (PlaylistFragment)getSupportFragmentManager()
|
||||||
|
|
|
@ -552,13 +552,18 @@ public class RemoteFragment extends Fragment
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Integer result) {
|
public void onSuccess(Integer result) {
|
||||||
if (!isAdded()) return;
|
if (!isAdded()) return;
|
||||||
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, result);
|
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, result == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(int errorCode, String description) { }
|
public void onError(int errorCode, String description) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void playerOnPropertyChanged(org.xbmc.kore.jsonrpc.notification.Player.NotificationsData notificationsData) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HostConnectionObserver.PlayerEventsObserver interface callbacks
|
* HostConnectionObserver.PlayerEventsObserver interface callbacks
|
||||||
*/
|
*/
|
||||||
|
@ -569,7 +574,7 @@ public class RemoteFragment extends Fragment
|
||||||
currentActivePlayerId = getActivePlayerResult.playerid;
|
currentActivePlayerId = getActivePlayerResult.playerid;
|
||||||
currentNowPlayingItemType = getItemResult.type;
|
currentNowPlayingItemType = getItemResult.type;
|
||||||
// Switch icon
|
// Switch icon
|
||||||
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, getPropertiesResult.speed);
|
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, getPropertiesResult.speed == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void playerOnPause(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
public void playerOnPause(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||||
|
@ -579,7 +584,7 @@ public class RemoteFragment extends Fragment
|
||||||
currentActivePlayerId = getActivePlayerResult.playerid;
|
currentActivePlayerId = getActivePlayerResult.playerid;
|
||||||
currentNowPlayingItemType = getItemResult.type;
|
currentNowPlayingItemType = getItemResult.type;
|
||||||
// Switch icon
|
// Switch icon
|
||||||
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, getPropertiesResult.speed);
|
UIUtils.setPlayPauseButtonIcon(getActivity(), playButton, getPropertiesResult.speed == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void playerOnStop() {
|
public void playerOnStop() {
|
||||||
|
|
|
@ -18,6 +18,8 @@ package org.xbmc.kore.ui.widgets;
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
@ -25,6 +27,7 @@ import android.widget.SeekBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.xbmc.kore.R;
|
import org.xbmc.kore.R;
|
||||||
|
import org.xbmc.kore.utils.LogUtils;
|
||||||
import org.xbmc.kore.utils.UIUtils;
|
import org.xbmc.kore.utils.UIUtils;
|
||||||
|
|
||||||
public class MediaProgressIndicator extends LinearLayout {
|
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() {
|
private Runnable seekBarUpdater = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
@ -119,6 +140,10 @@ public class MediaProgressIndicator extends LinearLayout {
|
||||||
progressTextView.setText(UIUtils.formatTime(progress));
|
progressTextView.setText(UIUtils.formatTime(progress));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getProgress() {
|
||||||
|
return progress;
|
||||||
|
}
|
||||||
|
|
||||||
public void setMaxProgress(int max) {
|
public void setMaxProgress(int max) {
|
||||||
maxProgress = max;
|
maxProgress = max;
|
||||||
seekBar.setMax(max);
|
seekBar.setMax(max);
|
||||||
|
@ -140,4 +165,37 @@ public class MediaProgressIndicator extends LinearLayout {
|
||||||
if (speed > 0)
|
if (speed > 0)
|
||||||
seekBar.postDelayed(seekBarUpdater, SEEK_BAR_UPDATE_INTERVAL);
|
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
|
// TODO: Remove this later
|
||||||
private static final List<String> doNotLogTags = Arrays.asList(
|
private static final List<String> doNotLogTags = Arrays.asList(
|
||||||
HostConnection.TAG,
|
// HostConnection.TAG,
|
||||||
HostConnectionObserver.TAG
|
// HostConnectionObserver.TAG
|
||||||
);
|
);
|
||||||
|
|
||||||
public static String makeLogTag(String str) {
|
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
|
* Formats a file size, ISO prefixes
|
||||||
*/
|
*/
|
||||||
|
@ -198,9 +213,9 @@ public class UIUtils {
|
||||||
char charAvatar = TextUtils.isEmpty(str) ?
|
char charAvatar = TextUtils.isEmpty(str) ?
|
||||||
' ' : str.charAt(0);
|
' ' : str.charAt(0);
|
||||||
int avatarColorsIdx = TextUtils.isEmpty(str) ? 0 :
|
int avatarColorsIdx = TextUtils.isEmpty(str) ? 0 :
|
||||||
Math.max(Character.getNumericValue(str.charAt(0)) +
|
Math.max(Character.getNumericValue(str.charAt(0)) +
|
||||||
Character.getNumericValue(str.charAt(str.length() - 1)) +
|
Character.getNumericValue(str.charAt(str.length() - 1)) +
|
||||||
str.length(), 0) % characterAvatarColors.length();
|
str.length(), 0) % characterAvatarColors.length();
|
||||||
int color = characterAvatarColors.getColor(avatarColorsIdx, 0xff000000);
|
int color = characterAvatarColors.getColor(avatarColorsIdx, 0xff000000);
|
||||||
// avatarColorsIdx = randomGenerator.nextInt(characterAvatarColors.length());
|
// avatarColorsIdx = randomGenerator.nextInt(characterAvatarColors.length());
|
||||||
return new CharacterDrawable(charAvatar, color);
|
return new CharacterDrawable(charAvatar, color);
|
||||||
|
@ -210,12 +225,12 @@ public class UIUtils {
|
||||||
static int iconPauseResId = R.drawable.ic_pause_white_24dp,
|
static int iconPauseResId = R.drawable.ic_pause_white_24dp,
|
||||||
iconPlayResId = R.drawable.ic_play_arrow_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 context Activity
|
||||||
* @param view ImageView/ImageButton
|
* @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) {
|
if (!playPauseIconsLoaded) {
|
||||||
TypedArray styledAttributes = context.obtainStyledAttributes(new int[]{R.attr.iconPause, R.attr.iconPlay});
|
TypedArray styledAttributes = context.obtainStyledAttributes(new int[]{R.attr.iconPause, R.attr.iconPlay});
|
||||||
|
@ -225,7 +240,7 @@ public class UIUtils {
|
||||||
playPauseIconsLoaded = true;
|
playPauseIconsLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
view.setImageResource((speed == 1) ? iconPauseResId: iconPlayResId);
|
view.setImageResource(play ? iconPauseResId : iconPlayResId );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -28,10 +28,17 @@
|
||||||
|
|
||||||
<include layout="@layout/toolbar_default" />
|
<include layout="@layout/toolbar_default" />
|
||||||
|
|
||||||
<FrameLayout
|
<org.xbmc.kore.ui.widgets.NowPlayingPanel
|
||||||
android:id="@+id/fragment_container"
|
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_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>
|
</LinearLayout>
|
||||||
|
|
||||||
<fragment android:id="@+id/navigation_drawer"
|
<fragment android:id="@+id/navigation_drawer"
|
||||||
|
|
|
@ -169,12 +169,14 @@
|
||||||
style="@style/Widget.Button.Borderless"
|
style="@style/Widget.Button.Borderless"
|
||||||
android:src="?attr/iconVolumeMute"
|
android:src="?attr/iconVolumeMute"
|
||||||
android:contentDescription="@string/volume_mute"/>
|
android:contentDescription="@string/volume_mute"/>
|
||||||
|
|
||||||
<org.xbmc.kore.ui.widgets.VolumeLevelIndicator
|
<org.xbmc.kore.ui.widgets.VolumeLevelIndicator
|
||||||
android:id="@+id/volume_level_indicator"
|
android:id="@+id/volume_level_indicator"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_weight="2"
|
android:layout_weight="2"
|
||||||
android:orientation="vertical"/>
|
android:orientation="vertical" />
|
||||||
|
|
||||||
<org.xbmc.kore.ui.widgets.RepeatModeButton
|
<org.xbmc.kore.ui.widgets.RepeatModeButton
|
||||||
android:id="@+id/repeat"
|
android:id="@+id/repeat"
|
||||||
android:layout_width="0dp"
|
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_expanded_height">128dp</dimen>
|
||||||
<dimen name="notification_art_default_height">64dp</dimen>
|
<dimen name="notification_art_default_height">64dp</dimen>
|
||||||
<dimen name="notification_art_default_width">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_height">128dp</dimen>
|
||||||
<dimen name="notification_expanded_art_default_width">128dp</dimen>
|
<dimen name="notification_expanded_art_default_width">128dp</dimen>
|
||||||
<dimen name="notification_expanded_art_slim_width">88dp</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="keep_remote_above_lockscreen">Show over lockscreen</string>
|
||||||
<string name="pref_keep_screen_on">Stay awake</string>
|
<string name="pref_keep_screen_on">Stay awake</string>
|
||||||
<string name="show_notification">Show notification while playing</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="pause_during_calls">Pause during phone call</string>
|
||||||
<string name="use_hardware_volume_keys">Use device volume keys</string>
|
<string name="use_hardware_volume_keys">Use device volume keys</string>
|
||||||
<string name="vibrate_on_remote">Vibrate on touch</string>
|
<string name="vibrate_on_remote">Vibrate on touch</string>
|
||||||
|
|
|
@ -64,6 +64,12 @@
|
||||||
android:title="@string/show_notification"
|
android:title="@string/show_notification"
|
||||||
android:defaultValue="false"/>
|
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
|
<SwitchPreferenceCompat
|
||||||
android:key="pref_pause_during_calls"
|
android:key="pref_pause_during_calls"
|
||||||
android:title="@string/pause_during_calls"
|
android:title="@string/pause_during_calls"
|
||||||
|
|
Loading…
Reference in New Issue