Added now playing notifications
This commit is contained in:
parent
8d8dd8c6c2
commit
24ef1d5ff6
|
@ -42,6 +42,10 @@
|
|||
<!-- Services -->
|
||||
<service android:name="com.syncedsynapse.kore2.service.LibrarySyncService"
|
||||
android:exported="false"/>
|
||||
<service android:name="com.syncedsynapse.kore2.service.NotificationService"
|
||||
android:exported="false"/>
|
||||
<service android:name="com.syncedsynapse.kore2.service.IntentActionsService"
|
||||
android:exported="false"/>
|
||||
|
||||
</application>
|
||||
|
||||
|
|
|
@ -58,6 +58,8 @@ public class Settings {
|
|||
public static final String KEY_PREF_THEME = "pref_theme";
|
||||
public static final String KEY_PREF_SWITCH_TO_REMOTE_AFTER_MEDIA_START =
|
||||
"pref_switch_to_remote_after_media_start";
|
||||
public static final String KEY_PREF_SHOW_NOTIFICATION =
|
||||
"pref_show_notification";
|
||||
public static final String KEY_PREF_ABOUT = "pref_about";
|
||||
public static final String KEY_PREF_COFFEE = "pref_coffee";
|
||||
|
||||
|
@ -79,6 +81,7 @@ public class Settings {
|
|||
// Defaults for the preferences
|
||||
public static final String DEFAULT_PREF_THEME = "0";
|
||||
public static final boolean DEFAULT_PREF_SWITCH_TO_REMOTE_AFTER_MEDIA_START = true;
|
||||
public static final boolean DEFAULT_PREF_SHOW_NOTIFICATION = false;
|
||||
public static final boolean DEFAULT_PREF_MOVIES_FILTER_HIDE_WATCHED = false;
|
||||
public static final boolean DEFAULT_PREF_TVSHOWS_FILTER_HIDE_WATCHED = false;
|
||||
public static final boolean DEFAULT_PREF_TVSHOW_EPISODES_FILTER_HIDE_WATCHED = false;
|
||||
|
|
|
@ -107,6 +107,11 @@ public class HostConnectionObserver
|
|||
* Notifies that XBMC has requested input
|
||||
*/
|
||||
public void inputOnInputRequested(String title, String type, String value);
|
||||
|
||||
/**
|
||||
* Notifies the observer that it this is stopping
|
||||
*/
|
||||
public void observerOnStopObserving();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -191,7 +196,7 @@ public class HostConnectionObserver
|
|||
* Registers a new observer that will be notified about player events
|
||||
* @param observer Observer
|
||||
*/
|
||||
public synchronized void registerPlayerObserver(PlayerEventsObserver observer) {
|
||||
public void registerPlayerObserver(PlayerEventsObserver observer, boolean replyImmediately) {
|
||||
if (this.connection == null)
|
||||
return;
|
||||
|
||||
|
@ -199,6 +204,8 @@ public class HostConnectionObserver
|
|||
playerEventsObservers.add(observer);
|
||||
// observerHandlerMap.put(observer, new Handler());
|
||||
|
||||
if (replyImmediately) replyWithLastResult(observer);
|
||||
|
||||
if (playerEventsObservers.size() == 1) {
|
||||
// If this is the first observer, start checking through HTTP or register us
|
||||
// as a connection observer, which we will pass to the "real" observer
|
||||
|
@ -218,8 +225,7 @@ public class HostConnectionObserver
|
|||
* Unregisters a previously registered observer
|
||||
* @param observer Observer to unregister
|
||||
*/
|
||||
public synchronized void unregisterPlayerObserver(PlayerEventsObserver observer) {
|
||||
// Remove this observer and its associated handler
|
||||
public void unregisterPlayerObserver(PlayerEventsObserver observer) {
|
||||
playerEventsObservers.remove(observer);
|
||||
// observerHandlerMap.remove(observer);
|
||||
|
||||
|
@ -244,8 +250,10 @@ public class HostConnectionObserver
|
|||
/**
|
||||
* Unregisters all observers
|
||||
*/
|
||||
public void unregisterAllObservers() {
|
||||
// observerHandlerMap.clear();
|
||||
public void stopObserving() {
|
||||
for (final PlayerEventsObserver observer : playerEventsObservers)
|
||||
observer.observerOnStopObserving();
|
||||
|
||||
playerEventsObservers.clear();
|
||||
|
||||
if (connection.getProtocol() == HostConnection.PROTOCOL_TCP) {
|
||||
|
@ -293,25 +301,33 @@ public class HostConnectionObserver
|
|||
* The {@link HostConnection.SystemNotificationsObserver} interface methods
|
||||
*/
|
||||
public void onQuit(System.OnQuit notification) {
|
||||
for (final PlayerEventsObserver observer : playerEventsObservers) {
|
||||
// Copy list to prevent ConcurrentModificationExceptions
|
||||
List<PlayerEventsObserver> allObservers = new ArrayList<>(playerEventsObservers);
|
||||
for (final PlayerEventsObserver observer : allObservers) {
|
||||
observer.systemOnQuit();
|
||||
}
|
||||
}
|
||||
|
||||
public void onRestart(System.OnRestart notification) {
|
||||
for (final PlayerEventsObserver observer : playerEventsObservers) {
|
||||
// Copy list to prevent ConcurrentModificationExceptions
|
||||
List<PlayerEventsObserver> allObservers = new ArrayList<>(playerEventsObservers);
|
||||
for (final PlayerEventsObserver observer : allObservers) {
|
||||
observer.systemOnQuit();
|
||||
}
|
||||
}
|
||||
|
||||
public void onSleep(System.OnSleep notification) {
|
||||
for (final PlayerEventsObserver observer : playerEventsObservers) {
|
||||
// Copy list to prevent ConcurrentModificationExceptions
|
||||
List<PlayerEventsObserver> allObservers = new ArrayList<>(playerEventsObservers);
|
||||
for (final PlayerEventsObserver observer : allObservers) {
|
||||
observer.systemOnQuit();
|
||||
}
|
||||
}
|
||||
|
||||
public void onInputRequested(Input.OnInputRequested notification) {
|
||||
for (final PlayerEventsObserver observer : playerEventsObservers) {
|
||||
// Copy list to prevent ConcurrentModificationExceptions
|
||||
List<PlayerEventsObserver> allObservers = new ArrayList<>(playerEventsObservers);
|
||||
for (final PlayerEventsObserver observer : allObservers) {
|
||||
observer.inputOnInputRequested(notification.title, notification.type, notification.value);
|
||||
}
|
||||
}
|
||||
|
@ -467,7 +483,9 @@ public class HostConnectionObserver
|
|||
lastErrorCode = errorCode;
|
||||
lastErrorDescription = description;
|
||||
forceReply = false;
|
||||
for (final PlayerEventsObserver observer : observers) {
|
||||
// Copy list to prevent ConcurrentModificationExceptions
|
||||
List<PlayerEventsObserver> allObservers = new ArrayList<>(observers);
|
||||
for (final PlayerEventsObserver observer : allObservers) {
|
||||
notifyConnectionError(errorCode, description, observer);
|
||||
}
|
||||
}
|
||||
|
@ -503,7 +521,9 @@ public class HostConnectionObserver
|
|||
(lastCallResult != PlayerEventsObserver.PLAYER_IS_STOPPED)) {
|
||||
lastCallResult = PlayerEventsObserver.PLAYER_IS_STOPPED;
|
||||
forceReply = false;
|
||||
for (final PlayerEventsObserver observer : observers) {
|
||||
// Copy list to prevent ConcurrentModificationExceptions
|
||||
List<PlayerEventsObserver> allObservers = new ArrayList<>(observers);
|
||||
for (final PlayerEventsObserver observer : allObservers) {
|
||||
notifyNothingIsPlaying(observer);
|
||||
}
|
||||
}
|
||||
|
@ -543,7 +563,9 @@ public class HostConnectionObserver
|
|||
lastGetPropertiesResult = getPropertiesResult;
|
||||
lastGetItemResult = getItemResult;
|
||||
forceReply = false;
|
||||
for (final PlayerEventsObserver observer : observers) {
|
||||
// Copy list to prevent ConcurrentModificationExceptions
|
||||
List<PlayerEventsObserver> allObservers = new ArrayList<>(observers);
|
||||
for (final PlayerEventsObserver observer : allObservers) {
|
||||
notifySomethingIsPlaying(getActivePlayersResult, getPropertiesResult, getItemResult, observer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,8 +22,8 @@ import android.net.Uri;
|
|||
|
||||
import com.squareup.picasso.Picasso;
|
||||
import com.syncedsynapse.kore2.Settings;
|
||||
import com.syncedsynapse.kore2.provider.MediaContract;
|
||||
import com.syncedsynapse.kore2.jsonrpc.HostConnection;
|
||||
import com.syncedsynapse.kore2.provider.MediaContract;
|
||||
import com.syncedsynapse.kore2.utils.BasicAuthPicassoDownloader;
|
||||
import com.syncedsynapse.kore2.utils.LogUtils;
|
||||
|
||||
|
@ -375,7 +375,7 @@ public class HostManager {
|
|||
*/
|
||||
private void releaseCurrentHost() {
|
||||
if (currentHostConnectionObserver != null) {
|
||||
currentHostConnectionObserver.unregisterAllObservers();
|
||||
currentHostConnectionObserver.stopObserving();
|
||||
currentHostConnectionObserver = null;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright 2015 Synced Synapse. 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 com.syncedsynapse.kore2.service;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
|
||||
import com.syncedsynapse.kore2.host.HostManager;
|
||||
import com.syncedsynapse.kore2.jsonrpc.HostConnection;
|
||||
import com.syncedsynapse.kore2.jsonrpc.method.Player;
|
||||
import com.syncedsynapse.kore2.jsonrpc.type.GlobalType;
|
||||
import com.syncedsynapse.kore2.utils.LogUtils;
|
||||
|
||||
/**
|
||||
* Service that implements some player actions
|
||||
* Used to support the notifications actions
|
||||
*/
|
||||
public class IntentActionsService extends Service {
|
||||
public static final String TAG = LogUtils.makeLogTag(IntentActionsService.class);
|
||||
|
||||
public static final String EXTRA_PLAYER_ID = "extra_player_id";
|
||||
|
||||
public static final String ACTION_PLAY_PAUSE = "play_pause",
|
||||
ACTION_REWIND = "rewind",
|
||||
ACTION_FAST_FORWARD = "fast_forward",
|
||||
ACTION_PREVIOUS = "previous",
|
||||
ACTION_NEXT = "next";
|
||||
|
||||
@Override
|
||||
public void onCreate() { }
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
// We won't create a new thread because the request to the host are
|
||||
// already done in a separate thread. Just fire the request and forget
|
||||
HostConnection hostConnection = HostManager.getInstance(this).getConnection();
|
||||
|
||||
String action = intent.getAction();
|
||||
int playerId = intent.getIntExtra(EXTRA_PLAYER_ID, -1);
|
||||
|
||||
if ((hostConnection != null) && (playerId != -1)) {
|
||||
switch (action) {
|
||||
case ACTION_PLAY_PAUSE:
|
||||
hostConnection.execute(
|
||||
new Player.PlayPause(playerId),
|
||||
null, null);
|
||||
break;
|
||||
case ACTION_REWIND:
|
||||
hostConnection.execute(
|
||||
new Player.SetSpeed(playerId, GlobalType.IncrementDecrement.DECREMENT),
|
||||
null, null);
|
||||
break;
|
||||
case ACTION_FAST_FORWARD:
|
||||
hostConnection.execute(
|
||||
new Player.SetSpeed(playerId, GlobalType.IncrementDecrement.INCREMENT),
|
||||
null, null);
|
||||
break;
|
||||
case ACTION_PREVIOUS:
|
||||
hostConnection.execute(
|
||||
new Player.GoTo(playerId, Player.GoTo.PREVIOUS),
|
||||
null, null);
|
||||
break;
|
||||
case ACTION_NEXT:
|
||||
hostConnection.execute(
|
||||
new Player.GoTo(playerId, Player.GoTo.NEXT),
|
||||
null, null);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) { return null; }
|
||||
}
|
|
@ -0,0 +1,313 @@
|
|||
/*
|
||||
* Copyright 2015 Synced Synapse. 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 com.syncedsynapse.kore2.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.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.app.TaskStackBuilder;
|
||||
import android.view.View;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import com.syncedsynapse.kore2.R;
|
||||
import com.syncedsynapse.kore2.host.HostConnectionObserver;
|
||||
import com.syncedsynapse.kore2.host.HostManager;
|
||||
import com.syncedsynapse.kore2.jsonrpc.type.ListType;
|
||||
import com.syncedsynapse.kore2.jsonrpc.type.PlayerType;
|
||||
import com.syncedsynapse.kore2.ui.RemoteActivity;
|
||||
import com.syncedsynapse.kore2.utils.LogUtils;
|
||||
import com.syncedsynapse.kore2.utils.Utils;
|
||||
|
||||
/**
|
||||
* This service maintains a notification in the notification area while
|
||||
* something is playing, and keeps running while it is playing.
|
||||
* This service stops itself as soon as the playing stops or there's no
|
||||
* connection. Thus, this should only be started if something is already
|
||||
* playing, otherwise it will shutdown automatically.
|
||||
* It doesn't try to mirror Kodi's state at all times, because that would
|
||||
* imply running at all times which can be resource consuming.
|
||||
*
|
||||
* A {@link HostConnectionObserver} singleton is used to keep track of Kodi's
|
||||
* state. This singleton should be the same as used in the app's activities
|
||||
*/
|
||||
public class NotificationService extends Service
|
||||
implements HostConnectionObserver.PlayerEventsObserver {
|
||||
public static final String TAG = LogUtils.makeLogTag(NotificationService.class);
|
||||
|
||||
private static final int NOTIFICATION_ID = 1;
|
||||
|
||||
private HostConnectionObserver mHostConnectionObserver = null;
|
||||
|
||||
private PendingIntent mRemoteStartPendingIntent;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
// We do not create any thread because all the works is supposed to
|
||||
// be done on the main thread, so that the connection observer
|
||||
// can be shared with the app, and notify it on the UI thread
|
||||
LogUtils.LOGD(TAG, "onCreate");
|
||||
|
||||
// Create the intent to start the remote when the user taps the notification
|
||||
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
|
||||
stackBuilder.addParentStack(RemoteActivity.class);
|
||||
stackBuilder.addNextIntent(new Intent(this, RemoteActivity.class));
|
||||
mRemoteStartPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
LogUtils.LOGD(TAG, "onStartCommand");
|
||||
// Get the connection observer here, not on create to check if
|
||||
// there has been a change in hosts, and if so unregister the previous one
|
||||
HostConnectionObserver connectionObserver = HostManager.getInstance(this).getHostConnectionObserver();
|
||||
|
||||
// If we are already initialized and the same host, exit
|
||||
if (mHostConnectionObserver == connectionObserver) {
|
||||
LogUtils.LOGD(TAG, "Already initialized");
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
// If there's a change in hosts, unregister from the previous one
|
||||
if (mHostConnectionObserver != null) {
|
||||
mHostConnectionObserver.unregisterPlayerObserver(this);
|
||||
}
|
||||
|
||||
// Register us on the connection observer
|
||||
mHostConnectionObserver = connectionObserver;
|
||||
mHostConnectionObserver.registerPlayerObserver(this, true);
|
||||
|
||||
// If we get killed, after returning from here, don't restart
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
// We don't provide binding, so return null
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* HostConnectionObserver.PlayerEventsObserver interface callbacks
|
||||
*/
|
||||
|
||||
public void playerOnPlay(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||
PlayerType.PropertyValue getPropertiesResult,
|
||||
ListType.ItemsAll getItemResult) {
|
||||
buildNotification(getActivePlayerResult, getPropertiesResult, getItemResult);
|
||||
}
|
||||
|
||||
public void playerOnPause(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||
PlayerType.PropertyValue getPropertiesResult,
|
||||
ListType.ItemsAll getItemResult) {
|
||||
buildNotification(getActivePlayerResult, getPropertiesResult, getItemResult);
|
||||
}
|
||||
|
||||
public void playerOnStop() {
|
||||
removeNotification();
|
||||
// Stop service
|
||||
LogUtils.LOGD(TAG, "Shutting down notification service - Player stopped");
|
||||
mHostConnectionObserver.unregisterPlayerObserver(this);
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
public void playerNoResultsYet() {
|
||||
removeNotification();
|
||||
}
|
||||
|
||||
public void playerOnConnectionError(int errorCode, String description) {
|
||||
removeNotification();
|
||||
// Stop service
|
||||
LogUtils.LOGD(TAG, "Shutting down notification service - Connection error");
|
||||
mHostConnectionObserver.unregisterPlayerObserver(this);
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
public void systemOnQuit() {
|
||||
removeNotification();
|
||||
// Stop service
|
||||
LogUtils.LOGD(TAG, "Shutting down notification service - System quit");
|
||||
mHostConnectionObserver.unregisterPlayerObserver(this);
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
// Ignore this
|
||||
public void inputOnInputRequested(String title, String type, String value) { }
|
||||
|
||||
public void observerOnStopObserving() {
|
||||
// Called when the user changes host
|
||||
removeNotification();
|
||||
LogUtils.LOGD(TAG, "Shutting down notification service - System quit");
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
|
||||
private void buildNotification(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||
PlayerType.PropertyValue getPropertiesResult,
|
||||
ListType.ItemsAll getItemResult) {
|
||||
String title, underTitle, poster;
|
||||
int smallIcon, playPauseIcon, rewindIcon, ffIcon;
|
||||
|
||||
boolean isVideo = ((getItemResult.type.equals(ListType.ItemsAll.TYPE_MOVIE)) ||
|
||||
(getItemResult.type.equals(ListType.ItemsAll.TYPE_EPISODE)));
|
||||
|
||||
switch (getItemResult.type) {
|
||||
case ListType.ItemsAll.TYPE_MOVIE:
|
||||
title = getItemResult.title;
|
||||
underTitle = getItemResult.tagline;
|
||||
poster = getItemResult.thumbnail;
|
||||
smallIcon = R.drawable.ic_movie_white_24dp;
|
||||
break;
|
||||
case ListType.ItemsAll.TYPE_EPISODE:
|
||||
title = getItemResult.title;
|
||||
String seasonEpisode = String.format(getString(R.string.season_episode_abbrev),
|
||||
getItemResult.season, getItemResult.episode);
|
||||
underTitle = String.format("%s | %s", getItemResult.showtitle, seasonEpisode);
|
||||
poster = getItemResult.art.poster;
|
||||
smallIcon = R.drawable.ic_tv_white_24dp;
|
||||
break;
|
||||
case ListType.ItemsAll.TYPE_SONG:
|
||||
title = getItemResult.title;
|
||||
underTitle = getItemResult.displayartist + " - " + getItemResult.album;
|
||||
poster = getItemResult.thumbnail;
|
||||
smallIcon = R.drawable.ic_headset_white_24dp;
|
||||
break;
|
||||
case ListType.ItemsAll.TYPE_MUSIC_VIDEO:
|
||||
title = getItemResult.title;
|
||||
underTitle = Utils.listStringConcat(getItemResult.artist, ", ") + " - " + getItemResult.album;
|
||||
poster = getItemResult.thumbnail;
|
||||
smallIcon = R.drawable.ic_headset_white_24dp;
|
||||
break;
|
||||
default:
|
||||
// We don't know what this is, forget it
|
||||
return;
|
||||
}
|
||||
|
||||
switch (getPropertiesResult.speed) {
|
||||
case 1:
|
||||
playPauseIcon = R.drawable.ic_pause_white_24dp;
|
||||
break;
|
||||
default:
|
||||
playPauseIcon = R.drawable.ic_play_arrow_white_24dp;
|
||||
break;
|
||||
}
|
||||
|
||||
// Create the actions, depending on the type of media
|
||||
PendingIntent rewindPendingItent, ffPendingItent, playPausePendingIntent;
|
||||
playPausePendingIntent = buildActionPendingIntent(getActivePlayerResult.playerid, IntentActionsService.ACTION_PLAY_PAUSE);
|
||||
if (getItemResult.type.equals(ListType.ItemsAll.TYPE_SONG)) {
|
||||
rewindPendingItent = buildActionPendingIntent(getActivePlayerResult.playerid, IntentActionsService.ACTION_PREVIOUS);
|
||||
rewindIcon = R.drawable.ic_skip_previous_white_24dp;
|
||||
ffPendingItent = buildActionPendingIntent(getActivePlayerResult.playerid, IntentActionsService.ACTION_NEXT);
|
||||
ffIcon = R.drawable.ic_skip_next_white_24dp;
|
||||
} else {
|
||||
rewindPendingItent = buildActionPendingIntent(getActivePlayerResult.playerid, IntentActionsService.ACTION_REWIND);
|
||||
rewindIcon = R.drawable.ic_fast_rewind_white_24dp;
|
||||
ffPendingItent = buildActionPendingIntent(getActivePlayerResult.playerid, IntentActionsService.ACTION_FAST_FORWARD);
|
||||
ffIcon = R.drawable.ic_fast_forward_white_24dp;
|
||||
}
|
||||
|
||||
// Setup the collpased and expanded notifications
|
||||
RemoteViews collapsedRV = new RemoteViews(this.getPackageName(), R.layout.notification_colapsed);
|
||||
collapsedRV.setImageViewResource(R.id.rewind, rewindIcon);
|
||||
collapsedRV.setOnClickPendingIntent(R.id.rewind, rewindPendingItent);
|
||||
collapsedRV.setImageViewResource(R.id.play, playPauseIcon);
|
||||
collapsedRV.setOnClickPendingIntent(R.id.play, playPausePendingIntent);
|
||||
collapsedRV.setImageViewResource(R.id.fast_forward, ffIcon);
|
||||
collapsedRV.setOnClickPendingIntent(R.id.fast_forward, ffPendingItent);
|
||||
collapsedRV.setTextViewText(R.id.title, title);
|
||||
collapsedRV.setTextViewText(R.id.text2, underTitle);
|
||||
|
||||
RemoteViews expandedRV = new RemoteViews(this.getPackageName(), R.layout.notification_expanded);
|
||||
expandedRV.setImageViewResource(R.id.rewind, rewindIcon);
|
||||
expandedRV.setOnClickPendingIntent(R.id.rewind, rewindPendingItent);
|
||||
expandedRV.setImageViewResource(R.id.play, playPauseIcon);
|
||||
expandedRV.setOnClickPendingIntent(R.id.play, playPausePendingIntent);
|
||||
expandedRV.setImageViewResource(R.id.fast_forward, ffIcon);
|
||||
expandedRV.setOnClickPendingIntent(R.id.fast_forward, ffPendingItent);
|
||||
expandedRV.setTextViewText(R.id.title, title);
|
||||
expandedRV.setTextViewText(R.id.text2, underTitle);
|
||||
int expandedIconResId;
|
||||
if (isVideo) {
|
||||
expandedIconResId = R.id.icon_slim;
|
||||
expandedRV.setViewVisibility(R.id.icon_slim, View.VISIBLE);
|
||||
expandedRV.setViewVisibility(R.id.icon_square, View.GONE);
|
||||
} else {
|
||||
expandedIconResId = R.id.icon_square;
|
||||
expandedRV.setViewVisibility(R.id.icon_slim, View.GONE);
|
||||
expandedRV.setViewVisibility(R.id.icon_square, View.VISIBLE);
|
||||
}
|
||||
|
||||
// Build the notification
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
|
||||
Notification notification = builder
|
||||
.setSmallIcon(smallIcon)
|
||||
.setShowWhen(false)
|
||||
.setOngoing(true)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setCategory(NotificationCompat.CATEGORY_TRANSPORT)
|
||||
.setContentIntent(mRemoteStartPendingIntent)
|
||||
.setContent(collapsedRV)
|
||||
.build();
|
||||
|
||||
// Load images. Use the same dimensions as the remote to hit the cache both times
|
||||
Resources resources = this.getResources();
|
||||
int posterWidth = resources.getDimensionPixelOffset(R.dimen.now_playing_poster_width);
|
||||
int posterHeight = isVideo?
|
||||
resources.getDimensionPixelOffset(R.dimen.now_playing_poster_height):
|
||||
posterWidth;
|
||||
|
||||
HostManager hostManager = HostManager.getInstance(this);
|
||||
hostManager.getPicasso()
|
||||
.load(hostManager.getHostInfo().getImageUrl(poster))
|
||||
.resize(posterWidth, posterHeight)
|
||||
.into(collapsedRV, R.id.icon, NOTIFICATION_ID, notification);
|
||||
|
||||
if (Utils.isJellybeanOrLater()) {
|
||||
notification.bigContentView = expandedRV;
|
||||
hostManager.getPicasso()
|
||||
.load(hostManager.getHostInfo().getImageUrl(poster))
|
||||
.resize(posterWidth, posterHeight)
|
||||
.into(expandedRV, expandedIconResId, NOTIFICATION_ID, notification);
|
||||
}
|
||||
|
||||
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.notify(NOTIFICATION_ID, notification);
|
||||
|
||||
}
|
||||
|
||||
private PendingIntent buildActionPendingIntent(int playerId, String action) {
|
||||
LogUtils.LOGD(TAG, "Build action: " + action);
|
||||
Intent intent = new Intent(this, IntentActionsService.class)
|
||||
.setAction(action)
|
||||
.putExtra(IntentActionsService.EXTRA_PLAYER_ID, playerId);
|
||||
|
||||
return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
|
||||
private void removeNotification() {
|
||||
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.cancel(NOTIFICATION_ID);
|
||||
}
|
||||
}
|
|
@ -214,15 +214,12 @@ public class NowPlayingFragment extends Fragment
|
|||
public void onActivityCreated (Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
setHasOptionsMenu(false);
|
||||
// Get last result from host observer, so that we update the UI accordingly
|
||||
// One of the PlayerEventsObserver callbacks will be called if there's a result available
|
||||
hostConnectionObserver.replyWithLastResult(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
hostConnectionObserver.registerPlayerObserver(this);
|
||||
hostConnectionObserver.registerPlayerObserver(this, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -591,6 +588,7 @@ public class NowPlayingFragment extends Fragment
|
|||
|
||||
// Ignore this
|
||||
public void inputOnInputRequested(String title, String type, String value) {}
|
||||
public void observerOnStopObserving() {}
|
||||
|
||||
/**
|
||||
* Sets whats playing information
|
||||
|
|
|
@ -144,15 +144,12 @@ public class PlaylistFragment extends Fragment
|
|||
super.onActivityCreated(savedInstanceState);
|
||||
// We have options
|
||||
setHasOptionsMenu(true);
|
||||
// Get last result from host observer, so that we update the UI accordingly
|
||||
// One of the PlayerEventsObserver callbacks will be called if there's a result available
|
||||
hostConnectionObserver.replyWithLastResult(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
hostConnectionObserver.registerPlayerObserver(this);
|
||||
hostConnectionObserver.registerPlayerObserver(this, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -369,6 +366,7 @@ public class PlaylistFragment extends Fragment
|
|||
|
||||
// Ignore this
|
||||
public void inputOnInputRequested(String title, String type, String value) {}
|
||||
public void observerOnStopObserving() {}
|
||||
|
||||
/**
|
||||
* Starts the call chain to display the playlist
|
||||
|
|
|
@ -32,6 +32,7 @@ import android.widget.ImageView;
|
|||
import android.widget.Toast;
|
||||
|
||||
import com.syncedsynapse.kore2.R;
|
||||
import com.syncedsynapse.kore2.Settings;
|
||||
import com.syncedsynapse.kore2.host.HostConnectionObserver;
|
||||
import com.syncedsynapse.kore2.host.HostManager;
|
||||
import com.syncedsynapse.kore2.jsonrpc.ApiCallback;
|
||||
|
@ -43,6 +44,7 @@ import com.syncedsynapse.kore2.jsonrpc.method.VideoLibrary;
|
|||
import com.syncedsynapse.kore2.jsonrpc.type.GlobalType;
|
||||
import com.syncedsynapse.kore2.jsonrpc.type.ListType;
|
||||
import com.syncedsynapse.kore2.jsonrpc.type.PlayerType;
|
||||
import com.syncedsynapse.kore2.service.NotificationService;
|
||||
import com.syncedsynapse.kore2.ui.hosts.AddHostActivity;
|
||||
import com.syncedsynapse.kore2.ui.views.CirclePageIndicator;
|
||||
import com.syncedsynapse.kore2.utils.LogUtils;
|
||||
|
@ -129,9 +131,9 @@ public class RemoteActivity extends BaseActivity
|
|||
public void onResume() {
|
||||
super.onResume();
|
||||
hostConnectionObserver = hostManager.getHostConnectionObserver();
|
||||
hostConnectionObserver.registerPlayerObserver(this);
|
||||
// Get last result
|
||||
hostConnectionObserver.replyWithLastResult(this);
|
||||
hostConnectionObserver.registerPlayerObserver(this, true);
|
||||
// Force a refresh, mainly to update the time elapsed on the fragments
|
||||
hostConnectionObserver.forceRefreshResults();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -298,7 +300,7 @@ public class RemoteActivity extends BaseActivity
|
|||
|
||||
/**
|
||||
* Sets or clear the image background
|
||||
* @param url
|
||||
* @param url Image url
|
||||
*/
|
||||
private void setImageViewBackground(String url) {
|
||||
if (url != null) {
|
||||
|
@ -356,6 +358,16 @@ public class RemoteActivity extends BaseActivity
|
|||
setImageViewBackground(imageUrl);
|
||||
}
|
||||
lastImageUrl = imageUrl;
|
||||
|
||||
// Check whether we should show a notification
|
||||
boolean showNotification = PreferenceManager
|
||||
.getDefaultSharedPreferences(this)
|
||||
.getBoolean(Settings.KEY_PREF_SHOW_NOTIFICATION, Settings.DEFAULT_PREF_SHOW_NOTIFICATION);
|
||||
if (showNotification) {
|
||||
// Let's start the notification service
|
||||
LogUtils.LOGD(TAG, "Starting notification service");
|
||||
startService(new Intent(this, NotificationService.class));
|
||||
}
|
||||
}
|
||||
|
||||
public void playerOnPause(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||
|
@ -390,6 +402,8 @@ public class RemoteActivity extends BaseActivity
|
|||
dialog.show(getSupportFragmentManager(), null);
|
||||
}
|
||||
|
||||
public void observerOnStopObserving() {}
|
||||
|
||||
/**
|
||||
* Now playing fragment listener
|
||||
*/
|
||||
|
|
|
@ -168,15 +168,12 @@ public class RemoteFragment extends Fragment
|
|||
public void onActivityCreated (Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
setHasOptionsMenu(false);
|
||||
// Get last result from host observer, so that we update the UI accordingly
|
||||
// One of the PlayerEventsObserver callbacks will be called if there's a result available
|
||||
hostConnectionObserver.replyWithLastResult(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
hostConnectionObserver.registerPlayerObserver(this);
|
||||
hostConnectionObserver.registerPlayerObserver(this, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -339,6 +336,7 @@ public class RemoteFragment extends Fragment
|
|||
|
||||
// Ignore this
|
||||
public void inputOnInputRequested(String title, String type, String value) {}
|
||||
public void observerOnStopObserving() {}
|
||||
|
||||
/**
|
||||
* Sets whats playing information
|
||||
|
|
|
@ -16,12 +16,10 @@
|
|||
|
||||
package com.syncedsynapse.kore2.utils;
|
||||
|
||||
import com.syncedsynapse.kore2.BuildConfig;
|
||||
import com.syncedsynapse.kore2.host.HostConnectionObserver;
|
||||
import com.syncedsynapse.kore2.jsonrpc.HostConnection;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.syncedsynapse.kore2.BuildConfig;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -33,8 +31,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) {
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2015 Synced Synapse. 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.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/status_bar_latest_event_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:background="@color/background_floating_material_dark">
|
||||
|
||||
<ImageView android:id="@+id/icon"
|
||||
android:layout_width="@dimen/notification_art_default_width"
|
||||
android:layout_height="@dimen/notification_art_default_height"
|
||||
android:layout_weight="0"
|
||||
android:padding="12dp"
|
||||
android:scaleType="centerInside"
|
||||
android:contentDescription="@string/poster"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="fill_vertical"
|
||||
android:minHeight="@dimen/notification_height"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="@dimen/small_padding"
|
||||
android:textAppearance="@style/TextAppearance.Notification.Title"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"/>
|
||||
<TextView
|
||||
android:id="@+id/text2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/TextAppearance.Notification.Details"
|
||||
android:singleLine="true"
|
||||
android:fadingEdge="horizontal"
|
||||
android:ellipsize="marquee"/>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/media_actions"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginRight="6dp"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:orientation="horizontal"
|
||||
android:layoutDirection="ltr">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/rewind"
|
||||
style="@style/Widget.Button.Borderless"
|
||||
android:layout_width="@dimen/default_icon_size"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="2dp"
|
||||
android:layout_marginRight="2dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:contentDescription="@string/rewind"/>
|
||||
<ImageButton
|
||||
android:id="@+id/play"
|
||||
style="@style/Widget.Button.Borderless"
|
||||
android:layout_width="@dimen/default_icon_size"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="2dp"
|
||||
android:layout_marginRight="2dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:contentDescription="@string/play"/>
|
||||
<ImageButton
|
||||
android:id="@+id/fast_forward"
|
||||
style="@style/Widget.Button.Borderless"
|
||||
android:layout_width="@dimen/default_icon_size"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="2dp"
|
||||
android:layout_marginRight="2dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:contentDescription="@string/fast_forward"/>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
|
@ -0,0 +1,224 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2015 Synced Synapse. 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.
|
||||
-->
|
||||
<!--<LinearLayout-->
|
||||
<!--xmlns:android="http://schemas.android.com/apk/res/android"-->
|
||||
<!--android:id="@+id/status_bar_latest_event_content"-->
|
||||
<!--android:layout_width="match_parent"-->
|
||||
<!--android:layout_height="wrap_content"-->
|
||||
<!--android:orientation="horizontal"-->
|
||||
<!--android:background="@color/background_floating_material_dark">-->
|
||||
|
||||
<!--<ImageView android:id="@+id/icon_square"-->
|
||||
<!--android:layout_width="@dimen/notification_expanded_art_default_height"-->
|
||||
<!--android:layout_height="@dimen/notification_expanded_art_default_height"-->
|
||||
<!--android:scaleType="centerCrop"-->
|
||||
<!--android:contentDescription="@string/poster"-->
|
||||
<!--android:visibility="gone"/>-->
|
||||
<!--<ImageView android:id="@+id/icon_slim"-->
|
||||
<!--android:layout_width="@dimen/notification_expanded_art_slim_width"-->
|
||||
<!--android:layout_height="@dimen/notification_expanded_art_default_height"-->
|
||||
<!--android:scaleType="centerCrop"-->
|
||||
<!--android:contentDescription="@string/poster"-->
|
||||
<!--android:visibility="gone"/>-->
|
||||
|
||||
<!--<LinearLayout-->
|
||||
<!--android:layout_width="0dp"-->
|
||||
<!--android:layout_height="wrap_content"-->
|
||||
<!--android:layout_weight="1"-->
|
||||
<!--android:layout_gravity="fill_vertical"-->
|
||||
<!--android:minHeight="@dimen/notification_expanded_height"-->
|
||||
<!--android:orientation="vertical">-->
|
||||
|
||||
<!--<TextView-->
|
||||
<!--android:id="@+id/title"-->
|
||||
<!--android:layout_width="wrap_content"-->
|
||||
<!--android:layout_height="wrap_content"-->
|
||||
<!--android:paddingTop="@dimen/small_padding"-->
|
||||
<!--android:layout_marginLeft="12dp"-->
|
||||
<!--android:layout_marginStart="12dp"-->
|
||||
<!--android:layout_marginRight="12dp"-->
|
||||
<!--android:layout_marginEnd="12dp"-->
|
||||
<!--android:textAppearance="@style/TextAppearance.Notification.Title"-->
|
||||
<!--android:singleLine="true"-->
|
||||
<!--android:ellipsize="marquee"-->
|
||||
<!--android:fadingEdge="horizontal"/>-->
|
||||
<!--<TextView-->
|
||||
<!--android:id="@+id/text2"-->
|
||||
<!--android:layout_width="wrap_content"-->
|
||||
<!--android:layout_height="0dp"-->
|
||||
<!--android:layout_weight="1"-->
|
||||
<!--android:layout_marginLeft="12dp"-->
|
||||
<!--android:layout_marginStart="12dp"-->
|
||||
<!--android:layout_marginRight="12dp"-->
|
||||
<!--android:layout_marginEnd="12dp"-->
|
||||
<!--android:textAppearance="@style/TextAppearance.Notification.Details"-->
|
||||
<!--android:maxLines="2"-->
|
||||
<!--android:fadingEdge="horizontal"-->
|
||||
<!--android:ellipsize="marquee"/>-->
|
||||
|
||||
<!--<ImageView-->
|
||||
<!--android:layout_width="match_parent"-->
|
||||
<!--android:layout_height="1dp"-->
|
||||
<!--android:id="@+id/action_divider"-->
|
||||
<!--android:background="#29ffffff"/>-->
|
||||
|
||||
<!--<LinearLayout-->
|
||||
<!--android:id="@+id/media_actions"-->
|
||||
<!--android:layout_width="match_parent"-->
|
||||
<!--android:layout_height="@dimen/default_icon_size"-->
|
||||
<!--android:layout_marginStart="12dp"-->
|
||||
<!--android:layout_marginEnd="12dp"-->
|
||||
<!--android:orientation="horizontal"-->
|
||||
<!--android:layoutDirection="ltr">-->
|
||||
|
||||
<!--<ImageButton-->
|
||||
<!--android:id="@+id/rewind"-->
|
||||
<!--style="@style/Widget.Button.Borderless"-->
|
||||
<!--android:layout_width="@dimen/default_icon_size"-->
|
||||
<!--android:layout_height="match_parent"-->
|
||||
<!--android:layout_marginLeft="2dp"-->
|
||||
<!--android:layout_marginRight="2dp"-->
|
||||
<!--android:layout_weight="1"-->
|
||||
<!--android:gravity="center"-->
|
||||
<!--android:contentDescription="@string/rewind"/>-->
|
||||
<!--<ImageButton-->
|
||||
<!--android:id="@+id/play"-->
|
||||
<!--style="@style/Widget.Button.Borderless"-->
|
||||
<!--android:layout_width="@dimen/default_icon_size"-->
|
||||
<!--android:layout_height="match_parent"-->
|
||||
<!--android:layout_marginLeft="2dp"-->
|
||||
<!--android:layout_marginRight="2dp"-->
|
||||
<!--android:layout_weight="1"-->
|
||||
<!--android:gravity="center"-->
|
||||
<!--android:contentDescription="@string/play"/>-->
|
||||
<!--<ImageButton-->
|
||||
<!--android:id="@+id/fast_forward"-->
|
||||
<!--style="@style/Widget.Button.Borderless"-->
|
||||
<!--android:layout_width="@dimen/default_icon_size"-->
|
||||
<!--android:layout_height="match_parent"-->
|
||||
<!--android:layout_marginLeft="2dp"-->
|
||||
<!--android:layout_marginRight="2dp"-->
|
||||
<!--android:layout_weight="1"-->
|
||||
<!--android:gravity="center"-->
|
||||
<!--android:contentDescription="@string/fast_forward"/>-->
|
||||
<!--</LinearLayout>-->
|
||||
<!--</LinearLayout>-->
|
||||
<!--</LinearLayout>-->
|
||||
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/status_bar_latest_event_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="128dp"
|
||||
android:background="@color/background_floating_material_dark">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
<ImageView android:id="@+id/icon_square"
|
||||
android:layout_width="@dimen/notification_expanded_art_default_height"
|
||||
android:layout_height="@dimen/notification_expanded_art_default_height"
|
||||
android:scaleType="centerCrop"
|
||||
android:contentDescription="@string/poster"
|
||||
android:visibility="gone"/>
|
||||
<ImageView android:id="@+id/icon_slim"
|
||||
android:layout_width="@dimen/notification_expanded_art_slim_width"
|
||||
android:layout_height="@dimen/notification_expanded_art_default_height"
|
||||
android:scaleType="centerCrop"
|
||||
android:contentDescription="@string/poster"
|
||||
android:visibility="gone"/>
|
||||
</FrameLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_toRightOf="@id/icon"
|
||||
android:layout_toEndOf="@id/icon"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="@dimen/small_padding"
|
||||
android:textAppearance="@style/TextAppearance.Notification.Title"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"/>
|
||||
<TextView
|
||||
android:id="@+id/text2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/TextAppearance.Notification.Details"
|
||||
android:maxLines="2"
|
||||
android:fadingEdge="horizontal"
|
||||
android:ellipsize="marquee"/>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/media_actions"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/default_icon_size"
|
||||
android:layout_toRightOf="@id/icon"
|
||||
android:layout_toEndOf="@id/icon"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:orientation="horizontal"
|
||||
android:layoutDirection="ltr">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/rewind"
|
||||
style="@style/Widget.Button.Borderless"
|
||||
android:layout_width="@dimen/default_icon_size"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="2dp"
|
||||
android:layout_marginRight="2dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"/>
|
||||
<ImageButton
|
||||
android:id="@+id/play"
|
||||
style="@style/Widget.Button.Borderless"
|
||||
android:layout_width="@dimen/default_icon_size"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="2dp"
|
||||
android:layout_marginRight="2dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"/>
|
||||
<ImageButton
|
||||
android:id="@+id/fast_forward"
|
||||
style="@style/Widget.Button.Borderless"
|
||||
android:layout_width="@dimen/default_icon_size"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="2dp"
|
||||
android:layout_marginRight="2dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"/>
|
||||
</LinearLayout>
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_toRightOf="@id/icon"
|
||||
android:layout_toEndOf="@id/icon"
|
||||
android:layout_above="@id/media_actions"
|
||||
android:id="@+id/action_divider"
|
||||
android:background="#29ffffff"/>
|
||||
</RelativeLayout>
|
|
@ -114,4 +114,16 @@
|
|||
|
||||
<dimen name="addondetail_poster_width">112dp</dimen>
|
||||
<dimen name="addondetail_poster_heigth">112dp</dimen>
|
||||
|
||||
<!-- Notification -->
|
||||
<dimen name="notification_height">64dp</dimen>
|
||||
<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">42dp</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">84dp</dimen>
|
||||
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -270,7 +270,7 @@
|
|||
<string name="theme_solarized_dark">Solarized Dark</string>
|
||||
|
||||
<string name="switch_to_remote">Switch to remote after media start</string>
|
||||
|
||||
<string name="show_notification">Show notification while playing</string>
|
||||
|
||||
<string name="about">About</string>
|
||||
<string name="about_desc"><![CDATA[
|
||||
|
|
|
@ -66,7 +66,7 @@
|
|||
<item name="android:paddingBottom">@dimen/large_padding</item>
|
||||
<item name="android:paddingLeft">@dimen/large_padding</item>
|
||||
<item name="android:paddingRight">@dimen/large_padding</item>
|
||||
<item name="android:maxLines">1</item>
|
||||
<item name="android:maxLines">2</item>
|
||||
<item name="android:ellipsize">end</item>
|
||||
</style>
|
||||
|
||||
|
@ -282,5 +282,20 @@
|
|||
<!--<item name="android:maxLines">2</item>-->
|
||||
<item name="android:ellipsize">end</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Notification">
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Notification.Title">
|
||||
<item name="android:textAppearance">@style/TextAppearance.AppCompat.Title</item>
|
||||
<item name="android:textColor">@color/primary_text_default_material_dark</item>
|
||||
<item name="android:textSize">@dimen/text_size_large</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Notification.Details">
|
||||
<item name="android:textAppearance">@style/TextAppearance.AppCompat.Body1</item>
|
||||
<item name="android:textColor">@color/secondary_text_default_material_dark</item>
|
||||
<item name="android:textSize">@dimen/text_size_medium</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
||||
|
|
|
@ -34,6 +34,11 @@
|
|||
android:title="@string/switch_to_remote"
|
||||
android:defaultValue="true"/>
|
||||
|
||||
<CheckBoxPreference
|
||||
android:key="pref_show_notification"
|
||||
android:title="@string/show_notification"
|
||||
android:defaultValue="true"/>
|
||||
|
||||
<Preference
|
||||
android:key="pref_about"
|
||||
android:title="@string/about"/>
|
||||
|
|
Loading…
Reference in New Issue