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.Resources;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
|
import android.media.RemoteControlClient;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
@@ -2151,6 +2152,19 @@ public class Intent implements Parcelable, Cloneable {
|
|||||||
public static final String ACTION_USB_AUDIO_DEVICE_PLUG =
|
public static final String ACTION_USB_AUDIO_DEVICE_PLUG =
|
||||||
"android.intent.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>
|
* <p>Broadcast Action: The user has switched on advanced settings in the settings app:</p>
|
||||||
* <ul>
|
* <ul>
|
||||||
@@ -2839,6 +2853,27 @@ public class Intent implements Parcelable, Cloneable {
|
|||||||
*/
|
*/
|
||||||
public static final String EXTRA_USERID =
|
public static final String EXTRA_USERID =
|
||||||
"android.intent.extra.user_id";
|
"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).
|
// Intent flags (see mFlags variable).
|
||||||
|
|||||||
@@ -34,10 +34,7 @@ import android.media.ToneGenerator;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.os.Vibrator;
|
import android.os.Vibrator;
|
||||||
import android.provider.Settings;
|
|
||||||
import android.provider.Settings.System;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.WindowManager.LayoutParams;
|
import android.view.WindowManager.LayoutParams;
|
||||||
import android.widget.ImageView;
|
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_TIMEOUT = 5;
|
||||||
private static final int MSG_RINGER_MODE_CHANGED = 6;
|
private static final int MSG_RINGER_MODE_CHANGED = 6;
|
||||||
private static final int MSG_MUTE_CHANGED = 7;
|
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
|
// Pseudo stream type for master volume
|
||||||
private static final int STREAM_MASTER = -100;
|
private static final int STREAM_MASTER = -100;
|
||||||
|
// Pseudo stream type for remote volume is defined in AudioService.STREAM_REMOTE_MUSIC
|
||||||
|
|
||||||
protected Context mContext;
|
protected Context mContext;
|
||||||
private AudioManager mAudioManager;
|
private AudioManager mAudioManager;
|
||||||
@@ -155,10 +156,15 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
|||||||
true),
|
true),
|
||||||
// for now, use media resources for master volume
|
// for now, use media resources for master volume
|
||||||
MasterStream(STREAM_MASTER,
|
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,
|
||||||
R.drawable.ic_audio_vol_mute,
|
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 streamType;
|
||||||
int descRes;
|
int descRes;
|
||||||
@@ -184,7 +190,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
|||||||
StreamResources.MediaStream,
|
StreamResources.MediaStream,
|
||||||
StreamResources.NotificationStream,
|
StreamResources.NotificationStream,
|
||||||
StreamResources.AlarmStream,
|
StreamResources.AlarmStream,
|
||||||
StreamResources.MasterStream
|
StreamResources.MasterStream,
|
||||||
|
StreamResources.RemoteStream
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Object that contains data for each slider */
|
/** Object that contains data for each slider */
|
||||||
@@ -297,6 +304,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
|||||||
private boolean isMuted(int streamType) {
|
private boolean isMuted(int streamType) {
|
||||||
if (streamType == STREAM_MASTER) {
|
if (streamType == STREAM_MASTER) {
|
||||||
return mAudioManager.isMasterMute();
|
return mAudioManager.isMasterMute();
|
||||||
|
} else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
|
||||||
|
return (mAudioService.getRemoteStreamVolume() <= 0);
|
||||||
} else {
|
} else {
|
||||||
return mAudioManager.isStreamMute(streamType);
|
return mAudioManager.isStreamMute(streamType);
|
||||||
}
|
}
|
||||||
@@ -305,6 +314,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
|||||||
private int getStreamMaxVolume(int streamType) {
|
private int getStreamMaxVolume(int streamType) {
|
||||||
if (streamType == STREAM_MASTER) {
|
if (streamType == STREAM_MASTER) {
|
||||||
return mAudioManager.getMasterMaxVolume();
|
return mAudioManager.getMasterMaxVolume();
|
||||||
|
} else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
|
||||||
|
return mAudioService.getRemoteStreamMaxVolume();
|
||||||
} else {
|
} else {
|
||||||
return mAudioManager.getStreamMaxVolume(streamType);
|
return mAudioManager.getStreamMaxVolume(streamType);
|
||||||
}
|
}
|
||||||
@@ -313,6 +324,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
|||||||
private int getStreamVolume(int streamType) {
|
private int getStreamVolume(int streamType) {
|
||||||
if (streamType == STREAM_MASTER) {
|
if (streamType == STREAM_MASTER) {
|
||||||
return mAudioManager.getMasterVolume();
|
return mAudioManager.getMasterVolume();
|
||||||
|
} else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
|
||||||
|
return mAudioService.getRemoteStreamVolume();
|
||||||
} else {
|
} else {
|
||||||
return mAudioManager.getStreamVolume(streamType);
|
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) {
|
private void setStreamVolume(int streamType, int index, int flags) {
|
||||||
if (streamType == STREAM_MASTER) {
|
if (streamType == STREAM_MASTER) {
|
||||||
mAudioManager.setMasterVolume(index, flags);
|
mAudioManager.setMasterVolume(index, flags);
|
||||||
|
} else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
|
||||||
|
mAudioService.setRemoteStreamVolume(index);
|
||||||
} else {
|
} else {
|
||||||
mAudioManager.setStreamVolume(streamType, index, flags);
|
mAudioManager.setStreamVolume(streamType, index, flags);
|
||||||
}
|
}
|
||||||
@@ -398,7 +413,11 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
|||||||
mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
|
mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
|
||||||
sc.icon.setImageResource(R.drawable.ic_audio_ring_notif_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);
|
sc.seekbarView.setEnabled(false);
|
||||||
} else {
|
} else {
|
||||||
sc.seekbarView.setEnabled(true);
|
sc.seekbarView.setEnabled(true);
|
||||||
@@ -446,6 +465,40 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
|||||||
obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
|
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) {
|
public void postMasterVolumeChanged(int flags) {
|
||||||
postVolumeChanged(STREAM_MASTER, flags);
|
postVolumeChanged(STREAM_MASTER, flags);
|
||||||
}
|
}
|
||||||
@@ -585,6 +638,11 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
|||||||
max++;
|
max++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case AudioService.STREAM_REMOTE_MUSIC: {
|
||||||
|
if (LOGD) { Log.d(TAG, "showing remote volume "+index+" over "+ max); }
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamControl sc = mStreamControls.get(streamType);
|
StreamControl sc = mStreamControls.get(streamType);
|
||||||
@@ -593,7 +651,8 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
|||||||
sc.seekbarView.setMax(max);
|
sc.seekbarView.setMax(max);
|
||||||
}
|
}
|
||||||
sc.seekbarView.setProgress(index);
|
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);
|
sc.seekbarView.setEnabled(false);
|
||||||
} else {
|
} else {
|
||||||
sc.seekbarView.setEnabled(true);
|
sc.seekbarView.setEnabled(true);
|
||||||
@@ -601,7 +660,9 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!mDialog.isShowing()) {
|
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);
|
mDialog.setContentView(mView);
|
||||||
// Showing dialog - use collapsed state
|
// Showing dialog - use collapsed state
|
||||||
if (mShowCombinedVolumes) {
|
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)
|
// 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) &&
|
mAudioService.isStreamAffectedByRingerMode(streamType) &&
|
||||||
mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
|
mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
|
||||||
sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
|
sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
|
||||||
@@ -658,6 +720,72 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie
|
|||||||
mVibrator.vibrate(VIBRATE_DURATION);
|
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.
|
* 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;
|
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) {
|
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) {
|
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_bg" />
|
||||||
<java-symbol type="drawable" name="notification_template_icon_low_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_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_home" />
|
||||||
<java-symbol type="layout" name="action_bar_title_item" />
|
<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.
|
* @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_TYPE
|
||||||
* @see #EXTRA_VOLUME_STREAM_VALUE
|
* @see #EXTRA_VOLUME_STREAM_VALUE
|
||||||
@@ -1498,6 +1501,24 @@ public class AudioManager {
|
|||||||
return AudioSystem.isStreamActive(STREAM_MUSIC, 0);
|
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
|
* Sets a generic audio configuration parameter. The use of these parameters
|
||||||
* are platform dependant, see libaudio
|
* are platform dependant, see libaudio
|
||||||
@@ -2074,10 +2095,12 @@ public class AudioManager {
|
|||||||
}
|
}
|
||||||
IAudioService service = getService();
|
IAudioService service = getService();
|
||||||
try {
|
try {
|
||||||
service.registerRemoteControlClient(rcClient.getRcMediaIntent(), /* mediaIntent */
|
int rcseId = service.registerRemoteControlClient(
|
||||||
|
rcClient.getRcMediaIntent(), /* mediaIntent */
|
||||||
rcClient.getIRemoteControlClient(),/* rcClient */
|
rcClient.getIRemoteControlClient(),/* rcClient */
|
||||||
// used to match media button event receiver and audio focus
|
// used to match media button event receiver and audio focus
|
||||||
mContext.getPackageName()); /* packageName */
|
mContext.getPackageName()); /* packageName */
|
||||||
|
rcClient.setRcseId(rcseId);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(TAG, "Dead object in registerRemoteControlClient"+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 */
|
/** Debug remote control client/display feature */
|
||||||
protected static final boolean DEBUG_RC = false;
|
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. */
|
/** How long to delay before persisting a change in volume/ringer mode. */
|
||||||
private static final int PERSIST_DELAY = 500;
|
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. */
|
/** If the msg is already queued, queue this one and leave the old. */
|
||||||
private static final int SENDMSG_QUEUE = 2;
|
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_SET_DEVICE_VOLUME = 0;
|
||||||
private static final int MSG_PERSIST_VOLUME = 1;
|
private static final int MSG_PERSIST_VOLUME = 1;
|
||||||
private static final int MSG_PERSIST_MASTER_VOLUME = 2;
|
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_SET_ALL_VOLUMES = 14;
|
||||||
private static final int MSG_PERSIST_MASTER_VOLUME_MUTE = 15;
|
private static final int MSG_PERSIST_MASTER_VOLUME_MUTE = 15;
|
||||||
private static final int MSG_REPORT_NEW_ROUTES = 16;
|
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, ...)
|
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
|
||||||
private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 17;
|
private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 18;
|
||||||
private static final int MSG_SET_A2DP_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
|
// flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be
|
||||||
// persisted
|
// persisted
|
||||||
@@ -405,6 +409,11 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
|||||||
final RemoteCallbackList<IAudioRoutesObserver> mRoutesObservers
|
final RemoteCallbackList<IAudioRoutesObserver> mRoutesObservers
|
||||||
= new RemoteCallbackList<IAudioRoutesObserver>();
|
= new RemoteCallbackList<IAudioRoutesObserver>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A fake stream type to match the notion of remote media playback
|
||||||
|
*/
|
||||||
|
public final static int STREAM_REMOTE_MUSIC = -200;
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// Construction
|
// Construction
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
@@ -488,6 +497,11 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
|||||||
mMasterVolumeRamp = context.getResources().getIntArray(
|
mMasterVolumeRamp = context.getResources().getIntArray(
|
||||||
com.android.internal.R.array.config_masterVolumeRamp);
|
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() {
|
private void createAudioSystemThread() {
|
||||||
@@ -657,9 +671,20 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
|||||||
adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags);
|
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) */
|
/** @see AudioManager#adjustVolume(int, int, int) */
|
||||||
public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
|
public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
|
||||||
|
if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream="+suggestedStreamType);
|
||||||
int streamType;
|
int streamType;
|
||||||
if (mVolumeControlStream != -1) {
|
if (mVolumeControlStream != -1) {
|
||||||
streamType = mVolumeControlStream;
|
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.
|
// 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)
|
((mStreamVolumeAlias[streamType] != AudioSystem.STREAM_RING)
|
||||||
|| (mKeyguardManager != null && mKeyguardManager.isKeyguardLocked()))) {
|
|| (mKeyguardManager != null && mKeyguardManager.isKeyguardLocked()))) {
|
||||||
flags &= ~AudioManager.FLAG_PLAY_SOUND;
|
flags &= ~AudioManager.FLAG_PLAY_SOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
adjustStreamVolume(streamType, direction, flags);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @see AudioManager#adjustStreamVolume(int, int, int) */
|
/** @see AudioManager#adjustStreamVolume(int, int, int) */
|
||||||
public void adjustStreamVolume(int streamType, int direction, int flags) {
|
public void adjustStreamVolume(int streamType, int direction, int flags) {
|
||||||
|
if (DEBUG_VOL) Log.d(TAG, "adjustStreamVolume() stream="+streamType+", dir="+direction);
|
||||||
|
|
||||||
ensureValidDirection(direction);
|
ensureValidDirection(direction);
|
||||||
ensureValidStreamType(streamType);
|
ensureValidStreamType(streamType);
|
||||||
|
|
||||||
@@ -1370,6 +1405,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
|
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 device = getDeviceForStream(streamType);
|
||||||
int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device, false);
|
int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device, false);
|
||||||
setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true, 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...");
|
// Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
|
||||||
return AudioSystem.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) {
|
} else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
|
||||||
// Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING..."
|
// Having the suggested stream be USE_DEFAULT_STREAM_TYPE is how remote control
|
||||||
// + " b/c USE_DEFAULT_STREAM_TYPE...");
|
// volume can have priority over STREAM_MUSIC
|
||||||
return AudioSystem.STREAM_RING;
|
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 {
|
} else {
|
||||||
// Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
|
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 {
|
||||||
|
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Returning suggested type "
|
||||||
|
+ suggestedStreamType);
|
||||||
return suggestedStreamType;
|
return suggestedStreamType;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isInCommunication()) {
|
if (isInCommunication()) {
|
||||||
if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
|
if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
|
||||||
== AudioSystem.FORCE_BT_SCO) {
|
== 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;
|
return AudioSystem.STREAM_BLUETOOTH_SCO;
|
||||||
} else {
|
} 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;
|
return AudioSystem.STREAM_VOICE_CALL;
|
||||||
}
|
}
|
||||||
} else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION,
|
} else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION,
|
||||||
NOTIFICATION_VOLUME_DELAY_MS) ||
|
NOTIFICATION_VOLUME_DELAY_MS) ||
|
||||||
AudioSystem.isStreamActive(AudioSystem.STREAM_RING,
|
AudioSystem.isStreamActive(AudioSystem.STREAM_RING,
|
||||||
NOTIFICATION_VOLUME_DELAY_MS)) {
|
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;
|
return AudioSystem.STREAM_NOTIFICATION;
|
||||||
} else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) ||
|
} else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
|
||||||
(suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE)) {
|
if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
|
||||||
// Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC "
|
// Having the suggested stream be USE_DEFAULT_STREAM_TYPE is how remote control
|
||||||
// + " b/c USE_DEFAULT_STREAM_TYPE...");
|
// volume can have priority over STREAM_MUSIC
|
||||||
return AudioSystem.STREAM_MUSIC;
|
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
|
||||||
|
return STREAM_REMOTE_MUSIC;
|
||||||
} else {
|
} else {
|
||||||
// Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
|
if (DEBUG_VOL)
|
||||||
|
Log.v(TAG, "getActiveStreamType: using STREAM_MUSIC as default");
|
||||||
|
return AudioSystem.STREAM_MUSIC;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Returning suggested type "
|
||||||
|
+ suggestedStreamType);
|
||||||
return suggestedStreamType;
|
return suggestedStreamType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3036,6 +3096,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
|||||||
mRoutesObservers.finishBroadcast();
|
mRoutesObservers.finishBroadcast();
|
||||||
break;
|
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
|
// remote control client died, make sure the displays don't use it anymore
|
||||||
// by setting its remote control client to null
|
// by setting its remote control client to null
|
||||||
registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/);
|
registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/);
|
||||||
|
// the dead client was maybe handling remote playback, reevaluate
|
||||||
|
postReevaluateRemote();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IBinder getBinder() {
|
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 {
|
private static class RemoteControlStackEntry {
|
||||||
|
public int mRccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
|
||||||
/**
|
/**
|
||||||
* The target for the ACTION_MEDIA_BUTTON events.
|
* The target for the ACTION_MEDIA_BUTTON events.
|
||||||
* Always non null.
|
* Always non null.
|
||||||
@@ -4129,6 +4234,24 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
|||||||
* but no remote control client has been registered) */
|
* but no remote control client has been registered) */
|
||||||
public IRemoteControlClient mRcClient;
|
public IRemoteControlClient mRcClient;
|
||||||
public RcClientDeathHandler mRcClientDeathHandler;
|
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 */
|
/** precondition: mediaIntent != null, eventReceiver != null */
|
||||||
public RemoteControlStackEntry(PendingIntent mediaIntent, ComponentName eventReceiver) {
|
public RemoteControlStackEntry(PendingIntent mediaIntent, ComponentName eventReceiver) {
|
||||||
@@ -4136,6 +4259,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
|||||||
mReceiverComponent = eventReceiver;
|
mReceiverComponent = eventReceiver;
|
||||||
mCallingUid = -1;
|
mCallingUid = -1;
|
||||||
mRcClient = null;
|
mRcClient = null;
|
||||||
|
mRccId = ++sLastRccId;
|
||||||
|
|
||||||
|
resetPlaybackInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unlinkToRcClientDeath() {
|
public void unlinkToRcClientDeath() {
|
||||||
@@ -4185,11 +4311,46 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
|||||||
pw.println(" pi: " + rcse.mMediaIntent +
|
pw.println(" pi: " + rcse.mMediaIntent +
|
||||||
" -- ercvr: " + rcse.mReceiverComponent +
|
" -- ercvr: " + rcse.mReceiverComponent +
|
||||||
" -- client: " + rcse.mRcClient +
|
" -- 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:
|
* Helper function:
|
||||||
* Remove any entry in the remote control stack that has the same package name as packageName
|
* 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, ...)
|
* 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
|
* 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
|
* without modifying the RC stack, but while still causing the display to refresh (will
|
||||||
* become blank as a result of this)
|
* become blank as a result of this)
|
||||||
*/
|
*/
|
||||||
public void registerRemoteControlClient(PendingIntent mediaIntent,
|
public int registerRemoteControlClient(PendingIntent mediaIntent,
|
||||||
IRemoteControlClient rcClient, String callingPackageName) {
|
IRemoteControlClient rcClient, String callingPackageName) {
|
||||||
if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
|
if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
|
||||||
|
int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
|
||||||
synchronized(mAudioFocusLock) {
|
synchronized(mAudioFocusLock) {
|
||||||
synchronized(mRCStack) {
|
synchronized(mRCStack) {
|
||||||
// store the new display information
|
// store the new display information
|
||||||
@@ -4581,8 +4744,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
|||||||
rcse.mCallingUid = Binder.getCallingUid();
|
rcse.mCallingUid = Binder.getCallingUid();
|
||||||
if (rcClient == null) {
|
if (rcClient == null) {
|
||||||
// here rcse.mRcClientDeathHandler is null;
|
// here rcse.mRcClientDeathHandler is null;
|
||||||
|
rcse.resetPlaybackInfo();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
rccId = rcse.mRccId;
|
||||||
|
|
||||||
// there is a new (non-null) client:
|
// there is a new (non-null) client:
|
||||||
// 1/ give the new client the current display (if any)
|
// 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
|
// Device orientation
|
||||||
//==========================================================================================
|
//==========================================================================================
|
||||||
@@ -4871,9 +5279,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
|
|||||||
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||||
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
|
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);
|
dumpFocusStack(pw);
|
||||||
dumpRCStack(pw);
|
dumpRCStack(pw);
|
||||||
|
dumpRCCStack(pw);
|
||||||
dumpStreamStates(pw);
|
dumpStreamStates(pw);
|
||||||
pw.println("\nAudio routes:");
|
pw.println("\nAudio routes:");
|
||||||
pw.print(" mMainType=0x"); pw.println(Integer.toHexString(mCurAudioRoutes.mMainType));
|
pw.print(" mMainType=0x"); pw.println(Integer.toHexString(mCurAudioRoutes.mMainType));
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ interface IAudioService {
|
|||||||
|
|
||||||
void adjustVolume(int direction, int flags);
|
void adjustVolume(int direction, int flags);
|
||||||
|
|
||||||
|
oneway void adjustLocalOrRemoteStreamVolume(int streamType, int direction);
|
||||||
|
|
||||||
void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags);
|
void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags);
|
||||||
|
|
||||||
void adjustStreamVolume(int streamType, int direction, int flags);
|
void adjustStreamVolume(int streamType, int direction, int flags);
|
||||||
@@ -43,6 +45,8 @@ interface IAudioService {
|
|||||||
|
|
||||||
void setStreamVolume(int streamType, int index, int flags);
|
void setStreamVolume(int streamType, int index, int flags);
|
||||||
|
|
||||||
|
oneway void setRemoteStreamVolume(int index);
|
||||||
|
|
||||||
void setMasterVolume(int index, int flags);
|
void setMasterVolume(int index, int flags);
|
||||||
|
|
||||||
void setStreamSolo(int streamType, boolean state, IBinder cb);
|
void setStreamSolo(int streamType, boolean state, IBinder cb);
|
||||||
@@ -119,7 +123,7 @@ interface IAudioService {
|
|||||||
oneway void registerMediaButtonEventReceiverForCalls(in ComponentName c);
|
oneway void registerMediaButtonEventReceiverForCalls(in ComponentName c);
|
||||||
oneway void unregisterMediaButtonEventReceiverForCalls();
|
oneway void unregisterMediaButtonEventReceiverForCalls();
|
||||||
|
|
||||||
oneway void registerRemoteControlClient(in PendingIntent mediaIntent,
|
int registerRemoteControlClient(in PendingIntent mediaIntent,
|
||||||
in IRemoteControlClient rcClient, in String callingPackageName);
|
in IRemoteControlClient rcClient, in String callingPackageName);
|
||||||
oneway void unregisterRemoteControlClient(in PendingIntent mediaIntent,
|
oneway void unregisterRemoteControlClient(in PendingIntent mediaIntent,
|
||||||
in IRemoteControlClient rcClient);
|
in IRemoteControlClient rcClient);
|
||||||
@@ -128,6 +132,10 @@ interface IAudioService {
|
|||||||
oneway void unregisterRemoteControlDisplay(in IRemoteControlDisplay rcd);
|
oneway void unregisterRemoteControlDisplay(in IRemoteControlDisplay rcd);
|
||||||
oneway void remoteControlDisplayUsesBitmapSize(in IRemoteControlDisplay rcd, int w, int h);
|
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 startBluetoothSco(IBinder cb);
|
||||||
void stopBluetoothSco(IBinder cb);
|
void stopBluetoothSco(IBinder cb);
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package android.media;
|
|||||||
|
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
@@ -26,9 +27,11 @@ import android.graphics.RectF;
|
|||||||
import android.media.MediaMetadataRetriever;
|
import android.media.MediaMetadataRetriever;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
import android.os.ServiceManager;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@@ -130,6 +133,88 @@ public class RemoteControlClient
|
|||||||
*/
|
*/
|
||||||
public final static int PLAYSTATE_NONE = 0;
|
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.
|
* 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
|
// send to remote control display if conditions are met
|
||||||
sendPlaybackState_syncCacheLock();
|
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
|
* 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 EventHandler mEventHandler;
|
||||||
private final static int MSG_REQUEST_PLAYBACK_STATE = 1;
|
private final static int MSG_REQUEST_PLAYBACK_STATE = 1;
|
||||||
private final static int MSG_REQUEST_METADATA = 2;
|
private final static int MSG_REQUEST_METADATA = 2;
|
||||||
@@ -731,6 +955,9 @@ public class RemoteControlClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//===========================================================
|
||||||
|
// Communication with IRemoteControlDisplay
|
||||||
|
|
||||||
private void detachFromDisplay_syncCacheLock() {
|
private void detachFromDisplay_syncCacheLock() {
|
||||||
mRcDisplay = null;
|
mRcDisplay = null;
|
||||||
mArtworkExpectedWidth = ARTWORK_INVALID_SIZE;
|
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) {
|
private void onNewInternalClientGen(Integer clientGeneration, int artWidth, int artHeight) {
|
||||||
synchronized (mCacheLock) {
|
synchronized (mCacheLock) {
|
||||||
// this remote control client is told it is the "focused" one:
|
// 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.
|
* 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.
|
* If the bitmap fits, then do nothing and return the original.
|
||||||
|
|||||||
@@ -213,16 +213,13 @@ public abstract class KeyguardViewBase extends FrameLayout {
|
|||||||
Context.AUDIO_SERVICE);
|
Context.AUDIO_SERVICE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Volume buttons should only function for music.
|
// Volume buttons should only function for music (local or remote).
|
||||||
if (mAudioManager.isMusicActive()) {
|
|
||||||
// TODO: Actually handle MUTE.
|
// TODO: Actually handle MUTE.
|
||||||
mAudioManager.adjustStreamVolume(
|
mAudioManager.adjustLocalOrRemoteStreamVolume(
|
||||||
AudioManager.STREAM_MUSIC,
|
AudioManager.STREAM_MUSIC,
|
||||||
keyCode == KeyEvent.KEYCODE_VOLUME_UP
|
keyCode == KeyEvent.KEYCODE_VOLUME_UP
|
||||||
? AudioManager.ADJUST_RAISE
|
? AudioManager.ADJUST_RAISE
|
||||||
: AudioManager.ADJUST_LOWER,
|
: AudioManager.ADJUST_LOWER);
|
||||||
0);
|
|
||||||
}
|
|
||||||
// Don't execute default volume behavior
|
// Don't execute default volume behavior
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user