Added support for pausing when phone in call
This commit is contained in:
parent
eca7d2b2ad
commit
2e7ac6af80
|
@ -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"
|
||||||
|
@ -70,6 +71,13 @@
|
||||||
<service android:name="org.xbmc.kore.service.IntentActionsService"
|
<service android:name="org.xbmc.kore.service.IntentActionsService"
|
||||||
android:exported="false"/>
|
android:exported="false"/>
|
||||||
|
|
||||||
|
<!-- Receivers -->
|
||||||
|
<receiver android:name="org.xbmc.kore.service.PauseCallService">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.PHONE_STATE"/>
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
<uses-library android:required="false" android:name="com.sec.android.app.multiwindow"/>
|
<uses-library android:required="false" android:name="com.sec.android.app.multiwindow"/>
|
||||||
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true" />
|
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true" />
|
||||||
<meta-data android:name="com.sec.android.multiwindow.DEFAULT_SIZE_W" android:value="632.0dip" />
|
<meta-data android:name="com.sec.android.multiwindow.DEFAULT_SIZE_W" android:value="632.0dip" />
|
||||||
|
|
|
@ -53,6 +53,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 = true;
|
||||||
|
|
||||||
// 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,155 @@
|
||||||
|
/*
|
||||||
|
* 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.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.telephony.TelephonyManager;
|
||||||
|
|
||||||
|
import org.xbmc.kore.R;
|
||||||
|
import org.xbmc.kore.Settings;
|
||||||
|
import org.xbmc.kore.host.HostConnectionObserver;
|
||||||
|
import org.xbmc.kore.host.HostManager;
|
||||||
|
import org.xbmc.kore.jsonrpc.method.Player;
|
||||||
|
import org.xbmc.kore.jsonrpc.type.ListType;
|
||||||
|
import org.xbmc.kore.jsonrpc.type.PlayerType;
|
||||||
|
import org.xbmc.kore.utils.LogUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 PauseCallService extends BroadcastReceiver
|
||||||
|
implements HostConnectionObserver.PlayerEventsObserver {
|
||||||
|
public static final String TAG = LogUtils.makeLogTag(PauseCallService.class);
|
||||||
|
private static int lastState = TelephonyManager.CALL_STATE_IDLE;
|
||||||
|
private static HostConnectionObserver mHostConnectionObserver = null;
|
||||||
|
private static int currentActivePlayerId = -1;
|
||||||
|
private static boolean isPlaying = false;
|
||||||
|
private static boolean shouldResume = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
// Check whether we should react to phone state changes
|
||||||
|
boolean shouldPause = PreferenceManager
|
||||||
|
.getDefaultSharedPreferences(context)
|
||||||
|
.getBoolean(Settings.KEY_PREF_PAUSE_DURING_CALLS, Settings.DEFAULT_PREF_PAUSE_DURING_CALLS);
|
||||||
|
if(!shouldPause) return;
|
||||||
|
|
||||||
|
int state = 0;
|
||||||
|
String stateStr = intent.getExtras().getString(TelephonyManager.EXTRA_STATE);
|
||||||
|
LogUtils.LOGD(TAG, "onReceive " + stateStr);
|
||||||
|
|
||||||
|
// The phone state changed from in call to idle
|
||||||
|
if(stateStr.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
|
||||||
|
state = TelephonyManager.CALL_STATE_IDLE;
|
||||||
|
}
|
||||||
|
// The phone state changed from idle to in call
|
||||||
|
else if(stateStr.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
|
||||||
|
state = TelephonyManager.CALL_STATE_OFFHOOK;
|
||||||
|
}
|
||||||
|
// The phone state changed from idle to ringing
|
||||||
|
else if(stateStr.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
|
||||||
|
state = TelephonyManager.CALL_STATE_RINGING;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(state == lastState) return;
|
||||||
|
handleState(context, state);
|
||||||
|
lastState = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void handleState(Context context, int state) {
|
||||||
|
// 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);
|
||||||
|
if (mHostConnectionObserver != null) {
|
||||||
|
mHostConnectionObserver.unregisterPlayerObserver(this);
|
||||||
|
}
|
||||||
|
mHostConnectionObserver = hostManager.getHostConnectionObserver();
|
||||||
|
mHostConnectionObserver.registerPlayerObserver(this, true);
|
||||||
|
|
||||||
|
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) {
|
||||||
|
currentActivePlayerId = getActivePlayerResult.playerid;
|
||||||
|
isPlaying = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void playerOnStop() {
|
||||||
|
if (mHostConnectionObserver != null) {
|
||||||
|
mHostConnectionObserver.unregisterPlayerObserver(this);
|
||||||
|
}
|
||||||
|
currentActivePlayerId = -1;
|
||||||
|
isPlaying = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void playerOnConnectionError(int errorCode, String description) {
|
||||||
|
playerOnStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void playerNoResultsYet() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void systemOnQuit() {
|
||||||
|
playerOnStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void inputOnInputRequested(String title, String type, String value) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void observerOnStopObserving() {}
|
||||||
|
}
|
|
@ -36,9 +36,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;
|
||||||
|
|
|
@ -318,6 +318,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="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>
|
||||||
|
@ -343,6 +344,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>
|
||||||
|
|
|
@ -34,6 +34,11 @@
|
||||||
android:title="@string/show_notification"
|
android:title="@string/show_notification"
|
||||||
android:defaultValue="false"/>
|
android:defaultValue="false"/>
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
android:key="pref_pause_during_calls"
|
||||||
|
android:title="@string/pause_during_calls"
|
||||||
|
android:defaultValue="true"/>
|
||||||
|
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
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