diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2b61071..5baf1f0 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -9,6 +9,7 @@
+
{
* Constructor, sets up the necessary items to make the call later
*/
public ApiMethod() {
- synchronized (this) {
- this.id = (++lastId % 10000);
- }
+ this(true);
+ }
+ /**
+ * 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
jsonRequest = objectMapper.createObjectNode();
jsonRequest.put("jsonrpc", "2.0");
jsonRequest.put(METHOD_NODE, getMethodName());
- jsonRequest.put(ID_NODE, id);
+
+ if(sendId) {
+ synchronized (this) {
+ this.id = (++lastId % 10000);
+ }
+ jsonRequest.put(ID_NODE, id);
+ }
+ else {
+ id = -1;
+ }
}
/**
diff --git a/app/src/main/java/org/xbmc/kore/jsonrpc/method/Player.java b/app/src/main/java/org/xbmc/kore/jsonrpc/method/Player.java
index 02c72ec..f98aafc 100644
--- a/app/src/main/java/org/xbmc/kore/jsonrpc/method/Player.java
+++ b/app/src/main/java/org/xbmc/kore/jsonrpc/method/Player.java
@@ -116,7 +116,6 @@ public class Player {
}
}
-
/**
* 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 {
+ 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();
+ }
+ }
}
diff --git a/app/src/main/java/org/xbmc/kore/service/PauseCallService.java b/app/src/main/java/org/xbmc/kore/service/PauseCallService.java
new file mode 100644
index 0000000..8ecf548
--- /dev/null
+++ b/app/src/main/java/org/xbmc/kore/service/PauseCallService.java
@@ -0,0 +1,118 @@
+/*
+ * 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 PauseCallService extends PhoneStateListener
+ implements HostConnectionObserver.PlayerEventsObserver {
+ public static final String TAG = LogUtils.makeLogTag(PauseCallService.class);
+ private Context context;
+ private int currentActivePlayerId = -1;
+ private boolean isPlaying = false;
+ private boolean shouldResume = false;
+
+ public PauseCallService(Context context) {
+ this.context = context;
+ }
+
+ @Override
+ public void onCallStateChanged(int state, String incomingNumber) {
+ // 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
+ HostManager hostManager = HostManager.getInstance(context);
+ hostManager.getHostConnectionObserver().replyWithLastResult(this);
+
+ if (state == TelephonyManager.CALL_STATE_OFFHOOK && isPlaying) {
+ Player.PlayPause action = new Player.PlayPause(currentActivePlayerId);
+ action.execute(hostManager.getConnection(), null, null);
+ shouldResume = true;
+ } else if (state == TelephonyManager.CALL_STATE_IDLE && !isPlaying && shouldResume) {
+ Player.PlayPause action = new Player.PlayPause(currentActivePlayerId);
+ action.execute(hostManager.getConnection(), null, null);
+ shouldResume = false;
+ } else if (state == TelephonyManager.CALL_STATE_RINGING) {
+ Player.Notification action = new Player.Notification(
+ context.getResources().getString(R.string.pause_call_incoming_title),
+ context.getResources().getString(R.string.pause_call_incoming_message));
+ action.execute(hostManager.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;
+ }
+
+ @Override
+ public void playerOnStop() {
+ currentActivePlayerId = -1;
+ isPlaying = false;
+ shouldResume = false;
+ }
+
+ @Override
+ public void playerOnConnectionError(int errorCode, String description) {
+ playerOnStop();
+ }
+
+ @Override
+ public void playerNoResultsYet() {
+ playerOnStop();
+ }
+
+ @Override
+ public void systemOnQuit() {}
+
+ @Override
+ public void inputOnInputRequested(String title, String type, String value) {}
+
+ @Override
+ public void observerOnStopObserving() {}
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/xbmc/kore/ui/RemoteActivity.java b/app/src/main/java/org/xbmc/kore/ui/RemoteActivity.java
index 132e294..9f6a625 100644
--- a/app/src/main/java/org/xbmc/kore/ui/RemoteActivity.java
+++ b/app/src/main/java/org/xbmc/kore/ui/RemoteActivity.java
@@ -15,6 +15,7 @@
*/
package org.xbmc.kore.ui;
+import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Point;
@@ -27,6 +28,8 @@ import android.support.v4.view.ViewPager;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.Menu;
@@ -38,9 +41,7 @@ import android.widget.Toast;
import org.xbmc.kore.R;
import org.xbmc.kore.Settings;
-import org.xbmc.kore.eventclient.EventServerConnection;
import org.xbmc.kore.host.HostConnectionObserver;
-import org.xbmc.kore.host.HostInfo;
import org.xbmc.kore.host.HostManager;
import org.xbmc.kore.jsonrpc.ApiCallback;
import org.xbmc.kore.jsonrpc.HostConnection;
@@ -57,6 +58,7 @@ import org.xbmc.kore.jsonrpc.type.ListType;
import org.xbmc.kore.jsonrpc.type.PlayerType;
import org.xbmc.kore.jsonrpc.type.PlaylistType;
import org.xbmc.kore.service.NotificationService;
+import org.xbmc.kore.service.PauseCallService;
import org.xbmc.kore.ui.hosts.AddHostActivity;
import org.xbmc.kore.ui.hosts.AddHostFragmentFinish;
import org.xbmc.kore.ui.views.CirclePageIndicator;
@@ -97,6 +99,8 @@ public class RemoteActivity extends BaseActivity
private NavigationDrawerFragment navigationDrawerFragment;
+ private PauseCallService pauseCallService = null;
+
@InjectView(R.id.background_image) ImageView backgroundImage;
@InjectView(R.id.pager_indicator) CirclePageIndicator pageIndicator;
@InjectView(R.id.pager) ViewPager viewPager;
@@ -621,12 +625,28 @@ public class RemoteActivity extends BaseActivity
// Check whether we should show a notification
boolean showNotification = PreferenceManager
.getDefaultSharedPreferences(this)
- .getBoolean(Settings.KEY_PREF_SHOW_NOTIFICATION, Settings.DEFAULT_PREF_SHOW_NOTIFICATION);
+ .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));
}
+
+ // 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 notification service
+ LogUtils.LOGD(TAG, "Starting phone state listener");
+ if(pauseCallService == null) {
+ pauseCallService = new PauseCallService(this);
+ ((TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE)).listen(
+ pauseCallService, PhoneStateListener.LISTEN_CALL_STATE);
+ }
+ }
}
public void playerOnPause(PlayerType.GetActivePlayersReturnType getActivePlayerResult,
@@ -640,6 +660,12 @@ public class RemoteActivity extends BaseActivity
setImageViewBackground(null);
}
lastImageUrl = null;
+
+ if(pauseCallService != null) {
+ ((TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE)).listen(
+ pauseCallService, PhoneStateListener.LISTEN_NONE);
+ pauseCallService = null;
+ }
}
public void playerNoResultsYet() {
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 2200a89..13cac18 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -327,6 +327,7 @@
Switch to remote after media start
Keep remote above lockscreen
Show notification while playing
+ Pause playing while phone in a call
Use volume keys to control volume
Vibrate on remote button press
Side menu shortcuts
@@ -352,6 +353,8 @@
Play on Kodi
+ Incoming call
+ Check your phone, someone is calling you
An error occurred while getting pvr info: %1$s
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.
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 16b987c..479c529 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -39,6 +39,11 @@
android:title="@string/show_notification"
android:defaultValue="false"/>
+
+