Merge "Remote volume handling" into jb-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
2f4423043f
@@ -27,6 +27,7 @@ import android.content.pm.ResolveInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Rect;
|
||||
import android.media.RemoteControlClient;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
@@ -2151,6 +2152,19 @@ public class Intent implements Parcelable, Cloneable {
|
||||
public static final String ACTION_USB_AUDIO_DEVICE_PLUG =
|
||||
"android.intent.action.USB_AUDIO_DEVICE_PLUG";
|
||||
|
||||
/**
|
||||
* @hide (to be un-hidden)
|
||||
* Broadcast Action: the volume handled by the receiver should be updated based on the
|
||||
* mutually exclusive extras, {@link #EXTRA_VOLUME_UPDATE_DIRECTION}
|
||||
* and {@link #EXTRA_VOLUME_UPDATE_VALUE}.
|
||||
*
|
||||
* @see #EXTRA_VOLUME_UPDATE_DIRECTION
|
||||
* @see #EXTRA_VOLUME_UPDATE_VALUE
|
||||
* @see android.media.RemoteControlClient
|
||||
*/
|
||||
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
|
||||
public static final String ACTION_VOLUME_UPDATE = "android.intent.action.VOLUME_UPDATE";
|
||||
|
||||
/**
|
||||
* <p>Broadcast Action: The user has switched on advanced settings in the settings app:</p>
|
||||
* <ul>
|
||||
@@ -2839,6 +2853,27 @@ public class Intent implements Parcelable, Cloneable {
|
||||
*/
|
||||
public static final String EXTRA_USERID =
|
||||
"android.intent.extra.user_id";
|
||||
|
||||
/**
|
||||
* @hide (to be un-hidden)
|
||||
* An integer indicating whether the volume is to be increased (positive value) or decreased
|
||||
* (negative value). For bundled changes, the absolute value indicates the number of changes
|
||||
* in the same direction, e.g. +3 corresponds to three "volume up" changes.
|
||||
* @see #ACTION_VOLUME_UPDATE
|
||||
*/
|
||||
public static final String EXTRA_VOLUME_UPDATE_DIRECTION =
|
||||
"android.intent.extra.VOLUME_UPDATE_DIRECTION";
|
||||
|
||||
/**
|
||||
* @hide (to be un-hidden)
|
||||
* An integer indicating the new volume value, always between 0 and the value set for
|
||||
* {@link RemoteControlClient#PLAYBACKINFO_VOLUME_MAX} with
|
||||
* {@link RemoteControlClient#setPlaybackInformation(int, int)}
|
||||
* @see #ACTION_VOLUME_UPDATE
|
||||
*/
|
||||
public static final String EXTRA_VOLUME_UPDATE_VALUE =
|
||||
"android.intent.extra.VOLUME_UPDATE_VALUE";
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------
|
||||
// Intent flags (see mFlags variable).
|
||||
|
||||
@@ -34,10 +34,7 @@ import android.media.ToneGenerator;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.os.Vibrator;
|
||||
import android.provider.Settings;
|
||||
import android.provider.Settings.System;
|
||||
import android.util.Log;
|
||||
import android.view.WindowManager.LayoutParams;
|
||||
import android.widget.ImageView;
|
||||
@@ -92,9 +89,13 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
||||
private static final int MSG_TIMEOUT = 5;
|
||||
private static final int MSG_RINGER_MODE_CHANGED = 6;
|
||||
private static final int MSG_MUTE_CHANGED = 7;
|
||||
private static final int MSG_REMOTE_VOLUME_CHANGED = 8;
|
||||
private static final int MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN = 9;
|
||||
private static final int MSG_SLIDER_VISIBILITY_CHANGED = 10;
|
||||
|
||||
// Pseudo stream type for master volume
|
||||
private static final int STREAM_MASTER = -100;
|
||||
// Pseudo stream type for remote volume is defined in AudioService.STREAM_REMOTE_MUSIC
|
||||
|
||||
protected Context mContext;
|
||||
private AudioManager mAudioManager;
|
||||
@@ -155,10 +156,15 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
||||
true),
|
||||
// for now, use media resources for master volume
|
||||
MasterStream(STREAM_MASTER,
|
||||
R.string.volume_icon_description_media,
|
||||
R.string.volume_icon_description_media, //FIXME should have its own description
|
||||
R.drawable.ic_audio_vol,
|
||||
R.drawable.ic_audio_vol_mute,
|
||||
false);
|
||||
false),
|
||||
RemoteStream(AudioService.STREAM_REMOTE_MUSIC,
|
||||
R.string.volume_icon_description_media, //FIXME should have its own description
|
||||
R.drawable.ic_media_route_on_holo_dark,
|
||||
R.drawable.ic_media_route_disabled_holo_dark,
|
||||
false);// will be dynamically updated
|
||||
|
||||
int streamType;
|
||||
int descRes;
|
||||
@@ -184,7 +190,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
||||
StreamResources.MediaStream,
|
||||
StreamResources.NotificationStream,
|
||||
StreamResources.AlarmStream,
|
||||
StreamResources.MasterStream
|
||||
StreamResources.MasterStream,
|
||||
StreamResources.RemoteStream
|
||||
};
|
||||
|
||||
/** Object that contains data for each slider */
|
||||
@@ -297,6 +304,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
||||
private boolean isMuted(int streamType) {
|
||||
if (streamType == STREAM_MASTER) {
|
||||
return mAudioManager.isMasterMute();
|
||||
} else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
|
||||
return (mAudioService.getRemoteStreamVolume() <= 0);
|
||||
} else {
|
||||
return mAudioManager.isStreamMute(streamType);
|
||||
}
|
||||
@@ -305,6 +314,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
||||
private int getStreamMaxVolume(int streamType) {
|
||||
if (streamType == STREAM_MASTER) {
|
||||
return mAudioManager.getMasterMaxVolume();
|
||||
} else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
|
||||
return mAudioService.getRemoteStreamMaxVolume();
|
||||
} else {
|
||||
return mAudioManager.getStreamMaxVolume(streamType);
|
||||
}
|
||||
@@ -313,6 +324,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
||||
private int getStreamVolume(int streamType) {
|
||||
if (streamType == STREAM_MASTER) {
|
||||
return mAudioManager.getMasterVolume();
|
||||
} else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
|
||||
return mAudioService.getRemoteStreamVolume();
|
||||
} else {
|
||||
return mAudioManager.getStreamVolume(streamType);
|
||||
}
|
||||
@@ -321,6 +334,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
||||
private void setStreamVolume(int streamType, int index, int flags) {
|
||||
if (streamType == STREAM_MASTER) {
|
||||
mAudioManager.setMasterVolume(index, flags);
|
||||
} else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
|
||||
mAudioService.setRemoteStreamVolume(index);
|
||||
} else {
|
||||
mAudioManager.setStreamVolume(streamType, index, flags);
|
||||
}
|
||||
@@ -398,7 +413,11 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
||||
mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
|
||||
sc.icon.setImageResource(R.drawable.ic_audio_ring_notif_vibrate);
|
||||
}
|
||||
if (sc.streamType != mAudioManager.getMasterStreamType() && muted) {
|
||||
if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
|
||||
// never disable touch interactions for remote playback, the muting is not tied to
|
||||
// the state of the phone.
|
||||
sc.seekbarView.setEnabled(true);
|
||||
} else if (sc.streamType != mAudioManager.getMasterStreamType() && muted) {
|
||||
sc.seekbarView.setEnabled(false);
|
||||
} else {
|
||||
sc.seekbarView.setEnabled(true);
|
||||
@@ -446,6 +465,40 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
||||
obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
|
||||
}
|
||||
|
||||
public void postRemoteVolumeChanged(int streamType, int flags) {
|
||||
if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
|
||||
synchronized (this) {
|
||||
if (mStreamControls == null) {
|
||||
createSliders();
|
||||
}
|
||||
}
|
||||
removeMessages(MSG_FREE_RESOURCES);
|
||||
obtainMessage(MSG_REMOTE_VOLUME_CHANGED, streamType, flags).sendToTarget();
|
||||
}
|
||||
|
||||
public void postRemoteSliderVisibility(boolean visible) {
|
||||
obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
|
||||
AudioService.STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by AudioService when it has received new remote playback information that
|
||||
* would affect the VolumePanel display (mainly volumes). The difference with
|
||||
* {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
|
||||
* (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
|
||||
* displayed.
|
||||
* This special code path is due to the fact that remote volume updates arrive to AudioService
|
||||
* asynchronously. So after AudioService has sent the volume update (which should be treated
|
||||
* as a request to update the volume), the application will likely set a new volume. If the UI
|
||||
* is still up, we need to refresh the display to show this new value.
|
||||
*/
|
||||
public void postHasNewRemotePlaybackInfo() {
|
||||
if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
|
||||
// don't create or prevent resources to be freed, if they disappear, this update came too
|
||||
// late and shouldn't warrant the panel to be displayed longer
|
||||
obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
|
||||
}
|
||||
|
||||
public void postMasterVolumeChanged(int flags) {
|
||||
postVolumeChanged(STREAM_MASTER, flags);
|
||||
}
|
||||
@@ -585,6 +638,11 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
||||
max++;
|
||||
break;
|
||||
}
|
||||
|
||||
case AudioService.STREAM_REMOTE_MUSIC: {
|
||||
if (LOGD) { Log.d(TAG, "showing remote volume "+index+" over "+ max); }
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
StreamControl sc = mStreamControls.get(streamType);
|
||||
@@ -593,7 +651,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
||||
sc.seekbarView.setMax(max);
|
||||
}
|
||||
sc.seekbarView.setProgress(index);
|
||||
if (streamType != mAudioManager.getMasterStreamType() && isMuted(streamType)) {
|
||||
if (streamType != mAudioManager.getMasterStreamType()
|
||||
&& streamType != AudioService.STREAM_REMOTE_MUSIC && isMuted(streamType)) {
|
||||
sc.seekbarView.setEnabled(false);
|
||||
} else {
|
||||
sc.seekbarView.setEnabled(true);
|
||||
@@ -601,7 +660,9 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
||||
}
|
||||
|
||||
if (!mDialog.isShowing()) {
|
||||
mAudioManager.forceVolumeControlStream(streamType);
|
||||
int stream = (streamType == AudioService.STREAM_REMOTE_MUSIC) ? -1 : streamType;
|
||||
// when the stream is for remote playback, use -1 to reset the stream type evaluation
|
||||
mAudioManager.forceVolumeControlStream(stream);
|
||||
mDialog.setContentView(mView);
|
||||
// Showing dialog - use collapsed state
|
||||
if (mShowCombinedVolumes) {
|
||||
@@ -611,7 +672,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
||||
}
|
||||
|
||||
// Do a little vibrate if applicable (only when going into vibrate mode)
|
||||
if ((flags & AudioManager.FLAG_VIBRATE) != 0 &&
|
||||
if ((streamType != AudioService.STREAM_REMOTE_MUSIC) &&
|
||||
((flags & AudioManager.FLAG_VIBRATE) != 0) &&
|
||||
mAudioService.isStreamAffectedByRingerMode(streamType) &&
|
||||
mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
|
||||
sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
|
||||
@@ -658,6 +720,72 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
||||
mVibrator.vibrate(VIBRATE_DURATION);
|
||||
}
|
||||
|
||||
protected void onRemoteVolumeChanged(int streamType, int flags) {
|
||||
// streamType is the real stream type being affected, but for the UI sliders, we
|
||||
// refer to AudioService.STREAM_REMOTE_MUSIC. We still play the beeps on the real
|
||||
// stream type.
|
||||
if (LOGD) Log.d(TAG, "onRemoteVolumeChanged(stream:"+streamType+", flags: " + flags + ")");
|
||||
|
||||
if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || mDialog.isShowing()) {
|
||||
synchronized (this) {
|
||||
if (mActiveStreamType != AudioService.STREAM_REMOTE_MUSIC) {
|
||||
reorderSliders(AudioService.STREAM_REMOTE_MUSIC);
|
||||
}
|
||||
onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, flags);
|
||||
}
|
||||
} else {
|
||||
if (LOGD) Log.d(TAG, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
|
||||
}
|
||||
|
||||
if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
|
||||
removeMessages(MSG_PLAY_SOUND);
|
||||
sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
|
||||
}
|
||||
|
||||
if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
|
||||
removeMessages(MSG_PLAY_SOUND);
|
||||
removeMessages(MSG_VIBRATE);
|
||||
onStopSounds();
|
||||
}
|
||||
|
||||
removeMessages(MSG_FREE_RESOURCES);
|
||||
sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
|
||||
|
||||
resetTimeout();
|
||||
}
|
||||
|
||||
protected void onRemoteVolumeUpdateIfShown() {
|
||||
if (LOGD) Log.d(TAG, "onRemoteVolumeUpdateIfShown()");
|
||||
if (mDialog.isShowing()
|
||||
&& (mActiveStreamType == AudioService.STREAM_REMOTE_MUSIC)
|
||||
&& (mStreamControls != null)) {
|
||||
onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handler for MSG_SLIDER_VISIBILITY_CHANGED
|
||||
* Hide or show a slider
|
||||
* @param streamType can be a valid stream type value, or VolumePanel.STREAM_MASTER,
|
||||
* or AudioService.STREAM_REMOTE_MUSIC
|
||||
* @param visible
|
||||
*/
|
||||
synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
|
||||
if (LOGD) Log.d(TAG, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
|
||||
boolean isVisible = (visible == 1);
|
||||
for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
|
||||
StreamResources streamRes = STREAMS[i];
|
||||
if (streamRes.streamType == streamType) {
|
||||
streamRes.show = isVisible;
|
||||
if (!isVisible && (mActiveStreamType == streamType)) {
|
||||
mActiveStreamType = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
|
||||
*/
|
||||
@@ -750,6 +878,19 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_REMOTE_VOLUME_CHANGED: {
|
||||
onRemoteVolumeChanged(msg.arg1, msg.arg2);
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
|
||||
onRemoteVolumeUpdateIfShown();
|
||||
break;
|
||||
|
||||
case MSG_SLIDER_VISIBILITY_CHANGED:
|
||||
onSliderVisibilityChanged(msg.arg1, msg.arg2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -779,6 +920,17 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
||||
}
|
||||
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
final Object tag = seekBar.getTag();
|
||||
if (tag instanceof StreamControl) {
|
||||
StreamControl sc = (StreamControl) tag;
|
||||
// because remote volume updates are asynchronous, AudioService might have received
|
||||
// a new remote volume value since the finger adjusted the slider. So when the
|
||||
// progress of the slider isn't being tracked anymore, adjust the slider to the last
|
||||
// "published" remote volume value, so the UI reflects the actual volume.
|
||||
if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
|
||||
seekBar.setProgress(getStreamVolume(AudioService.STREAM_REMOTE_MUSIC));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onClick(View v) {
|
||||
|
||||
@@ -1024,6 +1024,8 @@
|
||||
<java-symbol type="drawable" name="notification_template_icon_bg" />
|
||||
<java-symbol type="drawable" name="notification_template_icon_low_bg" />
|
||||
<java-symbol type="drawable" name="ic_lockscreen_unlock_phantom" />
|
||||
<java-symbol type="drawable" name="ic_media_route_on_holo_dark" />
|
||||
<java-symbol type="drawable" name="ic_media_route_disabled_holo_dark" />
|
||||
|
||||
<java-symbol type="layout" name="action_bar_home" />
|
||||
<java-symbol type="layout" name="action_bar_title_item" />
|
||||
|
||||
@@ -98,7 +98,10 @@ public class AudioManager {
|
||||
|
||||
/**
|
||||
* @hide Broadcast intent when the volume for a particular stream type changes.
|
||||
* Includes the stream, the new volume and previous volumes
|
||||
* Includes the stream, the new volume and previous volumes.
|
||||
* Notes:
|
||||
* - for internal platform use only, do not make public,
|
||||
* - never used for "remote" volume changes
|
||||
*
|
||||
* @see #EXTRA_VOLUME_STREAM_TYPE
|
||||
* @see #EXTRA_VOLUME_STREAM_VALUE
|
||||
@@ -1498,6 +1501,24 @@ public class AudioManager {
|
||||
return AudioSystem.isStreamActive(STREAM_MUSIC, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* If the stream is active locally or remotely, adjust its volume according to the enforced
|
||||
* priority rules.
|
||||
* Note: only AudioManager.STREAM_MUSIC is supported at the moment
|
||||
*/
|
||||
public void adjustLocalOrRemoteStreamVolume(int streamType, int direction) {
|
||||
if (streamType != STREAM_MUSIC) {
|
||||
Log.w(TAG, "adjustLocalOrRemoteStreamVolume() doesn't support stream " + streamType);
|
||||
}
|
||||
IAudioService service = getService();
|
||||
try {
|
||||
service.adjustLocalOrRemoteStreamVolume(streamType, direction);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in adjustLocalOrRemoteStreamVolume", e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets a generic audio configuration parameter. The use of these parameters
|
||||
* are platform dependant, see libaudio
|
||||
@@ -2074,10 +2095,12 @@ public class AudioManager {
|
||||
}
|
||||
IAudioService service = getService();
|
||||
try {
|
||||
service.registerRemoteControlClient(rcClient.getRcMediaIntent(), /* mediaIntent */
|
||||
rcClient.getIRemoteControlClient(), /* rcClient */
|
||||
int rcseId = service.registerRemoteControlClient(
|
||||
rcClient.getRcMediaIntent(), /* mediaIntent */
|
||||
rcClient.getIRemoteControlClient(),/* rcClient */
|
||||
// used to match media button event receiver and audio focus
|
||||
mContext.getPackageName()); /* packageName */
|
||||
mContext.getPackageName()); /* packageName */
|
||||
rcClient.setRcseId(rcseId);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in registerRemoteControlClient"+e);
|
||||
}
|
||||
|
||||
@@ -101,6 +101,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
||||
|
||||
/** Debug remote control client/display feature */
|
||||
protected static final boolean DEBUG_RC = false;
|
||||
/** Debug volumes */
|
||||
protected static final boolean DEBUG_VOL = false;
|
||||
|
||||
/** How long to delay before persisting a change in volume/ringer mode. */
|
||||
private static final int PERSIST_DELAY = 500;
|
||||
@@ -120,7 +122,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
||||
/** If the msg is already queued, queue this one and leave the old. */
|
||||
private static final int SENDMSG_QUEUE = 2;
|
||||
|
||||
// AudioHandler message.whats
|
||||
// AudioHandler messages
|
||||
private static final int MSG_SET_DEVICE_VOLUME = 0;
|
||||
private static final int MSG_PERSIST_VOLUME = 1;
|
||||
private static final int MSG_PERSIST_MASTER_VOLUME = 2;
|
||||
@@ -138,11 +140,13 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
||||
private static final int MSG_SET_ALL_VOLUMES = 14;
|
||||
private static final int MSG_PERSIST_MASTER_VOLUME_MUTE = 15;
|
||||
private static final int MSG_REPORT_NEW_ROUTES = 16;
|
||||
// messages handled under wakelock, can only be queued, i.e. sent with queueMsgUnderWakeLock(),
|
||||
private static final int MSG_REEVALUATE_REMOTE = 17;
|
||||
// start of messages handled under wakelock
|
||||
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
|
||||
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
|
||||
private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 17;
|
||||
private static final int MSG_SET_A2DP_CONNECTION_STATE = 18;
|
||||
|
||||
private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 18;
|
||||
private static final int MSG_SET_A2DP_CONNECTION_STATE = 19;
|
||||
// end of messages handled under wakelock
|
||||
|
||||
// flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be
|
||||
// persisted
|
||||
@@ -405,6 +409,11 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
||||
final RemoteCallbackList<IAudioRoutesObserver> mRoutesObservers
|
||||
= new RemoteCallbackList<IAudioRoutesObserver>();
|
||||
|
||||
/**
|
||||
* A fake stream type to match the notion of remote media playback
|
||||
*/
|
||||
public final static int STREAM_REMOTE_MUSIC = -200;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Construction
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
@@ -488,6 +497,11 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
||||
mMasterVolumeRamp = context.getResources().getIntArray(
|
||||
com.android.internal.R.array.config_masterVolumeRamp);
|
||||
|
||||
mMainRemote = new RemotePlaybackState(-1, MAX_STREAM_VOLUME[AudioManager.STREAM_MUSIC],
|
||||
MAX_STREAM_VOLUME[AudioManager.STREAM_MUSIC]);
|
||||
mHasRemotePlayback = false;
|
||||
mMainRemoteIsActive = false;
|
||||
postReevaluateRemote();
|
||||
}
|
||||
|
||||
private void createAudioSystemThread() {
|
||||
@@ -657,9 +671,20 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
||||
adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags);
|
||||
}
|
||||
|
||||
/** @see AudioManager#adjustLocalOrRemoteStreamVolume(int, int) with current assumption
|
||||
* on streamType: fixed to STREAM_MUSIC */
|
||||
public void adjustLocalOrRemoteStreamVolume(int streamType, int direction) {
|
||||
if (DEBUG_VOL) Log.d(TAG, "adjustLocalOrRemoteStreamVolume(dir="+direction+")");
|
||||
if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
|
||||
adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, 0);
|
||||
} else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
|
||||
adjustStreamVolume(AudioSystem.STREAM_MUSIC, direction, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/** @see AudioManager#adjustVolume(int, int, int) */
|
||||
public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
|
||||
|
||||
if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream="+suggestedStreamType);
|
||||
int streamType;
|
||||
if (mVolumeControlStream != -1) {
|
||||
streamType = mVolumeControlStream;
|
||||
@@ -668,17 +693,27 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
||||
}
|
||||
|
||||
// Play sounds on STREAM_RING only and if lock screen is not on.
|
||||
if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
|
||||
if ((streamType != STREAM_REMOTE_MUSIC) &&
|
||||
(flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
|
||||
((mStreamVolumeAlias[streamType] != AudioSystem.STREAM_RING)
|
||||
|| (mKeyguardManager != null && mKeyguardManager.isKeyguardLocked()))) {
|
||||
flags &= ~AudioManager.FLAG_PLAY_SOUND;
|
||||
}
|
||||
|
||||
adjustStreamVolume(streamType, direction, flags);
|
||||
if (streamType == STREAM_REMOTE_MUSIC) {
|
||||
// don't play sounds for remote
|
||||
flags &= ~AudioManager.FLAG_PLAY_SOUND;
|
||||
//if (DEBUG_VOL) Log.i(TAG, "Need to adjust remote volume: calling adjustRemoteVolume()");
|
||||
adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, flags);
|
||||
} else {
|
||||
adjustStreamVolume(streamType, direction, flags);
|
||||
}
|
||||
}
|
||||
|
||||
/** @see AudioManager#adjustStreamVolume(int, int, int) */
|
||||
public void adjustStreamVolume(int streamType, int direction, int flags) {
|
||||
if (DEBUG_VOL) Log.d(TAG, "adjustStreamVolume() stream="+streamType+", dir="+direction);
|
||||
|
||||
ensureValidDirection(direction);
|
||||
ensureValidStreamType(streamType);
|
||||
|
||||
@@ -1370,6 +1405,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
||||
}
|
||||
}
|
||||
int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
|
||||
if (streamType == STREAM_REMOTE_MUSIC) {
|
||||
// here handle remote media playback the same way as local playback
|
||||
streamType = AudioManager.STREAM_MUSIC;
|
||||
}
|
||||
int device = getDeviceForStream(streamType);
|
||||
int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device, false);
|
||||
setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true, false);
|
||||
@@ -2169,40 +2208,61 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
||||
// Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
|
||||
return AudioSystem.STREAM_VOICE_CALL;
|
||||
}
|
||||
} else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
|
||||
// Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC...");
|
||||
return AudioSystem.STREAM_MUSIC;
|
||||
} else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
|
||||
// Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING..."
|
||||
// + " b/c USE_DEFAULT_STREAM_TYPE...");
|
||||
return AudioSystem.STREAM_RING;
|
||||
// Having the suggested stream be USE_DEFAULT_STREAM_TYPE is how remote control
|
||||
// volume can have priority over STREAM_MUSIC
|
||||
if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
|
||||
if (DEBUG_VOL)
|
||||
Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
|
||||
return STREAM_REMOTE_MUSIC;
|
||||
} else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
|
||||
if (DEBUG_VOL)
|
||||
Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
|
||||
return AudioSystem.STREAM_MUSIC;
|
||||
} else {
|
||||
if (DEBUG_VOL)
|
||||
Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING b/c default");
|
||||
return AudioSystem.STREAM_RING;
|
||||
}
|
||||
} else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
|
||||
if (DEBUG_VOL)
|
||||
Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
|
||||
return AudioSystem.STREAM_MUSIC;
|
||||
} else {
|
||||
// Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
|
||||
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Returning suggested type "
|
||||
+ suggestedStreamType);
|
||||
return suggestedStreamType;
|
||||
}
|
||||
} else {
|
||||
if (isInCommunication()) {
|
||||
if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
|
||||
== AudioSystem.FORCE_BT_SCO) {
|
||||
// Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
|
||||
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO");
|
||||
return AudioSystem.STREAM_BLUETOOTH_SCO;
|
||||
} else {
|
||||
// Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
|
||||
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL");
|
||||
return AudioSystem.STREAM_VOICE_CALL;
|
||||
}
|
||||
} else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION,
|
||||
NOTIFICATION_VOLUME_DELAY_MS) ||
|
||||
AudioSystem.isStreamActive(AudioSystem.STREAM_RING,
|
||||
NOTIFICATION_VOLUME_DELAY_MS) ||
|
||||
AudioSystem.isStreamActive(AudioSystem.STREAM_RING,
|
||||
NOTIFICATION_VOLUME_DELAY_MS)) {
|
||||
// Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION...");
|
||||
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
|
||||
return AudioSystem.STREAM_NOTIFICATION;
|
||||
} else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) ||
|
||||
(suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE)) {
|
||||
// Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC "
|
||||
// + " b/c USE_DEFAULT_STREAM_TYPE...");
|
||||
return AudioSystem.STREAM_MUSIC;
|
||||
} else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
|
||||
if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
|
||||
// Having the suggested stream be USE_DEFAULT_STREAM_TYPE is how remote control
|
||||
// volume can have priority over STREAM_MUSIC
|
||||
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
|
||||
return STREAM_REMOTE_MUSIC;
|
||||
} else {
|
||||
if (DEBUG_VOL)
|
||||
Log.v(TAG, "getActiveStreamType: using STREAM_MUSIC as default");
|
||||
return AudioSystem.STREAM_MUSIC;
|
||||
}
|
||||
} else {
|
||||
// Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
|
||||
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Returning suggested type "
|
||||
+ suggestedStreamType);
|
||||
return suggestedStreamType;
|
||||
}
|
||||
}
|
||||
@@ -3036,6 +3096,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
||||
mRoutesObservers.finishBroadcast();
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_REEVALUATE_REMOTE:
|
||||
onReevaluateRemote();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4103,6 +4167,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
||||
// remote control client died, make sure the displays don't use it anymore
|
||||
// by setting its remote control client to null
|
||||
registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/);
|
||||
// the dead client was maybe handling remote playback, reevaluate
|
||||
postReevaluateRemote();
|
||||
}
|
||||
|
||||
public IBinder getBinder() {
|
||||
@@ -4110,7 +4176,46 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A global counter for RemoteControlClient identifiers
|
||||
*/
|
||||
private static int sLastRccId = 0;
|
||||
|
||||
private class RemotePlaybackState {
|
||||
int mRccId;
|
||||
int mVolume;
|
||||
int mVolumeMax;
|
||||
int mVolumeHandling;
|
||||
|
||||
private RemotePlaybackState(int id, int vol, int volMax) {
|
||||
mRccId = id;
|
||||
mVolume = vol;
|
||||
mVolumeMax = volMax;
|
||||
mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal cache for the playback information of the RemoteControlClient whose volume gets to
|
||||
* be controlled by the volume keys ("main"), so we don't have to iterate over the RC stack
|
||||
* every time we need this info.
|
||||
*/
|
||||
private RemotePlaybackState mMainRemote;
|
||||
/**
|
||||
* Indicates whether the "main" RemoteControlClient is considered active.
|
||||
* Use synchronized on mMainRemote.
|
||||
*/
|
||||
private boolean mMainRemoteIsActive;
|
||||
/**
|
||||
* Indicates whether there is remote playback going on. True even if there is no "active"
|
||||
* remote playback (mMainRemoteIsActive is false), but a RemoteControlClient has declared it
|
||||
* handles remote playback.
|
||||
* Use synchronized on mMainRemote.
|
||||
*/
|
||||
private boolean mHasRemotePlayback;
|
||||
|
||||
private static class RemoteControlStackEntry {
|
||||
public int mRccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
|
||||
/**
|
||||
* The target for the ACTION_MEDIA_BUTTON events.
|
||||
* Always non null.
|
||||
@@ -4129,6 +4234,24 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
||||
* but no remote control client has been registered) */
|
||||
public IRemoteControlClient mRcClient;
|
||||
public RcClientDeathHandler mRcClientDeathHandler;
|
||||
/**
|
||||
* Information only used for non-local playback
|
||||
*/
|
||||
public int mPlaybackType;
|
||||
public int mPlaybackVolume;
|
||||
public int mPlaybackVolumeMax;
|
||||
public int mPlaybackVolumeHandling;
|
||||
public int mPlaybackStream;
|
||||
public int mPlaybackState;
|
||||
|
||||
public void resetPlaybackInfo() {
|
||||
mPlaybackType = RemoteControlClient.PLAYBACK_TYPE_LOCAL;
|
||||
mPlaybackVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
|
||||
mPlaybackVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
|
||||
mPlaybackVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
|
||||
mPlaybackStream = AudioManager.STREAM_MUSIC;
|
||||
mPlaybackState = RemoteControlClient.PLAYSTATE_STOPPED;
|
||||
}
|
||||
|
||||
/** precondition: mediaIntent != null, eventReceiver != null */
|
||||
public RemoteControlStackEntry(PendingIntent mediaIntent, ComponentName eventReceiver) {
|
||||
@@ -4136,6 +4259,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
||||
mReceiverComponent = eventReceiver;
|
||||
mCallingUid = -1;
|
||||
mRcClient = null;
|
||||
mRccId = ++sLastRccId;
|
||||
|
||||
resetPlaybackInfo();
|
||||
}
|
||||
|
||||
public void unlinkToRcClientDeath() {
|
||||
@@ -4185,11 +4311,46 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
||||
pw.println(" pi: " + rcse.mMediaIntent +
|
||||
" -- ercvr: " + rcse.mReceiverComponent +
|
||||
" -- client: " + rcse.mRcClient +
|
||||
" -- uid: " + rcse.mCallingUid);
|
||||
" -- uid: " + rcse.mCallingUid +
|
||||
" -- type: " + rcse.mPlaybackType +
|
||||
" state: " + rcse.mPlaybackState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function:
|
||||
* Display in the log the current entries in the remote control stack, focusing
|
||||
* on RemoteControlClient data
|
||||
*/
|
||||
private void dumpRCCStack(PrintWriter pw) {
|
||||
pw.println("\nRemote Control Client stack entries:");
|
||||
synchronized(mRCStack) {
|
||||
Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
|
||||
while(stackIterator.hasNext()) {
|
||||
RemoteControlStackEntry rcse = stackIterator.next();
|
||||
pw.println(" uid: " + rcse.mCallingUid +
|
||||
" -- id: " + rcse.mRccId +
|
||||
" -- type: " + rcse.mPlaybackType +
|
||||
" -- state: " + rcse.mPlaybackState +
|
||||
" -- vol handling: " + rcse.mPlaybackVolumeHandling +
|
||||
" -- vol: " + rcse.mPlaybackVolume +
|
||||
" -- volMax: " + rcse.mPlaybackVolumeMax);
|
||||
}
|
||||
}
|
||||
synchronized (mMainRemote) {
|
||||
pw.println("\nRemote Volume State:");
|
||||
pw.println(" has remote: " + mHasRemotePlayback);
|
||||
pw.println(" is remote active: " + mMainRemoteIsActive);
|
||||
pw.println(" rccId: " + mMainRemote.mRccId);
|
||||
pw.println(" volume handling: "
|
||||
+ ((mMainRemote.mVolumeHandling == RemoteControlClient.PLAYBACK_VOLUME_FIXED) ?
|
||||
"PLAYBACK_VOLUME_FIXED(0)" : "PLAYBACK_VOLUME_VARIABLE(1)"));
|
||||
pw.println(" volume: " + mMainRemote.mVolume);
|
||||
pw.println(" volume steps: " + mMainRemote.mVolumeMax);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function:
|
||||
* Remove any entry in the remote control stack that has the same package name as packageName
|
||||
@@ -4556,13 +4717,15 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
||||
|
||||
/**
|
||||
* see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...)
|
||||
* @return the unique ID of the RemoteControlStackEntry associated with the RemoteControlClient
|
||||
* Note: using this method with rcClient == null is a way to "disable" the IRemoteControlClient
|
||||
* without modifying the RC stack, but while still causing the display to refresh (will
|
||||
* become blank as a result of this)
|
||||
*/
|
||||
public void registerRemoteControlClient(PendingIntent mediaIntent,
|
||||
public int registerRemoteControlClient(PendingIntent mediaIntent,
|
||||
IRemoteControlClient rcClient, String callingPackageName) {
|
||||
if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
|
||||
int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
|
||||
synchronized(mAudioFocusLock) {
|
||||
synchronized(mRCStack) {
|
||||
// store the new display information
|
||||
@@ -4581,8 +4744,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
||||
rcse.mCallingUid = Binder.getCallingUid();
|
||||
if (rcClient == null) {
|
||||
// here rcse.mRcClientDeathHandler is null;
|
||||
rcse.resetPlaybackInfo();
|
||||
break;
|
||||
}
|
||||
rccId = rcse.mRccId;
|
||||
|
||||
// there is a new (non-null) client:
|
||||
// 1/ give the new client the current display (if any)
|
||||
@@ -4616,6 +4781,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
||||
}
|
||||
}
|
||||
}
|
||||
return rccId;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4790,6 +4956,248 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME send a message instead of updating the stack synchronously
|
||||
public void setPlaybackInfoForRcc(int rccId, int what, int value) {
|
||||
if(DEBUG_RC) Log.d(TAG, "setPlaybackInfoForRcc(id="+rccId+", what="+what+",val="+value+")");
|
||||
synchronized(mRCStack) {
|
||||
Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
|
||||
while(stackIterator.hasNext()) {
|
||||
RemoteControlStackEntry rcse = stackIterator.next();
|
||||
if (rcse.mRccId == rccId) {
|
||||
switch (what) {
|
||||
case RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE:
|
||||
rcse.mPlaybackType = value;
|
||||
postReevaluateRemote();
|
||||
break;
|
||||
case RemoteControlClient.PLAYBACKINFO_VOLUME:
|
||||
rcse.mPlaybackVolume = value;
|
||||
synchronized (mMainRemote) {
|
||||
if (rccId == mMainRemote.mRccId) {
|
||||
mMainRemote.mVolume = value;
|
||||
mVolumePanel.postHasNewRemotePlaybackInfo();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RemoteControlClient.PLAYBACKINFO_VOLUME_MAX:
|
||||
rcse.mPlaybackVolumeMax = value;
|
||||
synchronized (mMainRemote) {
|
||||
if (rccId == mMainRemote.mRccId) {
|
||||
mMainRemote.mVolumeMax = value;
|
||||
mVolumePanel.postHasNewRemotePlaybackInfo();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING:
|
||||
rcse.mPlaybackVolumeHandling = value;
|
||||
synchronized (mMainRemote) {
|
||||
if (rccId == mMainRemote.mRccId) {
|
||||
mMainRemote.mVolumeHandling = value;
|
||||
mVolumePanel.postHasNewRemotePlaybackInfo();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case RemoteControlClient.PLAYBACKINFO_USES_STREAM:
|
||||
rcse.mPlaybackStream = value;
|
||||
break;
|
||||
case RemoteControlClient.PLAYBACKINFO_PLAYSTATE:
|
||||
rcse.mPlaybackState = value;
|
||||
synchronized (mMainRemote) {
|
||||
if (rccId == mMainRemote.mRccId) {
|
||||
mMainRemoteIsActive = isPlaystateActive(value);
|
||||
postReevaluateRemote();
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "unhandled key " + what + " for RCC " + rccId);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a remote client is active on the supplied stream type. Update the remote stream
|
||||
* volume state if found and playing
|
||||
* @param streamType
|
||||
* @return false if no remote playing is currently playing
|
||||
*/
|
||||
private boolean checkUpdateRemoteStateIfActive(int streamType) {
|
||||
synchronized(mRCStack) {
|
||||
Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
|
||||
while(stackIterator.hasNext()) {
|
||||
RemoteControlStackEntry rcse = stackIterator.next();
|
||||
if ((rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE)
|
||||
&& isPlaystateActive(rcse.mPlaybackState)
|
||||
&& (rcse.mPlaybackStream == streamType)) {
|
||||
if (DEBUG_RC) Log.d(TAG, "remote playback active on stream " + streamType
|
||||
+ ", vol =" + rcse.mPlaybackVolume);
|
||||
synchronized (mMainRemote) {
|
||||
mMainRemote.mRccId = rcse.mRccId;
|
||||
mMainRemote.mVolume = rcse.mPlaybackVolume;
|
||||
mMainRemote.mVolumeMax = rcse.mPlaybackVolumeMax;
|
||||
mMainRemote.mVolumeHandling = rcse.mPlaybackVolumeHandling;
|
||||
mMainRemoteIsActive = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
synchronized (mMainRemote) {
|
||||
mMainRemoteIsActive = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given playback state is considered "active", i.e. it describes a state
|
||||
* where playback is happening, or about to
|
||||
* @param playState the playback state to evaluate
|
||||
* @return true if active, false otherwise (inactive or unknown)
|
||||
*/
|
||||
private static boolean isPlaystateActive(int playState) {
|
||||
switch (playState) {
|
||||
case RemoteControlClient.PLAYSTATE_PLAYING:
|
||||
case RemoteControlClient.PLAYSTATE_BUFFERING:
|
||||
case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
|
||||
case RemoteControlClient.PLAYSTATE_REWINDING:
|
||||
case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
|
||||
case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void adjustRemoteVolume(int streamType, int direction, int flags) {
|
||||
int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
|
||||
boolean volFixed = false;
|
||||
synchronized (mMainRemote) {
|
||||
if (!mMainRemoteIsActive) {
|
||||
if (DEBUG_VOL) Log.w(TAG, "adjustRemoteVolume didn't find an active client");
|
||||
return;
|
||||
}
|
||||
rccId = mMainRemote.mRccId;
|
||||
volFixed = (mMainRemote.mVolumeHandling ==
|
||||
RemoteControlClient.PLAYBACK_VOLUME_FIXED);
|
||||
}
|
||||
// unlike "local" stream volumes, we can't compute the new volume based on the direction,
|
||||
// we can only notify the remote that volume needs to be updated, and we'll get an async'
|
||||
// update through setPlaybackInfoForRcc()
|
||||
if (!volFixed) {
|
||||
sendVolumeUpdateToRemote(rccId, direction);
|
||||
}
|
||||
|
||||
// fire up the UI
|
||||
mVolumePanel.postRemoteVolumeChanged(streamType, flags);
|
||||
}
|
||||
|
||||
private void sendVolumeUpdateToRemote(int rccId, int direction) {
|
||||
if (DEBUG_VOL) { Log.d(TAG, "sendVolumeUpdateToRemote(rccId="+rccId+" , dir="+direction); }
|
||||
if (direction == 0) {
|
||||
// only handling discrete events
|
||||
return;
|
||||
}
|
||||
String packageForRcc = null;
|
||||
synchronized (mRCStack) {
|
||||
Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
|
||||
while(stackIterator.hasNext()) {
|
||||
RemoteControlStackEntry rcse = stackIterator.next();
|
||||
//FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
|
||||
if (rcse.mRccId == rccId) {
|
||||
packageForRcc = rcse.mReceiverComponent.getPackageName();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (packageForRcc != null) {
|
||||
Intent intent = new Intent(Intent.ACTION_VOLUME_UPDATE);
|
||||
intent.putExtra(Intent.EXTRA_VOLUME_UPDATE_DIRECTION, direction);
|
||||
intent.setPackage(packageForRcc);
|
||||
mContext.sendBroadcast(intent);
|
||||
}
|
||||
}
|
||||
|
||||
public int getRemoteStreamMaxVolume() {
|
||||
synchronized (mMainRemote) {
|
||||
if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
|
||||
return 0;
|
||||
}
|
||||
return mMainRemote.mVolumeMax;
|
||||
}
|
||||
}
|
||||
|
||||
public int getRemoteStreamVolume() {
|
||||
synchronized (mMainRemote) {
|
||||
if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
|
||||
return 0;
|
||||
}
|
||||
return mMainRemote.mVolume;
|
||||
}
|
||||
}
|
||||
|
||||
public void setRemoteStreamVolume(int vol) {
|
||||
if (DEBUG_VOL) { Log.d(TAG, "setRemoteStreamVolume(vol="+vol+")"); }
|
||||
int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
|
||||
synchronized (mMainRemote) {
|
||||
if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
|
||||
return;
|
||||
}
|
||||
rccId = mMainRemote.mRccId;
|
||||
}
|
||||
String packageForRcc = null;
|
||||
synchronized (mRCStack) {
|
||||
Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
|
||||
while(stackIterator.hasNext()) {
|
||||
RemoteControlStackEntry rcse = stackIterator.next();
|
||||
if (rcse.mRccId == rccId) {
|
||||
//FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
|
||||
packageForRcc = rcse.mReceiverComponent.getPackageName();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (packageForRcc != null) {
|
||||
Intent intent = new Intent(Intent.ACTION_VOLUME_UPDATE);
|
||||
intent.putExtra(Intent.EXTRA_VOLUME_UPDATE_VALUE, vol);
|
||||
intent.setPackage(packageForRcc);
|
||||
mContext.sendBroadcast(intent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call to make AudioService reevaluate whether it's in a mode where remote players should
|
||||
* have their volume controlled. In this implementation this is only to reset whether
|
||||
* VolumePanel should display remote volumes
|
||||
*/
|
||||
private void postReevaluateRemote() {
|
||||
sendMsg(mAudioHandler, MSG_REEVALUATE_REMOTE, SENDMSG_QUEUE, 0, 0, null, 0);
|
||||
}
|
||||
|
||||
private void onReevaluateRemote() {
|
||||
if (DEBUG_VOL) { Log.w(TAG, "onReevaluateRemote()"); }
|
||||
// is there a registered RemoteControlClient that is handling remote playback
|
||||
boolean hasRemotePlayback = false;
|
||||
synchronized (mRCStack) {
|
||||
Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
|
||||
while(stackIterator.hasNext()) {
|
||||
RemoteControlStackEntry rcse = stackIterator.next();
|
||||
if (rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
|
||||
hasRemotePlayback = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
synchronized (mMainRemote) {
|
||||
if (mHasRemotePlayback != hasRemotePlayback) {
|
||||
mHasRemotePlayback = hasRemotePlayback;
|
||||
mVolumePanel.postRemoteSliderVisibility(hasRemotePlayback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================================
|
||||
// Device orientation
|
||||
//==========================================================================================
|
||||
@@ -4871,9 +5279,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
||||
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
|
||||
|
||||
// TODO probably a lot more to do here than just the audio focus and remote control stacks
|
||||
dumpFocusStack(pw);
|
||||
dumpRCStack(pw);
|
||||
dumpRCCStack(pw);
|
||||
dumpStreamStates(pw);
|
||||
pw.println("\nAudio routes:");
|
||||
pw.print(" mMainType=0x"); pw.println(Integer.toHexString(mCurAudioRoutes.mMainType));
|
||||
|
||||
@@ -35,6 +35,8 @@ interface IAudioService {
|
||||
|
||||
void adjustVolume(int direction, int flags);
|
||||
|
||||
oneway void adjustLocalOrRemoteStreamVolume(int streamType, int direction);
|
||||
|
||||
void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags);
|
||||
|
||||
void adjustStreamVolume(int streamType, int direction, int flags);
|
||||
@@ -43,6 +45,8 @@ interface IAudioService {
|
||||
|
||||
void setStreamVolume(int streamType, int index, int flags);
|
||||
|
||||
oneway void setRemoteStreamVolume(int index);
|
||||
|
||||
void setMasterVolume(int index, int flags);
|
||||
|
||||
void setStreamSolo(int streamType, boolean state, IBinder cb);
|
||||
@@ -119,7 +123,7 @@ interface IAudioService {
|
||||
oneway void registerMediaButtonEventReceiverForCalls(in ComponentName c);
|
||||
oneway void unregisterMediaButtonEventReceiverForCalls();
|
||||
|
||||
oneway void registerRemoteControlClient(in PendingIntent mediaIntent,
|
||||
int registerRemoteControlClient(in PendingIntent mediaIntent,
|
||||
in IRemoteControlClient rcClient, in String callingPackageName);
|
||||
oneway void unregisterRemoteControlClient(in PendingIntent mediaIntent,
|
||||
in IRemoteControlClient rcClient);
|
||||
@@ -128,6 +132,10 @@ interface IAudioService {
|
||||
oneway void unregisterRemoteControlDisplay(in IRemoteControlDisplay rcd);
|
||||
oneway void remoteControlDisplayUsesBitmapSize(in IRemoteControlDisplay rcd, int w, int h);
|
||||
|
||||
oneway void setPlaybackInfoForRcc(int rccId, int what, int value);
|
||||
int getRemoteStreamMaxVolume();
|
||||
int getRemoteStreamVolume();
|
||||
|
||||
void startBluetoothSco(IBinder cb);
|
||||
void stopBluetoothSco(IBinder cb);
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.media;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
@@ -26,9 +27,11 @@ import android.graphics.RectF;
|
||||
import android.media.MediaMetadataRetriever;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -130,6 +133,88 @@ public class RemoteControlClient
|
||||
*/
|
||||
public final static int PLAYSTATE_NONE = 0;
|
||||
|
||||
/**
|
||||
* @hide (to be un-hidden)
|
||||
* The default playback type, "local", indicating the presentation of the media is happening on
|
||||
* the same device (e.g. a phone, a tablet) as where it is controlled from.
|
||||
*/
|
||||
public final static int PLAYBACK_TYPE_LOCAL = 0;
|
||||
/**
|
||||
* @hide (to be un-hidden)
|
||||
* A playback type indicating the presentation of the media is happening on
|
||||
* a different device (i.e. the remote device) than where it is controlled from.
|
||||
*/
|
||||
public final static int PLAYBACK_TYPE_REMOTE = 1;
|
||||
private final static int PLAYBACK_TYPE_MIN = PLAYBACK_TYPE_LOCAL;
|
||||
private final static int PLAYBACK_TYPE_MAX = PLAYBACK_TYPE_REMOTE;
|
||||
/**
|
||||
* @hide (to be un-hidden)
|
||||
* Playback information indicating the playback volume is fixed, i.e. it cannot be controlled
|
||||
* from this object. An example of fixed playback volume is a remote player, playing over HDMI
|
||||
* where the user prefer to control the volume on the HDMI sink, rather than attenuate at the
|
||||
* source.
|
||||
* @see #PLAYBACKINFO_VOLUME_HANDLING.
|
||||
*/
|
||||
public final static int PLAYBACK_VOLUME_FIXED = 0;
|
||||
/**
|
||||
* @hide (to be un-hidden)
|
||||
* Playback information indicating the playback volume is variable and can be controlled from
|
||||
* this object.
|
||||
* @see #PLAYBACKINFO_VOLUME_HANDLING.
|
||||
*/
|
||||
public final static int PLAYBACK_VOLUME_VARIABLE = 1;
|
||||
/**
|
||||
* @hide (to be un-hidden)
|
||||
* The playback information value indicating the value of a given information type is invalid.
|
||||
* @see #PLAYBACKINFO_VOLUME_HANDLING.
|
||||
*/
|
||||
public final static int PLAYBACKINFO_INVALID_VALUE = Integer.MIN_VALUE;
|
||||
|
||||
//==========================================
|
||||
// Public keys for playback information
|
||||
/**
|
||||
* @hide (to be un-hidden)
|
||||
* Playback information that defines the type of playback associated with this
|
||||
* RemoteControlClient. See {@link #PLAYBACK_TYPE_LOCAL} and {@link #PLAYBACK_TYPE_REMOTE}.
|
||||
*/
|
||||
public final static int PLAYBACKINFO_PLAYBACK_TYPE = 1;
|
||||
/**
|
||||
* @hide (to be un-hidden)
|
||||
* Playback information that defines at what volume the playback associated with this
|
||||
* RemoteControlClient is performed. This information is only used when the playback type is not
|
||||
* local (see {@link #PLAYBACKINFO_PLAYBACK_TYPE}).
|
||||
*/
|
||||
public final static int PLAYBACKINFO_VOLUME = 2;
|
||||
/**
|
||||
* @hide (to be un-hidden)
|
||||
* Playback information that defines the maximum volume volume value that is supported
|
||||
* by the playback associated with this RemoteControlClient. This information is only used
|
||||
* when the playback type is not local (see {@link #PLAYBACKINFO_PLAYBACK_TYPE}).
|
||||
*/
|
||||
public final static int PLAYBACKINFO_VOLUME_MAX = 3;
|
||||
/**
|
||||
* @hide (to be un-hidden)
|
||||
* Playback information that defines how volume is handled for the presentation of the media.
|
||||
* @see #PLAYBACK_VOLUME_FIXED
|
||||
* @see #PLAYBACK_VOLUME_VARIABLE
|
||||
*/
|
||||
public final static int PLAYBACKINFO_VOLUME_HANDLING = 4;
|
||||
/**
|
||||
* @hide (to be un-hidden)
|
||||
* Playback information that defines over what stream type the media is presented.
|
||||
*/
|
||||
public final static int PLAYBACKINFO_USES_STREAM = 5;
|
||||
|
||||
//==========================================
|
||||
// Private keys for playback information
|
||||
/**
|
||||
* @hide
|
||||
* Used internally to relay playback state (set by the application with
|
||||
* {@link #setPlaybackState(int)}) to AudioService
|
||||
*/
|
||||
public final static int PLAYBACKINFO_PLAYSTATE = 255;
|
||||
|
||||
|
||||
/**
|
||||
* Flag indicating a RemoteControlClient makes use of the "previous" media key.
|
||||
*
|
||||
@@ -516,6 +601,8 @@ public class RemoteControlClient
|
||||
|
||||
// send to remote control display if conditions are met
|
||||
sendPlaybackState_syncCacheLock();
|
||||
// update AudioService
|
||||
sendAudioServiceNewPlaybackInfo_syncCacheLock(PLAYBACKINFO_PLAYSTATE, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -542,6 +629,122 @@ public class RemoteControlClient
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public final static int DEFAULT_PLAYBACK_VOLUME_HANDLING = PLAYBACK_VOLUME_VARIABLE;
|
||||
/** @hide */
|
||||
// hard-coded to the same number of steps as AudioService.MAX_STREAM_VOLUME[STREAM_MUSIC]
|
||||
public final static int DEFAULT_PLAYBACK_VOLUME = 15;
|
||||
|
||||
private int mPlaybackType = PLAYBACK_TYPE_LOCAL;
|
||||
private int mPlaybackVolumeMax = DEFAULT_PLAYBACK_VOLUME;
|
||||
private int mPlaybackVolume = DEFAULT_PLAYBACK_VOLUME;
|
||||
private int mPlaybackVolumeHandling = DEFAULT_PLAYBACK_VOLUME_HANDLING;
|
||||
private int mPlaybackStream = AudioManager.STREAM_MUSIC;
|
||||
|
||||
/**
|
||||
* @hide (to be un-hidden)
|
||||
* Set information describing information related to the playback of media so the system
|
||||
* can implement additional behavior to handle non-local playback usecases.
|
||||
* @param what a key to specify the type of information to set. Valid keys are
|
||||
* {@link #PLAYBACKINFO_PLAYBACK_TYPE},
|
||||
* {@link #PLAYBACKINFO_USES_STREAM},
|
||||
* {@link #PLAYBACKINFO_VOLUME},
|
||||
* {@link #PLAYBACKINFO_VOLUME_MAX},
|
||||
* and {@link #PLAYBACKINFO_VOLUME_HANDLING}.
|
||||
* @param value the value for the supplied information to set.
|
||||
*/
|
||||
public void setPlaybackInformation(int what, int value) {
|
||||
synchronized(mCacheLock) {
|
||||
switch (what) {
|
||||
case PLAYBACKINFO_PLAYBACK_TYPE:
|
||||
if ((value >= PLAYBACK_TYPE_MIN) && (value <= PLAYBACK_TYPE_MAX)) {
|
||||
if (mPlaybackType != value) {
|
||||
mPlaybackType = value;
|
||||
sendAudioServiceNewPlaybackInfo_syncCacheLock(what, value);
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "using invalid value for PLAYBACKINFO_PLAYBACK_TYPE");
|
||||
}
|
||||
break;
|
||||
case PLAYBACKINFO_VOLUME:
|
||||
if ((value > -1) && (value <= mPlaybackVolumeMax)) {
|
||||
if (mPlaybackVolume != value) {
|
||||
mPlaybackVolume = value;
|
||||
sendAudioServiceNewPlaybackInfo_syncCacheLock(what, value);
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "using invalid value for PLAYBACKINFO_VOLUME");
|
||||
}
|
||||
break;
|
||||
case PLAYBACKINFO_VOLUME_MAX:
|
||||
if (value > 0) {
|
||||
if (mPlaybackVolumeMax != value) {
|
||||
mPlaybackVolumeMax = value;
|
||||
sendAudioServiceNewPlaybackInfo_syncCacheLock(what, value);
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "using invalid value for PLAYBACKINFO_VOLUME_MAX");
|
||||
}
|
||||
break;
|
||||
case PLAYBACKINFO_USES_STREAM:
|
||||
if ((value >= 0) && (value < AudioSystem.getNumStreamTypes())) {
|
||||
mPlaybackStream = value;
|
||||
} else {
|
||||
Log.w(TAG, "using invalid value for PLAYBACKINFO_USES_STREAM");
|
||||
}
|
||||
break;
|
||||
case PLAYBACKINFO_VOLUME_HANDLING:
|
||||
if ((value >= PLAYBACK_VOLUME_FIXED) && (value <= PLAYBACK_VOLUME_VARIABLE)) {
|
||||
if (mPlaybackVolumeHandling != value) {
|
||||
mPlaybackVolumeHandling = value;
|
||||
sendAudioServiceNewPlaybackInfo_syncCacheLock(what, value);
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "using invalid value for PLAYBACKINFO_VOLUME_HANDLING");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// not throwing an exception or returning an error if more keys are to be
|
||||
// supported in the future
|
||||
Log.w(TAG, "setPlaybackInformation() ignoring unknown key " + what);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide (to be un-hidden)
|
||||
* Return playback information represented as an integer value.
|
||||
* @param what a key to specify the type of information to retrieve. Valid keys are
|
||||
* {@link #PLAYBACKINFO_PLAYBACK_TYPE},
|
||||
* {@link #PLAYBACKINFO_USES_STREAM},
|
||||
* {@link #PLAYBACKINFO_VOLUME},
|
||||
* {@link #PLAYBACKINFO_VOLUME_MAX},
|
||||
* and {@link #PLAYBACKINFO_VOLUME_HANDLING}.
|
||||
* @return the current value for the given information type, or
|
||||
* {@link #PLAYBACKINFO_INVALID_VALUE} if an error occurred or the request is invalid, or
|
||||
* the value is unknown.
|
||||
*/
|
||||
public int getIntPlaybackInformation(int what) {
|
||||
synchronized(mCacheLock) {
|
||||
switch (what) {
|
||||
case PLAYBACKINFO_PLAYBACK_TYPE:
|
||||
return mPlaybackType;
|
||||
case PLAYBACKINFO_VOLUME:
|
||||
return mPlaybackVolume;
|
||||
case PLAYBACKINFO_VOLUME_MAX:
|
||||
return mPlaybackVolumeMax;
|
||||
case PLAYBACKINFO_USES_STREAM:
|
||||
return mPlaybackStream;
|
||||
case PLAYBACKINFO_VOLUME_HANDLING:
|
||||
return mPlaybackVolumeHandling;
|
||||
default:
|
||||
Log.e(TAG, "getIntPlaybackInformation() unknown key " + what);
|
||||
return PLAYBACKINFO_INVALID_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock for all cached data
|
||||
*/
|
||||
@@ -675,6 +878,27 @@ public class RemoteControlClient
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Default value for the unique identifier
|
||||
*/
|
||||
public final static int RCSE_ID_UNREGISTERED = -1;
|
||||
/**
|
||||
* Unique identifier of the RemoteControlStackEntry in AudioService with which
|
||||
* this RemoteControlClient is associated.
|
||||
*/
|
||||
private int mRcseId = RCSE_ID_UNREGISTERED;
|
||||
/**
|
||||
* @hide
|
||||
* To be only used by AudioManager after it has received the unique id from
|
||||
* IAudioService.registerRemoteControlClient()
|
||||
* @param id the unique identifier of the RemoteControlStackEntry in AudioService with which
|
||||
* this RemoteControlClient is associated.
|
||||
*/
|
||||
public void setRcseId(int id) {
|
||||
mRcseId = id;
|
||||
}
|
||||
|
||||
private EventHandler mEventHandler;
|
||||
private final static int MSG_REQUEST_PLAYBACK_STATE = 1;
|
||||
private final static int MSG_REQUEST_METADATA = 2;
|
||||
@@ -731,6 +955,9 @@ public class RemoteControlClient
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
// Communication with IRemoteControlDisplay
|
||||
|
||||
private void detachFromDisplay_syncCacheLock() {
|
||||
mRcDisplay = null;
|
||||
mArtworkExpectedWidth = ARTWORK_INVALID_SIZE;
|
||||
@@ -802,6 +1029,37 @@ public class RemoteControlClient
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
// Communication with AudioService
|
||||
|
||||
private static IAudioService sService;
|
||||
|
||||
private static IAudioService getService()
|
||||
{
|
||||
if (sService != null) {
|
||||
return sService;
|
||||
}
|
||||
IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
|
||||
sService = IAudioService.Stub.asInterface(b);
|
||||
return sService;
|
||||
}
|
||||
|
||||
private void sendAudioServiceNewPlaybackInfo_syncCacheLock(int what, int value) {
|
||||
if (mRcseId == RCSE_ID_UNREGISTERED) {
|
||||
return;
|
||||
}
|
||||
Log.d(TAG, "sending to AudioService key=" + what + ", value=" + value);
|
||||
IAudioService service = getService();
|
||||
try {
|
||||
service.setPlaybackInfoForRcc(mRcseId, what, value);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Dead object in sendAudioServiceNewPlaybackInfo_syncCacheLock", e);
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
// Message handlers
|
||||
|
||||
private void onNewInternalClientGen(Integer clientGeneration, int artWidth, int artHeight) {
|
||||
synchronized (mCacheLock) {
|
||||
// this remote control client is told it is the "focused" one:
|
||||
@@ -836,6 +1094,9 @@ public class RemoteControlClient
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
// Internal utilities
|
||||
|
||||
/**
|
||||
* Scale a bitmap to fit the smallest dimension by uniformly scaling the incoming bitmap.
|
||||
* If the bitmap fits, then do nothing and return the original.
|
||||
|
||||
@@ -213,16 +213,13 @@ public abstract class KeyguardViewBase extends FrameLayout {
|
||||
Context.AUDIO_SERVICE);
|
||||
}
|
||||
}
|
||||
// Volume buttons should only function for music.
|
||||
if (mAudioManager.isMusicActive()) {
|
||||
// TODO: Actually handle MUTE.
|
||||
mAudioManager.adjustStreamVolume(
|
||||
AudioManager.STREAM_MUSIC,
|
||||
keyCode == KeyEvent.KEYCODE_VOLUME_UP
|
||||
? AudioManager.ADJUST_RAISE
|
||||
: AudioManager.ADJUST_LOWER,
|
||||
0);
|
||||
}
|
||||
// Volume buttons should only function for music (local or remote).
|
||||
// TODO: Actually handle MUTE.
|
||||
mAudioManager.adjustLocalOrRemoteStreamVolume(
|
||||
AudioManager.STREAM_MUSIC,
|
||||
keyCode == KeyEvent.KEYCODE_VOLUME_UP
|
||||
? AudioManager.ADJUST_RAISE
|
||||
: AudioManager.ADJUST_LOWER);
|
||||
// Don't execute default volume behavior
|
||||
return true;
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user