summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2013-03-22 23:13:49 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2013-03-22 23:13:49 +0000
commit1a89c5324badd10dac142a5a0c40a203503db65f (patch)
tree269f901041482278e146a8447a41437939ff3471
parent5341f377ccdd71471894d23afaab4d7a75fd03ba (diff)
parent961cae92540763226648813d111c5b5c3b0f1597 (diff)
downloadframeworks_base-1a89c5324badd10dac142a5a0c40a203503db65f.zip
frameworks_base-1a89c5324badd10dac142a5a0c40a203503db65f.tar.gz
frameworks_base-1a89c5324badd10dac142a5a0c40a203503db65f.tar.bz2
Merge "New media button API." into jb-mr2-dev
-rw-r--r--api/current.txt15
-rw-r--r--core/java/android/view/ViewRootImpl.java3
-rw-r--r--core/java/android/view/ViewTreeObserver.java153
-rw-r--r--media/java/android/media/AudioManager.java31
-rw-r--r--media/java/android/media/AudioService.java46
5 files changed, 226 insertions, 22 deletions
diff --git a/api/current.txt b/api/current.txt
index f4b4d53..8b77cc9 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10956,6 +10956,7 @@ package android.media {
method public void playSoundEffect(int);
method public void playSoundEffect(int, float);
method public void registerMediaButtonEventReceiver(android.content.ComponentName);
+ method public void registerMediaButtonEventReceiver(android.app.PendingIntent);
method public void registerRemoteControlClient(android.media.RemoteControlClient);
method public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int);
method public deprecated void setBluetoothA2dpOn(boolean);
@@ -10976,6 +10977,7 @@ package android.media {
method public void stopBluetoothSco();
method public void unloadSoundEffects();
method public void unregisterMediaButtonEventReceiver(android.content.ComponentName);
+ method public void unregisterMediaButtonEventReceiver(android.app.PendingIntent);
method public void unregisterRemoteControlClient(android.media.RemoteControlClient);
field public static final java.lang.String ACTION_AUDIO_BECOMING_NOISY = "android.media.AUDIO_BECOMING_NOISY";
field public static final deprecated java.lang.String ACTION_SCO_AUDIO_STATE_CHANGED = "android.media.SCO_AUDIO_STATE_CHANGED";
@@ -26132,6 +26134,8 @@ package android.view {
method public void addOnPreDrawListener(android.view.ViewTreeObserver.OnPreDrawListener);
method public void addOnScrollChangedListener(android.view.ViewTreeObserver.OnScrollChangedListener);
method public void addOnTouchModeChangeListener(android.view.ViewTreeObserver.OnTouchModeChangeListener);
+ method public void addOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener);
+ method public void addOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener);
method public final void dispatchOnDraw();
method public final void dispatchOnGlobalLayout();
method public final boolean dispatchOnPreDraw();
@@ -26143,6 +26147,8 @@ package android.view {
method public void removeOnPreDrawListener(android.view.ViewTreeObserver.OnPreDrawListener);
method public void removeOnScrollChangedListener(android.view.ViewTreeObserver.OnScrollChangedListener);
method public void removeOnTouchModeChangeListener(android.view.ViewTreeObserver.OnTouchModeChangeListener);
+ method public void removeOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener);
+ method public void removeOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener);
}
public static abstract interface ViewTreeObserver.OnDrawListener {
@@ -26169,6 +26175,15 @@ package android.view {
method public abstract void onTouchModeChanged(boolean);
}
+ public static abstract interface ViewTreeObserver.OnWindowAttachListener {
+ method public abstract void onWindowAttached();
+ method public abstract void onWindowDetached();
+ }
+
+ public static abstract interface ViewTreeObserver.OnWindowFocusChangeListener {
+ method public abstract void onWindowFocusChanged(boolean);
+ }
+
public abstract class Window {
ctor public Window(android.content.Context);
method public abstract void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 69e2d1a..3ce4c13 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1233,6 +1233,7 @@ public final class ViewRootImpl implements ViewParent,
host.setLayoutDirection(mLastConfiguration.getLayoutDirection());
}
host.dispatchAttachedToWindow(attachInfo, 0);
+ attachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets);
host.fitSystemWindows(mFitSystemWindowsInsets);
//Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
@@ -2827,6 +2828,7 @@ public final class ViewRootImpl implements ViewParent,
mAttachInfo.mHardwareRenderer.isEnabled()) {
mAttachInfo.mHardwareRenderer.validate();
}
+ mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
mView.dispatchDetachedFromWindow();
}
@@ -3127,6 +3129,7 @@ public final class ViewRootImpl implements ViewParent,
}
mAttachInfo.mKeyDispatchState.reset();
mView.dispatchWindowFocusChanged(hasWindowFocus);
+ mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
}
// Note: must be done after the focus change callbacks,
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index cfcf3c0..072c95f 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -33,6 +33,8 @@ import java.util.concurrent.CopyOnWriteArrayList;
*/
public final class ViewTreeObserver {
// Recursive listeners use CopyOnWriteArrayList
+ private CopyOnWriteArrayList<OnWindowFocusChangeListener> mOnWindowFocusListeners;
+ private CopyOnWriteArrayList<OnWindowAttachListener> mOnWindowAttachListeners;
private CopyOnWriteArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners;
private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners;
@@ -49,6 +51,36 @@ public final class ViewTreeObserver {
private boolean mAlive = true;
/**
+ * Interface definition for a callback to be invoked when the view hierarchy is
+ * attached to and detached from its window.
+ */
+ public interface OnWindowAttachListener {
+ /**
+ * Callback method to be invoked when the view hierarchy is attached to a window
+ */
+ public void onWindowAttached();
+
+ /**
+ * Callback method to be invoked when the view hierarchy is detached from a window
+ */
+ public void onWindowDetached();
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when the view hierarchy's window
+ * focus state changes.
+ */
+ public interface OnWindowFocusChangeListener {
+ /**
+ * Callback method to be invoked when the window focus changes in the view tree.
+ *
+ * @param hasFocus Set to true if the window is gaining focus, false if it is
+ * losing focus.
+ */
+ public void onWindowFocusChanged(boolean hasFocus);
+ }
+
+ /**
* Interface definition for a callback to be invoked when the focus state within
* the view tree changes.
*/
@@ -272,6 +304,22 @@ public final class ViewTreeObserver {
* @param observer The ViewTreeObserver whose listeners must be added to this observer
*/
void merge(ViewTreeObserver observer) {
+ if (observer.mOnWindowAttachListeners != null) {
+ if (mOnWindowAttachListeners != null) {
+ mOnWindowAttachListeners.addAll(observer.mOnWindowAttachListeners);
+ } else {
+ mOnWindowAttachListeners = observer.mOnWindowAttachListeners;
+ }
+ }
+
+ if (observer.mOnWindowFocusListeners != null) {
+ if (mOnWindowFocusListeners != null) {
+ mOnWindowFocusListeners.addAll(observer.mOnWindowFocusListeners);
+ } else {
+ mOnWindowFocusListeners = observer.mOnWindowFocusListeners;
+ }
+ }
+
if (observer.mOnGlobalFocusListeners != null) {
if (mOnGlobalFocusListeners != null) {
mOnGlobalFocusListeners.addAll(observer.mOnGlobalFocusListeners);
@@ -324,6 +372,76 @@ public final class ViewTreeObserver {
}
/**
+ * Register a callback to be invoked when the view hierarchy is attached to a window.
+ *
+ * @param listener The callback to add
+ *
+ * @throws IllegalStateException If {@link #isAlive()} returns false
+ */
+ public void addOnWindowAttachListener(OnWindowAttachListener listener) {
+ checkIsAlive();
+
+ if (mOnWindowAttachListeners == null) {
+ mOnWindowAttachListeners
+ = new CopyOnWriteArrayList<OnWindowAttachListener>();
+ }
+
+ mOnWindowAttachListeners.add(listener);
+ }
+
+ /**
+ * Remove a previously installed window attach callback.
+ *
+ * @param victim The callback to remove
+ *
+ * @throws IllegalStateException If {@link #isAlive()} returns false
+ *
+ * @see #addOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener)
+ */
+ public void removeOnWindowAttachListener(OnWindowAttachListener victim) {
+ checkIsAlive();
+ if (mOnWindowAttachListeners == null) {
+ return;
+ }
+ mOnWindowAttachListeners.remove(victim);
+ }
+
+ /**
+ * Register a callback to be invoked when the window focus state within the view tree changes.
+ *
+ * @param listener The callback to add
+ *
+ * @throws IllegalStateException If {@link #isAlive()} returns false
+ */
+ public void addOnWindowFocusChangeListener(OnWindowFocusChangeListener listener) {
+ checkIsAlive();
+
+ if (mOnWindowFocusListeners == null) {
+ mOnWindowFocusListeners
+ = new CopyOnWriteArrayList<OnWindowFocusChangeListener>();
+ }
+
+ mOnWindowFocusListeners.add(listener);
+ }
+
+ /**
+ * Remove a previously installed window focus change callback.
+ *
+ * @param victim The callback to remove
+ *
+ * @throws IllegalStateException If {@link #isAlive()} returns false
+ *
+ * @see #addOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener)
+ */
+ public void removeOnWindowFocusChangeListener(OnWindowFocusChangeListener victim) {
+ checkIsAlive();
+ if (mOnWindowFocusListeners == null) {
+ return;
+ }
+ mOnWindowFocusListeners.remove(victim);
+ }
+
+ /**
* Register a callback to be invoked when the focus state within the view tree changes.
*
* @param listener The callback to add
@@ -621,6 +739,41 @@ public final class ViewTreeObserver {
}
/**
+ * Notifies registered listeners that window has been attached/detached.
+ */
+ final void dispatchOnWindowAttachedChange(boolean attached) {
+ // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
+ // perform the dispatching. The iterator is a safe guard against listeners that
+ // could mutate the list by calling the various add/remove methods. This prevents
+ // the array from being modified while we iterate it.
+ final CopyOnWriteArrayList<OnWindowAttachListener> listeners
+ = mOnWindowAttachListeners;
+ if (listeners != null && listeners.size() > 0) {
+ for (OnWindowAttachListener listener : listeners) {
+ if (attached) listener.onWindowAttached();
+ else listener.onWindowDetached();
+ }
+ }
+ }
+
+ /**
+ * Notifies registered listeners that window focus has changed.
+ */
+ final void dispatchOnWindowFocusChange(boolean hasFocus) {
+ // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
+ // perform the dispatching. The iterator is a safe guard against listeners that
+ // could mutate the list by calling the various add/remove methods. This prevents
+ // the array from being modified while we iterate it.
+ final CopyOnWriteArrayList<OnWindowFocusChangeListener> listeners
+ = mOnWindowFocusListeners;
+ if (listeners != null && listeners.size() > 0) {
+ for (OnWindowFocusChangeListener listener : listeners) {
+ listener.onWindowFocusChanged(hasFocus);
+ }
+ }
+ }
+
+ /**
* Notifies registered listeners that focus has changed.
*/
final void dispatchOnGlobalFocusChange(View oldFocus, View newFocus) {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 135d2c8..6f284f8 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2047,11 +2047,28 @@ public class AudioManager {
}
/**
+ * Register a component to be the sole receiver of MEDIA_BUTTON intents. This is like
+ * {@link #registerMediaButtonEventReceiver(android.content.ComponentName)}, but allows
+ * the buttons to go to any PendingIntent. Note that you should only use this form if
+ * you know you will continue running for the full time until unregistering the
+ * PendingIntent.
+ * @param eventReceiver target that will receive media button intents. The PendingIntent
+ * will be sent as-is when a media button action occurs, with {@link Intent#EXTRA_KEY_EVENT}
+ * added and holding the key code of the media button that was pressed.
+ */
+ public void registerMediaButtonEventReceiver(PendingIntent eventReceiver) {
+ if (eventReceiver == null) {
+ return;
+ }
+ registerMediaButtonIntent(eventReceiver, null);
+ }
+
+ /**
* @hide
* no-op if (pi == null) or (eventReceiver == null)
*/
public void registerMediaButtonIntent(PendingIntent pi, ComponentName eventReceiver) {
- if ((pi == null) || (eventReceiver == null)) {
+ if (pi == null) {
Log.e(TAG, "Cannot call registerMediaButtonIntent() with a null parameter");
return;
}
@@ -2114,6 +2131,18 @@ public class AudioManager {
}
/**
+ * Unregister the receiver of MEDIA_BUTTON intents.
+ * @param eventReceiver same PendingIntent that was registed with
+ * {@link #registerMediaButtonEventReceiver(PendingIntent)}.
+ */
+ public void unregisterMediaButtonEventReceiver(PendingIntent eventReceiver) {
+ if (eventReceiver == null) {
+ return;
+ }
+ unregisterMediaButtonIntent(eventReceiver, null);
+ }
+
+ /**
* @hide
*/
public void unregisterMediaButtonIntent(PendingIntent pi, ComponentName eventReceiver) {
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 3e4d6f3..47c70f8 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -776,7 +776,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
}
- /** @see AudioManager#adjustVolume(int, int, int) */
+ /** @see AudioManager#adjustVolume(int, int) */
public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream="+suggestedStreamType);
int streamType;
@@ -916,7 +916,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
sendVolumeUpdate(streamType, oldIndex, index, flags);
}
- /** @see AudioManager#adjustMasterVolume(int) */
+ /** @see AudioManager#adjustMasterVolume(int, int) */
public void adjustMasterVolume(int steps, int flags) {
ensureValidSteps(steps);
int volume = Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME);
@@ -1233,7 +1233,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
return (mStreamStates[streamType].muteCount() != 0);
}
- /** @see AudioManager#setMasterMute(boolean, IBinder) */
+ /** @see AudioManager#setMasterMute(boolean, int) */
public void setMasterMute(boolean state, int flags, IBinder cb) {
if (state != AudioSystem.getMasterMute()) {
AudioSystem.setMasterMute(state);
@@ -1315,7 +1315,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
return Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME);
}
- /** @see AudioManager#getMasterStreamType(int) */
+ /** @see AudioManager#getMasterStreamType() */
public int getMasterStreamType() {
if (mVoiceCapable) {
return AudioSystem.STREAM_RING;
@@ -1975,7 +1975,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
}
- /** @see AudioManager#setSpeakerphoneOn() */
+ /** @see AudioManager#setSpeakerphoneOn(boolean) */
public void setSpeakerphoneOn(boolean on){
if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
return;
@@ -1991,7 +1991,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
return (mForcedUseForComm == AudioSystem.FORCE_SPEAKER);
}
- /** @see AudioManager#setBluetoothScoOn() */
+ /** @see AudioManager#setBluetoothScoOn(boolean) */
public void setBluetoothScoOn(boolean on){
if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
return;
@@ -2009,7 +2009,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
return (mForcedUseForComm == AudioSystem.FORCE_BT_SCO);
}
- /** @see AudioManager#setBluetoothA2dpOn() */
+ /** @see AudioManager#setBluetoothA2dpOn(boolean) */
public void setBluetoothA2dpOn(boolean on) {
synchronized (mBluetoothA2dpEnabledLock) {
mBluetoothA2dpEnabled = on;
@@ -4127,7 +4127,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
AudioSystem.setParameters("screen_state=on");
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
AudioSystem.setParameters("screen_state=off");
- } else if (action.equalsIgnoreCase(Intent.ACTION_CONFIGURATION_CHANGED)) {
+ } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
handleConfigurationChanged(context);
} else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
// attempt to stop music playback for background user
@@ -4296,7 +4296,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
* Helper function:
* Called synchronized on mAudioFocusLock
* Remove a focus listener from the focus stack.
- * @param focusListenerToRemove the focus listener
+ * @param clientToRemove the focus listener
* @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
* focus, notify the next item in the stack it gained focus.
*/
@@ -4402,7 +4402,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
- /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */
+ /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int) */
public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
IAudioFocusDispatcher fd, String clientId, String callingPackageName) {
Log.i(TAG, " AudioFocus requestAudioFocus() from " + clientId);
@@ -4475,7 +4475,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
- /** @see AudioManager#abandonAudioFocus(IAudioFocusDispatcher) */
+ /** @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener) */
public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
Log.i(TAG, " AudioFocus abandonAudioFocus() from " + clientId);
try {
@@ -4813,8 +4813,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
* remote control stack if necessary.
*/
private class RcClientDeathHandler implements IBinder.DeathRecipient {
- private IBinder mCb; // To be notified of client's death
- private PendingIntent mMediaIntent;
+ final private IBinder mCb; // To be notified of client's death
+ final private PendingIntent mMediaIntent;
RcClientDeathHandler(IBinder cb, PendingIntent pi) {
mCb = cb;
@@ -4879,12 +4879,12 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
* The target for the ACTION_MEDIA_BUTTON events.
* Always non null.
*/
- public PendingIntent mMediaIntent;
+ final public PendingIntent mMediaIntent;
/**
* The registered media button event receiver.
* Always non null.
*/
- public ComponentName mReceiverComponent;
+ final public ComponentName mReceiverComponent;
public String mCallingPackageName;
public int mCallingUid;
/**
@@ -5048,7 +5048,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
// evaluated it, traversal order doesn't matter here)
while(stackIterator.hasNext()) {
RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
- if (packageName.equalsIgnoreCase(rcse.mReceiverComponent.getPackageName())) {
+ if (packageName.equals(rcse.mMediaIntent.getCreatorPackage())) {
// a stack entry is from the package being removed, remove it from the stack
stackIterator.remove();
rcse.unlinkToRcClientDeath();
@@ -5061,10 +5061,14 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
null));
} else if (oldTop != mRCStack.peek()) {
// the top of the stack has changed, save it in the system settings
- // by posting a message to persist it
- mAudioHandler.sendMessage(
- mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
- mRCStack.peek().mReceiverComponent));
+ // by posting a message to persist it; only do this however if it has
+ // a concrete component name (is not a transient registration)
+ RemoteControlStackEntry rcse = mRCStack.peek();
+ if (rcse.mReceiverComponent != null) {
+ mAudioHandler.sendMessage(
+ mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
+ rcse.mReceiverComponent));
+ }
}
}
}
@@ -5211,7 +5215,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
/**
* Update the displays and clients with the new "focused" client generation and name
* @param newClientGeneration the new generation value matching a client update
- * @param newClientEventReceiver the media button event receiver associated with the client.
+ * @param newMediaIntent the media button event receiver associated with the client.
* May be null, which implies there is no registered media button event receiver.
* @param clearing true if the new client generation value maps to a remote control update
* where the display should be cleared.