Merge branch 'tomerf-call_notifications'
This commit is contained in:
commit
67d8100245
|
@ -9,6 +9,7 @@
|
||||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||||
|
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
@ -67,7 +68,7 @@
|
||||||
<!-- Services -->
|
<!-- Services -->
|
||||||
<service android:name=".service.library.LibrarySyncService"
|
<service android:name=".service.library.LibrarySyncService"
|
||||||
android:exported="false"/>
|
android:exported="false"/>
|
||||||
<service android:name="org.xbmc.kore.service.NotificationService"
|
<service android:name="org.xbmc.kore.service.ConnectionObserversManagerService"
|
||||||
android:exported="false"/>
|
android:exported="false"/>
|
||||||
<service android:name="org.xbmc.kore.service.IntentActionsService"
|
<service android:name="org.xbmc.kore.service.IntentActionsService"
|
||||||
android:exported="false"/>
|
android:exported="false"/>
|
||||||
|
|
|
@ -69,6 +69,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;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
// Other keys used in preferences.xml
|
// Other keys used in preferences.xml
|
||||||
public static final String KEY_PREF_ABOUT = "pref_about";
|
public static final String KEY_PREF_ABOUT = "pref_about";
|
||||||
|
|
||||||
|
|
|
@ -64,16 +64,28 @@ public abstract class ApiMethod<T> {
|
||||||
* Constructor, sets up the necessary items to make the call later
|
* Constructor, sets up the necessary items to make the call later
|
||||||
*/
|
*/
|
||||||
public ApiMethod() {
|
public ApiMethod() {
|
||||||
synchronized (this) {
|
this(true);
|
||||||
this.id = (++lastId % 10000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor, sets up the necessary items to make the call later
|
||||||
|
*/
|
||||||
|
public ApiMethod(boolean sendId) {
|
||||||
// Create the rpc request object with the common fields according to JSON RPC spec
|
// Create the rpc request object with the common fields according to JSON RPC spec
|
||||||
jsonRequest = objectMapper.createObjectNode();
|
jsonRequest = objectMapper.createObjectNode();
|
||||||
jsonRequest.put("jsonrpc", "2.0");
|
jsonRequest.put("jsonrpc", "2.0");
|
||||||
jsonRequest.put(METHOD_NODE, getMethodName());
|
jsonRequest.put(METHOD_NODE, getMethodName());
|
||||||
|
|
||||||
|
if(sendId) {
|
||||||
|
synchronized (this) {
|
||||||
|
this.id = (++lastId % 10000);
|
||||||
|
}
|
||||||
jsonRequest.put(ID_NODE, id);
|
jsonRequest.put(ID_NODE, id);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
id = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the parameters node of the json request object
|
* Returns the parameters node of the json request object
|
||||||
|
|
|
@ -116,7 +116,6 @@ public class Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pauses or unpause playback and returns the new state
|
* Pauses or unpause playback and returns the new state
|
||||||
*/
|
*/
|
||||||
|
@ -493,4 +492,29 @@ public class Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send notification message to XBMC/Kodi
|
||||||
|
*/
|
||||||
|
public static final class Notification extends ApiMethod<String> {
|
||||||
|
public final static String METHOD_NAME = "GUI.ShowNotification";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a text notification message to XBMC/Kodi
|
||||||
|
* @param title The title of the notification
|
||||||
|
* @param message The text message of the notification
|
||||||
|
*/
|
||||||
|
public Notification(String title, String message) {
|
||||||
|
super(false);
|
||||||
|
addParameterToRequest("title", title);
|
||||||
|
addParameterToRequest("message", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMethodName() { return METHOD_NAME; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String resultFromJson(ObjectNode jsonObject) throws ApiException {
|
||||||
|
return jsonObject.get(RESULT_NODE).textValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,226 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 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 org.xbmc.kore.service;
|
||||||
|
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import org.xbmc.kore.Settings;
|
||||||
|
import org.xbmc.kore.host.HostConnectionObserver;
|
||||||
|
import org.xbmc.kore.host.HostManager;
|
||||||
|
import org.xbmc.kore.jsonrpc.type.ListType;
|
||||||
|
import org.xbmc.kore.jsonrpc.type.PlayerType;
|
||||||
|
import org.xbmc.kore.utils.LogUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This service is a wrapper over {@link HostConnectionObserver} that
|
||||||
|
* manages connection observers, and does it in a service that keeps running
|
||||||
|
* until the connection is lost.
|
||||||
|
* The observers are created here.
|
||||||
|
* This service stops itself as soon as there's no connection.
|
||||||
|
*
|
||||||
|
* 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 ConnectionObserversManagerService extends Service
|
||||||
|
implements HostConnectionObserver.PlayerEventsObserver {
|
||||||
|
public static final String TAG = LogUtils.makeLogTag(ConnectionObserversManagerService.class);
|
||||||
|
|
||||||
|
private HostConnectionObserver mHostConnectionObserver = null;
|
||||||
|
|
||||||
|
private List<HostConnectionObserver.PlayerEventsObserver> mConnectionObservers;
|
||||||
|
|
||||||
|
@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
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
|
LogUtils.LOGD(TAG, "onStartCommand");
|
||||||
|
// Create the observers we are managing
|
||||||
|
createObservers();
|
||||||
|
|
||||||
|
// If no observers created, stop immediately
|
||||||
|
if (mConnectionObservers.size() == 0) {
|
||||||
|
LogUtils.LOGD(TAG, "No observers, stopping observer service.");
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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_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_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createObservers() {
|
||||||
|
mConnectionObservers = new ArrayList<>();
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
mConnectionObservers.add(new NotificationObserver(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether we should react to phone state changes
|
||||||
|
boolean shouldPause = PreferenceManager
|
||||||
|
.getDefaultSharedPreferences(this)
|
||||||
|
.getBoolean(Settings.KEY_PREF_USE_HARDWARE_VOLUME_KEYS,
|
||||||
|
Settings.DEFAULT_PREF_USE_HARDWARE_VOLUME_KEYS);
|
||||||
|
if (shouldPause) {
|
||||||
|
mConnectionObservers.add(new PauseCallObserver(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
// We don't provide binding, so return null
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTaskRemoved (Intent rootIntent) {
|
||||||
|
// Gracefully stop
|
||||||
|
for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) {
|
||||||
|
observer.playerOnConnectionError(0, "Task removed");
|
||||||
|
}
|
||||||
|
|
||||||
|
LogUtils.LOGD(TAG, "Shutting down observer service - Task removed");
|
||||||
|
if (mHostConnectionObserver != null) {
|
||||||
|
mHostConnectionObserver.unregisterPlayerObserver(this);
|
||||||
|
}
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
// Gracefully stop
|
||||||
|
for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) {
|
||||||
|
observer.playerOnConnectionError(0, "Service destroyed");
|
||||||
|
}
|
||||||
|
LogUtils.LOGD(TAG, "Shutting down observer service - destroyed");
|
||||||
|
if (mHostConnectionObserver != null) {
|
||||||
|
mHostConnectionObserver.unregisterPlayerObserver(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HostConnectionObserver.PlayerEventsObserver interface callbacks
|
||||||
|
*/
|
||||||
|
public void playerOnPlay(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||||
|
PlayerType.PropertyValue getPropertiesResult,
|
||||||
|
ListType.ItemsAll getItemResult) {
|
||||||
|
for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) {
|
||||||
|
observer.playerOnPlay(getActivePlayerResult, getPropertiesResult, getItemResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void playerOnPause(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||||
|
PlayerType.PropertyValue getPropertiesResult,
|
||||||
|
ListType.ItemsAll getItemResult) {
|
||||||
|
for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) {
|
||||||
|
observer.playerOnPause(getActivePlayerResult, getPropertiesResult, getItemResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void playerOnStop() {
|
||||||
|
for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) {
|
||||||
|
observer.playerOnStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop service
|
||||||
|
LogUtils.LOGD(TAG, "Player stopped");
|
||||||
|
// if (mHostConnectionObserver != null) {
|
||||||
|
// mHostConnectionObserver.unregisterPlayerObserver(this);
|
||||||
|
// }
|
||||||
|
// stopSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void playerNoResultsYet() {
|
||||||
|
for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) {
|
||||||
|
observer.playerNoResultsYet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void playerOnConnectionError(int errorCode, String description) {
|
||||||
|
for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) {
|
||||||
|
observer.playerOnConnectionError(errorCode, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop service
|
||||||
|
LogUtils.LOGD(TAG, "Shutting down observer service - Connection error");
|
||||||
|
if (mHostConnectionObserver != null) {
|
||||||
|
mHostConnectionObserver.unregisterPlayerObserver(this);
|
||||||
|
}
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void systemOnQuit() {
|
||||||
|
for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) {
|
||||||
|
observer.systemOnQuit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop service
|
||||||
|
LogUtils.LOGD(TAG, "Shutting down observer service - System quit");
|
||||||
|
if (mHostConnectionObserver != null) {
|
||||||
|
mHostConnectionObserver.unregisterPlayerObserver(this);
|
||||||
|
}
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore this
|
||||||
|
public void inputOnInputRequested(String title, String type, String value) {
|
||||||
|
for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) {
|
||||||
|
observer.inputOnInputRequested(title, type, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void observerOnStopObserving() {
|
||||||
|
for (HostConnectionObserver.PlayerEventsObserver observer : mConnectionObservers) {
|
||||||
|
observer.observerOnStopObserving();
|
||||||
|
}
|
||||||
|
// Called when the user changes host
|
||||||
|
LogUtils.LOGD(TAG, "Shutting down observer service - Stop observing");
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
}
|
|
@ -46,87 +46,32 @@ import org.xbmc.kore.utils.UIUtils;
|
||||||
import org.xbmc.kore.utils.Utils;
|
import org.xbmc.kore.utils.Utils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This service maintains a notification in the notification area while
|
* This class mantains a notification on the notification area while something is playing.
|
||||||
* something is playing, and keeps running while it is playing.
|
* It is meant to be used in conjunction with {@link ConnectionObserversManagerService},
|
||||||
* This service stops itself as soon as the playing stops or there's no
|
* which should create an instance of this and manage it
|
||||||
* 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
|
public class NotificationObserver
|
||||||
implements HostConnectionObserver.PlayerEventsObserver {
|
implements HostConnectionObserver.PlayerEventsObserver {
|
||||||
public static final String TAG = LogUtils.makeLogTag(NotificationService.class);
|
public static final String TAG = LogUtils.makeLogTag(NotificationObserver.class);
|
||||||
|
|
||||||
private static final int NOTIFICATION_ID = 1;
|
private static final int NOTIFICATION_ID = 1;
|
||||||
|
|
||||||
private HostConnectionObserver mHostConnectionObserver = null;
|
|
||||||
|
|
||||||
private PendingIntent mRemoteStartPendingIntent;
|
private PendingIntent mRemoteStartPendingIntent;
|
||||||
|
private Context mContext;
|
||||||
|
|
||||||
@Override
|
public NotificationObserver(Context context) {
|
||||||
public void onCreate() {
|
this.mContext = context;
|
||||||
// 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
|
|
||||||
|
|
||||||
// Create the intent to start the remote when the user taps the notification
|
// Create the intent to start the remote when the user taps the notification
|
||||||
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
|
TaskStackBuilder stackBuilder = TaskStackBuilder.create(mContext);
|
||||||
stackBuilder.addParentStack(RemoteActivity.class);
|
stackBuilder.addParentStack(RemoteActivity.class);
|
||||||
stackBuilder.addNextIntent(new Intent(this, RemoteActivity.class));
|
stackBuilder.addNextIntent(new Intent(mContext, RemoteActivity.class));
|
||||||
mRemoteStartPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
|
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_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_STICKY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBinder onBind(Intent intent) {
|
|
||||||
// We don't provide binding, so return null
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTaskRemoved (Intent rootIntent) {
|
|
||||||
// Gracefully stop
|
|
||||||
removeNotification();
|
|
||||||
LogUtils.LOGD(TAG, "Shutting down notification service - Task removed");
|
|
||||||
if (mHostConnectionObserver != null) {
|
|
||||||
mHostConnectionObserver.unregisterPlayerObserver(this);
|
|
||||||
}
|
|
||||||
stopSelf();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HostConnectionObserver.PlayerEventsObserver interface callbacks
|
* HostConnectionObserver.PlayerEventsObserver interface callbacks
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void playerOnPlay(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
public void playerOnPlay(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||||
PlayerType.PropertyValue getPropertiesResult,
|
PlayerType.PropertyValue getPropertiesResult,
|
||||||
ListType.ItemsAll getItemResult) {
|
ListType.ItemsAll getItemResult) {
|
||||||
|
@ -141,12 +86,6 @@ public class NotificationService extends Service
|
||||||
|
|
||||||
public void playerOnStop() {
|
public void playerOnStop() {
|
||||||
removeNotification();
|
removeNotification();
|
||||||
// Stop service
|
|
||||||
LogUtils.LOGD(TAG, "Shutting down notification service - Player stopped");
|
|
||||||
if (mHostConnectionObserver != null) {
|
|
||||||
mHostConnectionObserver.unregisterPlayerObserver(this);
|
|
||||||
}
|
|
||||||
stopSelf();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void playerNoResultsYet() {
|
public void playerNoResultsYet() {
|
||||||
|
@ -155,22 +94,10 @@ public class NotificationService extends Service
|
||||||
|
|
||||||
public void playerOnConnectionError(int errorCode, String description) {
|
public void playerOnConnectionError(int errorCode, String description) {
|
||||||
removeNotification();
|
removeNotification();
|
||||||
// Stop service
|
|
||||||
LogUtils.LOGD(TAG, "Shutting down notification service - Connection error");
|
|
||||||
if (mHostConnectionObserver != null) {
|
|
||||||
mHostConnectionObserver.unregisterPlayerObserver(this);
|
|
||||||
}
|
|
||||||
stopSelf();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void systemOnQuit() {
|
public void systemOnQuit() {
|
||||||
removeNotification();
|
removeNotification();
|
||||||
// Stop service
|
|
||||||
LogUtils.LOGD(TAG, "Shutting down notification service - System quit");
|
|
||||||
if (mHostConnectionObserver != null) {
|
|
||||||
mHostConnectionObserver.unregisterPlayerObserver(this);
|
|
||||||
}
|
|
||||||
stopSelf();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore this
|
// Ignore this
|
||||||
|
@ -179,8 +106,6 @@ public class NotificationService extends Service
|
||||||
public void observerOnStopObserving() {
|
public void observerOnStopObserving() {
|
||||||
// Called when the user changes host
|
// Called when the user changes host
|
||||||
removeNotification();
|
removeNotification();
|
||||||
LogUtils.LOGD(TAG, "Shutting down notification service - System quit");
|
|
||||||
stopSelf();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Picasso target that will be used to load images
|
// Picasso target that will be used to load images
|
||||||
|
@ -205,7 +130,7 @@ public class NotificationService extends Service
|
||||||
break;
|
break;
|
||||||
case ListType.ItemsAll.TYPE_EPISODE:
|
case ListType.ItemsAll.TYPE_EPISODE:
|
||||||
title = getItemResult.title;
|
title = getItemResult.title;
|
||||||
String seasonEpisode = String.format(getString(R.string.season_episode_abbrev),
|
String seasonEpisode = String.format(mContext.getString(R.string.season_episode_abbrev),
|
||||||
getItemResult.season, getItemResult.episode);
|
getItemResult.season, getItemResult.episode);
|
||||||
underTitle = String.format("%s | %s", getItemResult.showtitle, seasonEpisode);
|
underTitle = String.format("%s | %s", getItemResult.showtitle, seasonEpisode);
|
||||||
poster = getItemResult.art.poster;
|
poster = getItemResult.art.poster;
|
||||||
|
@ -253,7 +178,7 @@ public class NotificationService extends Service
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup the collpased and expanded notifications
|
// Setup the collpased and expanded notifications
|
||||||
final RemoteViews collapsedRV = new RemoteViews(this.getPackageName(), R.layout.notification_colapsed);
|
final RemoteViews collapsedRV = new RemoteViews(mContext.getPackageName(), R.layout.notification_colapsed);
|
||||||
collapsedRV.setImageViewResource(R.id.rewind, rewindIcon);
|
collapsedRV.setImageViewResource(R.id.rewind, rewindIcon);
|
||||||
collapsedRV.setOnClickPendingIntent(R.id.rewind, rewindPendingItent);
|
collapsedRV.setOnClickPendingIntent(R.id.rewind, rewindPendingItent);
|
||||||
collapsedRV.setImageViewResource(R.id.play, playPauseIcon);
|
collapsedRV.setImageViewResource(R.id.play, playPauseIcon);
|
||||||
|
@ -263,7 +188,7 @@ public class NotificationService extends Service
|
||||||
collapsedRV.setTextViewText(R.id.title, title);
|
collapsedRV.setTextViewText(R.id.title, title);
|
||||||
collapsedRV.setTextViewText(R.id.text2, underTitle);
|
collapsedRV.setTextViewText(R.id.text2, underTitle);
|
||||||
|
|
||||||
final RemoteViews expandedRV = new RemoteViews(this.getPackageName(), R.layout.notification_expanded);
|
final RemoteViews expandedRV = new RemoteViews(mContext.getPackageName(), R.layout.notification_expanded);
|
||||||
expandedRV.setImageViewResource(R.id.rewind, rewindIcon);
|
expandedRV.setImageViewResource(R.id.rewind, rewindIcon);
|
||||||
expandedRV.setOnClickPendingIntent(R.id.rewind, rewindPendingItent);
|
expandedRV.setOnClickPendingIntent(R.id.rewind, rewindPendingItent);
|
||||||
expandedRV.setImageViewResource(R.id.play, playPauseIcon);
|
expandedRV.setImageViewResource(R.id.play, playPauseIcon);
|
||||||
|
@ -284,7 +209,7 @@ public class NotificationService extends Service
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the notification
|
// Build the notification
|
||||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext);
|
||||||
final Notification notification = builder
|
final Notification notification = builder
|
||||||
.setSmallIcon(smallIcon)
|
.setSmallIcon(smallIcon)
|
||||||
.setShowWhen(false)
|
.setShowWhen(false)
|
||||||
|
@ -314,7 +239,7 @@ public class NotificationService extends Service
|
||||||
//
|
//
|
||||||
// 4. We specifically resize the image to the same dimensions used in
|
// 4. We specifically resize the image to the same dimensions used in
|
||||||
// the remote, so that Picasso reuses it in the remote and here from the cache
|
// the remote, so that Picasso reuses it in the remote and here from the cache
|
||||||
Resources resources = this.getResources();
|
Resources resources = mContext.getResources();
|
||||||
final int posterWidth = resources.getDimensionPixelOffset(R.dimen.now_playing_poster_width);
|
final int posterWidth = resources.getDimensionPixelOffset(R.dimen.now_playing_poster_width);
|
||||||
final int posterHeight = isVideo?
|
final int posterHeight = isVideo?
|
||||||
resources.getDimensionPixelOffset(R.dimen.now_playing_poster_height):
|
resources.getDimensionPixelOffset(R.dimen.now_playing_poster_height):
|
||||||
|
@ -328,7 +253,7 @@ public class NotificationService extends Service
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBitmapFailed(Drawable errorDrawable) {
|
public void onBitmapFailed(Drawable errorDrawable) {
|
||||||
CharacterDrawable avatarDrawable = UIUtils.getCharacterAvatar(NotificationService.this, title);
|
CharacterDrawable avatarDrawable = UIUtils.getCharacterAvatar(mContext, title);
|
||||||
showNotification(Utils.drawableToBitmap(avatarDrawable, posterWidth, posterHeight));
|
showNotification(Utils.drawableToBitmap(avatarDrawable, posterWidth, posterHeight));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,14 +267,15 @@ public class NotificationService extends Service
|
||||||
expandedRV.setImageViewBitmap(expandedIconResId, bitmap);
|
expandedRV.setImageViewBitmap(expandedIconResId, bitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
|
NotificationManager notificationManager =
|
||||||
|
(NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
notificationManager.notify(NOTIFICATION_ID, notification);
|
notificationManager.notify(NOTIFICATION_ID, notification);
|
||||||
picassoTarget = null;
|
picassoTarget = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Load the image
|
// Load the image
|
||||||
HostManager hostManager = HostManager.getInstance(this);
|
HostManager hostManager = HostManager.getInstance(mContext);
|
||||||
hostManager.getPicasso()
|
hostManager.getPicasso()
|
||||||
.load(hostManager.getHostInfo().getImageUrl(poster))
|
.load(hostManager.getHostInfo().getImageUrl(poster))
|
||||||
.resize(posterWidth, posterHeight)
|
.resize(posterWidth, posterHeight)
|
||||||
|
@ -358,15 +284,16 @@ public class NotificationService extends Service
|
||||||
}
|
}
|
||||||
|
|
||||||
private PendingIntent buildActionPendingIntent(int playerId, String action) {
|
private PendingIntent buildActionPendingIntent(int playerId, String action) {
|
||||||
Intent intent = new Intent(this, IntentActionsService.class)
|
Intent intent = new Intent(mContext, IntentActionsService.class)
|
||||||
.setAction(action)
|
.setAction(action)
|
||||||
.putExtra(IntentActionsService.EXTRA_PLAYER_ID, playerId);
|
.putExtra(IntentActionsService.EXTRA_PLAYER_ID, playerId);
|
||||||
|
|
||||||
return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
return PendingIntent.getService(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeNotification() {
|
private void removeNotification() {
|
||||||
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
|
NotificationManager notificationManager =
|
||||||
|
(NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
notificationManager.cancel(NOTIFICATION_ID);
|
notificationManager.cancel(NOTIFICATION_ID);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Tomer Froumin. 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.service;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.telephony.PhoneStateListener;
|
||||||
|
import android.telephony.TelephonyManager;
|
||||||
|
|
||||||
|
import org.xbmc.kore.R;
|
||||||
|
import org.xbmc.kore.host.HostConnectionObserver;
|
||||||
|
import org.xbmc.kore.host.HostManager;
|
||||||
|
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.utils.LogUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This listener handles changes to the phone state, such as receiving a
|
||||||
|
* call or hanging up, and synchronizes Kodi's currently playing state
|
||||||
|
* in order to prevent missing the movie (or what's playing) while the
|
||||||
|
* viewer is talking on the phone.
|
||||||
|
*
|
||||||
|
* The listener query Kodi's state on phone state changed event.
|
||||||
|
* When a call ends we only resume if it was paused by the listener.
|
||||||
|
*/
|
||||||
|
public class PauseCallObserver extends PhoneStateListener
|
||||||
|
implements HostConnectionObserver.PlayerEventsObserver {
|
||||||
|
public static final String TAG = LogUtils.makeLogTag(PauseCallObserver.class);
|
||||||
|
|
||||||
|
private int currentActivePlayerId = -1;
|
||||||
|
private boolean isPlaying = false;
|
||||||
|
private boolean shouldResume = false;
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
private HostManager mHostManager;
|
||||||
|
|
||||||
|
public PauseCallObserver(Context context) {
|
||||||
|
this.mContext = context;
|
||||||
|
mHostManager = HostManager.getInstance(context);
|
||||||
|
|
||||||
|
((TelephonyManager)mContext
|
||||||
|
.getSystemService(Context.TELEPHONY_SERVICE))
|
||||||
|
.listen(this, PhoneStateListener.LISTEN_CALL_STATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCallStateChanged(int state, String incomingNumber) {
|
||||||
|
if (state == TelephonyManager.CALL_STATE_OFFHOOK && isPlaying) {
|
||||||
|
Player.PlayPause action = new Player.PlayPause(currentActivePlayerId);
|
||||||
|
action.execute(mHostManager.getConnection(), null, null);
|
||||||
|
shouldResume = true;
|
||||||
|
} else if (state == TelephonyManager.CALL_STATE_IDLE && !isPlaying && shouldResume) {
|
||||||
|
Player.PlayPause action = new Player.PlayPause(currentActivePlayerId);
|
||||||
|
action.execute(mHostManager.getConnection(), null, null);
|
||||||
|
shouldResume = false;
|
||||||
|
} else if (state == TelephonyManager.CALL_STATE_RINGING) {
|
||||||
|
Player.Notification action = new Player.Notification(
|
||||||
|
mContext.getResources().getString(R.string.pause_call_incoming_title),
|
||||||
|
mContext.getResources().getString(R.string.pause_call_incoming_message));
|
||||||
|
action.execute(mHostManager.getConnection(), null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void playerOnPlay(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||||
|
PlayerType.PropertyValue getPropertiesResult,
|
||||||
|
ListType.ItemsAll getItemResult) {
|
||||||
|
currentActivePlayerId = getActivePlayerResult.playerid;
|
||||||
|
isPlaying = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void playerOnPause(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||||
|
PlayerType.PropertyValue getPropertiesResult,
|
||||||
|
ListType.ItemsAll getItemResult) {
|
||||||
|
if(currentActivePlayerId != getActivePlayerResult.playerid) {
|
||||||
|
shouldResume = false;
|
||||||
|
}
|
||||||
|
currentActivePlayerId = getActivePlayerResult.playerid;
|
||||||
|
isPlaying = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopListener() {
|
||||||
|
((TelephonyManager)mContext
|
||||||
|
.getSystemService(Context.TELEPHONY_SERVICE))
|
||||||
|
.listen(this, PhoneStateListener.LISTEN_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void playerOnStop() {
|
||||||
|
currentActivePlayerId = -1;
|
||||||
|
isPlaying = false;
|
||||||
|
shouldResume = false;
|
||||||
|
stopListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void playerOnConnectionError(int errorCode, String description) {
|
||||||
|
playerOnStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void playerNoResultsYet() {
|
||||||
|
playerOnStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void systemOnQuit() {
|
||||||
|
playerOnStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void inputOnInputRequested(String title, String type, String value) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void observerOnStopObserving() {
|
||||||
|
playerOnStop();
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.xbmc.kore.ui;
|
package org.xbmc.kore.ui;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
|
@ -27,6 +28,8 @@ import android.support.v4.view.ViewPager;
|
||||||
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.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.telephony.PhoneStateListener;
|
||||||
|
import android.telephony.TelephonyManager;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
|
@ -38,9 +41,7 @@ import android.widget.Toast;
|
||||||
|
|
||||||
import org.xbmc.kore.R;
|
import org.xbmc.kore.R;
|
||||||
import org.xbmc.kore.Settings;
|
import org.xbmc.kore.Settings;
|
||||||
import org.xbmc.kore.eventclient.EventServerConnection;
|
|
||||||
import org.xbmc.kore.host.HostConnectionObserver;
|
import org.xbmc.kore.host.HostConnectionObserver;
|
||||||
import org.xbmc.kore.host.HostInfo;
|
|
||||||
import org.xbmc.kore.host.HostManager;
|
import org.xbmc.kore.host.HostManager;
|
||||||
import org.xbmc.kore.jsonrpc.ApiCallback;
|
import org.xbmc.kore.jsonrpc.ApiCallback;
|
||||||
import org.xbmc.kore.jsonrpc.HostConnection;
|
import org.xbmc.kore.jsonrpc.HostConnection;
|
||||||
|
@ -56,7 +57,7 @@ import org.xbmc.kore.jsonrpc.type.GlobalType;
|
||||||
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.jsonrpc.type.PlaylistType;
|
import org.xbmc.kore.jsonrpc.type.PlaylistType;
|
||||||
import org.xbmc.kore.service.NotificationService;
|
import org.xbmc.kore.service.ConnectionObserversManagerService;
|
||||||
import org.xbmc.kore.ui.hosts.AddHostActivity;
|
import org.xbmc.kore.ui.hosts.AddHostActivity;
|
||||||
import org.xbmc.kore.ui.hosts.AddHostFragmentFinish;
|
import org.xbmc.kore.ui.hosts.AddHostFragmentFinish;
|
||||||
import org.xbmc.kore.ui.views.CirclePageIndicator;
|
import org.xbmc.kore.ui.views.CirclePageIndicator;
|
||||||
|
@ -618,15 +619,32 @@ public class RemoteActivity extends BaseActivity
|
||||||
}
|
}
|
||||||
lastImageUrl = imageUrl;
|
lastImageUrl = imageUrl;
|
||||||
|
|
||||||
// Check whether we should show a notification
|
// Start service that manages connection observers
|
||||||
boolean showNotification = PreferenceManager
|
LogUtils.LOGD(TAG, "Starting observer service");
|
||||||
.getDefaultSharedPreferences(this)
|
startService(new Intent(this, ConnectionObserversManagerService.class));
|
||||||
.getBoolean(Settings.KEY_PREF_SHOW_NOTIFICATION, Settings.DEFAULT_PREF_SHOW_NOTIFICATION);
|
|
||||||
if (showNotification) {
|
|
||||||
// Let's start the notification service
|
// // Check whether we should show a notification
|
||||||
LogUtils.LOGD(TAG, "Starting notification service");
|
// boolean showNotification = PreferenceManager
|
||||||
startService(new Intent(this, NotificationService.class));
|
// .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, NotificationObserver.class));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Check whether we should react to phone state changes
|
||||||
|
// boolean shouldPause = PreferenceManager
|
||||||
|
// .getDefaultSharedPreferences(this)
|
||||||
|
// .getBoolean(Settings.KEY_PREF_USE_HARDWARE_VOLUME_KEYS,
|
||||||
|
// Settings.DEFAULT_PREF_USE_HARDWARE_VOLUME_KEYS);
|
||||||
|
// if (shouldPause) {
|
||||||
|
// // Let's start the listening service
|
||||||
|
// LogUtils.LOGD(TAG, "Starting phone state listener");
|
||||||
|
// startService(new Intent(this, PauseCallObserver.class));
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
public void playerOnPause(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
public void playerOnPause(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
|
||||||
|
@ -636,6 +654,7 @@ public class RemoteActivity extends BaseActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
public void playerOnStop() {
|
public void playerOnStop() {
|
||||||
|
LogUtils.LOGD(TAG, "Player stopping");
|
||||||
if (lastImageUrl != null) {
|
if (lastImageUrl != null) {
|
||||||
setImageViewBackground(null);
|
setImageViewBackground(null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import android.support.v4.app.TaskStackBuilder;
|
||||||
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.HostManager;
|
import org.xbmc.kore.host.HostManager;
|
||||||
|
import org.xbmc.kore.service.ConnectionObserversManagerService;
|
||||||
import org.xbmc.kore.utils.LogUtils;
|
import org.xbmc.kore.utils.LogUtils;
|
||||||
import org.xbmc.kore.utils.UIUtils;
|
import org.xbmc.kore.utils.UIUtils;
|
||||||
|
|
||||||
|
@ -105,6 +106,14 @@ public class SettingsFragment extends PreferenceFragment
|
||||||
.addNextIntent(new Intent(getActivity(), SettingsActivity.class))
|
.addNextIntent(new Intent(getActivity(), SettingsActivity.class))
|
||||||
.startActivities();
|
.startActivities();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If one of the settings that use the observer service are modified, restart it
|
||||||
|
if (key.equals(Settings.KEY_PREF_SHOW_NOTIFICATION) || key.equals(Settings.KEY_PREF_PAUSE_DURING_CALLS)) {
|
||||||
|
LogUtils.LOGD(TAG, "Stoping connection observer service");
|
||||||
|
Intent intent = new Intent(getActivity(), ConnectionObserversManagerService.class);
|
||||||
|
getActivity().stopService(intent);
|
||||||
|
getActivity().startService(intent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -327,6 +327,7 @@
|
||||||
<string name="switch_to_remote">Switch to remote after media start</string>
|
<string name="switch_to_remote">Switch to remote after media start</string>
|
||||||
<string name="keep_remote_above_lockscreen">Keep remote above lockscreen</string>
|
<string name="keep_remote_above_lockscreen">Keep remote above lockscreen</string>
|
||||||
<string name="show_notification">Show notification while playing</string>
|
<string name="show_notification">Show notification while playing</string>
|
||||||
|
<string name="pause_during_calls">Pause playing while phone in a call</string>
|
||||||
<string name="use_hardware_volume_keys">Use volume keys to control volume</string>
|
<string name="use_hardware_volume_keys">Use volume keys to control volume</string>
|
||||||
<string name="vibrate_on_remote">Vibrate on remote button press</string>
|
<string name="vibrate_on_remote">Vibrate on remote button press</string>
|
||||||
<string name="nav_drawer_items">Side menu shortcuts</string>
|
<string name="nav_drawer_items">Side menu shortcuts</string>
|
||||||
|
@ -352,6 +353,8 @@
|
||||||
<!--<string name="purchase_thanks">Thanks for your support!</string>-->
|
<!--<string name="purchase_thanks">Thanks for your support!</string>-->
|
||||||
|
|
||||||
<string name="play_on_kodi">Play on Kodi</string>
|
<string name="play_on_kodi">Play on Kodi</string>
|
||||||
|
<string name="pause_call_incoming_title">Incoming call</string>
|
||||||
|
<string name="pause_call_incoming_message">Check your phone, someone is calling you</string>
|
||||||
|
|
||||||
<string name="error_getting_pvr_info">An error occurred while getting pvr info: %1$s</string>
|
<string name="error_getting_pvr_info">An error occurred while getting pvr info: %1$s</string>
|
||||||
<string name="might_not_have_pvr">An error occurred while getting channels info, probably because your media center doesn\'t have a tuner or it isn\'t configured.\n\nIf that\'s the case and you\'d like to remove this entry from the side menu, you can do it in the Settings.</string>
|
<string name="might_not_have_pvr">An error occurred while getting channels info, probably because your media center doesn\'t have a tuner or it isn\'t configured.\n\nIf that\'s the case and you\'d like to remove this entry from the side menu, you can do it in the Settings.</string>
|
||||||
|
|
|
@ -39,6 +39,11 @@
|
||||||
android:title="@string/show_notification"
|
android:title="@string/show_notification"
|
||||||
android:defaultValue="false"/>
|
android:defaultValue="false"/>
|
||||||
|
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="pref_pause_during_calls"
|
||||||
|
android:title="@string/pause_during_calls"
|
||||||
|
android:defaultValue="false"/>
|
||||||
|
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
android:key="pref_use_hardware_volume_keys"
|
android:key="pref_use_hardware_volume_keys"
|
||||||
android:title="@string/use_hardware_volume_keys"
|
android:title="@string/use_hardware_volume_keys"
|
||||||
|
|
Loading…
Reference in New Issue