Merge "Fix keyguard camera logic" into lmp-preview-dev
diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml
index 2f51048..108334fa 100644
--- a/core/res/res/values/styles_quantum.xml
+++ b/core/res/res/values/styles_quantum.xml
@@ -518,7 +518,10 @@
<style name="Widget.Quantum.ExpandableListView.White"/>
<style name="Widget.Quantum.Gallery" parent="Widget.Gallery"/>
<style name="Widget.Quantum.GestureOverlayView" parent="Widget.GestureOverlayView"/>
- <style name="Widget.Quantum.GridView" parent="Widget.GridView"/>
+
+ <style name="Widget.Quantum.GridView" parent="Widget.GridView">
+ <item name="android:listSelector">?attr/selectableItemBackground</item>
+ </style>
<style name="Widget.Quantum.CalendarView" parent="Widget.CalendarView">
<item name="selectedWeekBackgroundColor">#330099FF</item>
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index b2d61ec..1512da5b 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -612,7 +612,9 @@
case LINE: {
RectF r = mRect;
float y = r.centerY();
- canvas.drawLine(r.left, y, r.right, y, mStrokePaint);
+ if (haveStroke) {
+ canvas.drawLine(r.left, y, r.right, y, mStrokePaint);
+ }
break;
}
case RING:
@@ -1431,7 +1433,7 @@
public int mChangingConfigurations;
public int mShape = RECTANGLE;
public int mGradient = LINEAR_GRADIENT;
- public int mAngle;
+ public int mAngle = 0;
public Orientation mOrientation;
public ColorStateList mColorStateList;
public ColorStateList mStrokeColorStateList;
@@ -1439,12 +1441,12 @@
public int[] mTempColors; // no need to copy
public float[] mTempPositions; // no need to copy
public float[] mPositions;
- public int mStrokeWidth = -1; // if >= 0 use stroking.
- public float mStrokeDashWidth;
- public float mStrokeDashGap;
- public float mRadius; // use this if mRadiusArray is null
- public float[] mRadiusArray;
- public Rect mPadding;
+ public int mStrokeWidth = -1; // if >= 0 use stroking.
+ public float mStrokeDashWidth = 0.0f;
+ public float mStrokeDashGap = 0.0f;
+ public float mRadius = 0.0f; // use this if mRadiusArray is null
+ public float[] mRadiusArray = null;
+ public Rect mPadding = null;
public int mWidth = -1;
public int mHeight = -1;
public float mInnerRadiusRatio = DEFAULT_INNER_RADIUS_RATIO;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 249b116..8ca303b 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -644,7 +644,12 @@
if (mUseMasterVolume) {
service.adjustMasterVolume(direction, flags, mContext.getOpPackageName());
} else {
- service.adjustVolume(direction, flags, mContext.getOpPackageName());
+ if (USE_SESSIONS) {
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+ helper.sendAdjustVolumeBy(USE_DEFAULT_STREAM_TYPE, direction, flags);
+ } else {
+ service.adjustVolume(direction, flags, mContext.getOpPackageName());
+ }
}
} catch (RemoteException e) {
Log.e(TAG, "Dead object in adjustVolume", e);
@@ -674,8 +679,13 @@
if (mUseMasterVolume) {
service.adjustMasterVolume(direction, flags, mContext.getOpPackageName());
} else {
- service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags,
- mContext.getOpPackageName());
+ if (USE_SESSIONS) {
+ MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
+ helper.sendAdjustVolumeBy(suggestedStreamType, direction, flags);
+ } else {
+ service.adjustSuggestedStreamVolume(direction, suggestedStreamType, flags,
+ mContext.getOpPackageName());
+ }
}
} catch (RemoteException e) {
Log.e(TAG, "Dead object in adjustSuggestedStreamVolume", e);
diff --git a/media/java/android/media/AudioPortEventHandler.java b/media/java/android/media/AudioPortEventHandler.java
index cd9a4de..782ecd8 100644
--- a/media/java/android/media/AudioPortEventHandler.java
+++ b/media/java/android/media/AudioPortEventHandler.java
@@ -49,73 +49,77 @@
// find the looper for our new event handler
Looper looper = Looper.myLooper();
if (looper == null) {
- throw new IllegalArgumentException("Calling thread not associated with a looper");
+ looper = Looper.getMainLooper();
}
- mHandler = new Handler(looper) {
- @Override
- public void handleMessage(Message msg) {
- Log.i(TAG, "handleMessage: "+msg.what);
- ArrayList<AudioManager.OnAudioPortUpdateListener> listeners;
- synchronized (this) {
- if (msg.what == AUDIOPORT_EVENT_NEW_LISTENER) {
- listeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>();
- if (mListeners.contains(msg.obj)) {
- listeners.add((AudioManager.OnAudioPortUpdateListener)msg.obj);
+ if (looper != null) {
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ Log.i(TAG, "handleMessage: "+msg.what);
+ ArrayList<AudioManager.OnAudioPortUpdateListener> listeners;
+ synchronized (this) {
+ if (msg.what == AUDIOPORT_EVENT_NEW_LISTENER) {
+ listeners = new ArrayList<AudioManager.OnAudioPortUpdateListener>();
+ if (mListeners.contains(msg.obj)) {
+ listeners.add((AudioManager.OnAudioPortUpdateListener)msg.obj);
+ }
+ } else {
+ listeners = mListeners;
}
- } else {
- listeners = mListeners;
}
- }
- if (listeners.isEmpty()) {
- return;
- }
- // reset audio port cache if the event corresponds to a change coming
- // from audio policy service or if mediaserver process died.
- if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED ||
- msg.what == AUDIOPORT_EVENT_PATCH_LIST_UPDATED ||
- msg.what == AUDIOPORT_EVENT_SERVICE_DIED) {
- mAudioManager.resetAudioPortGeneration();
- }
- ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
- ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>();
- if (msg.what != AUDIOPORT_EVENT_SERVICE_DIED) {
- int status = mAudioManager.updateAudioPortCache(ports, patches);
- if (status != AudioManager.SUCCESS) {
+ if (listeners.isEmpty()) {
return;
}
- }
-
- switch (msg.what) {
- case AUDIOPORT_EVENT_NEW_LISTENER:
- case AUDIOPORT_EVENT_PORT_LIST_UPDATED:
- AudioPort[] portList = ports.toArray(new AudioPort[0]);
- for (int i = 0; i < listeners.size(); i++) {
- listeners.get(i).OnAudioPortListUpdate(portList);
+ // reset audio port cache if the event corresponds to a change coming
+ // from audio policy service or if mediaserver process died.
+ if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED ||
+ msg.what == AUDIOPORT_EVENT_PATCH_LIST_UPDATED ||
+ msg.what == AUDIOPORT_EVENT_SERVICE_DIED) {
+ mAudioManager.resetAudioPortGeneration();
}
- if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED) {
+ ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
+ ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>();
+ if (msg.what != AUDIOPORT_EVENT_SERVICE_DIED) {
+ int status = mAudioManager.updateAudioPortCache(ports, patches);
+ if (status != AudioManager.SUCCESS) {
+ return;
+ }
+ }
+
+ switch (msg.what) {
+ case AUDIOPORT_EVENT_NEW_LISTENER:
+ case AUDIOPORT_EVENT_PORT_LIST_UPDATED:
+ AudioPort[] portList = ports.toArray(new AudioPort[0]);
+ for (int i = 0; i < listeners.size(); i++) {
+ listeners.get(i).OnAudioPortListUpdate(portList);
+ }
+ if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED) {
+ break;
+ }
+ // FALL THROUGH
+
+ case AUDIOPORT_EVENT_PATCH_LIST_UPDATED:
+ AudioPatch[] patchList = patches.toArray(new AudioPatch[0]);
+ for (int i = 0; i < listeners.size(); i++) {
+ listeners.get(i).OnAudioPatchListUpdate(patchList);
+ }
+ break;
+
+ case AUDIOPORT_EVENT_SERVICE_DIED:
+ for (int i = 0; i < listeners.size(); i++) {
+ listeners.get(i).OnServiceDied();
+ }
+ break;
+
+ default:
break;
}
- // FALL THROUGH
-
- case AUDIOPORT_EVENT_PATCH_LIST_UPDATED:
- AudioPatch[] patchList = patches.toArray(new AudioPatch[0]);
- for (int i = 0; i < listeners.size(); i++) {
- listeners.get(i).OnAudioPatchListUpdate(patchList);
- }
- break;
-
- case AUDIOPORT_EVENT_SERVICE_DIED:
- for (int i = 0; i < listeners.size(); i++) {
- listeners.get(i).OnServiceDied();
- }
- break;
-
- default:
- break;
}
- }
- };
+ };
+ } else {
+ mHandler = null;
+ }
native_setup(new WeakReference<AudioPortEventHandler>(this));
}
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index c4233c3..1cfc5bc 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -47,4 +47,8 @@
void setMetadata(in MediaMetadata metadata);
void setPlaybackState(in PlaybackState state);
void setRatingType(int type);
+
+ // These commands relate to volume handling
+ void configureVolumeHandling(int type, int arg1, int arg2);
+ void setCurrentVolume(int currentVolume);
}
\ No newline at end of file
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index 103c3f1..0316d1fa 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -45,4 +45,8 @@
void onRewind();
void onSeekTo(long pos);
void onRate(in Rating rating);
+
+ // These callbacks are for volume handling
+ void onAdjustVolumeBy(int delta);
+ void onSetVolumeTo(int value);
}
\ No newline at end of file
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 38b92932..6d9888f 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -29,4 +29,5 @@
ISession createSession(String packageName, in ISessionCallback cb, String tag, int userId);
List<IBinder> getSessions(in ComponentName compName, int userId);
void dispatchMediaKeyEvent(in KeyEvent keyEvent, boolean needWakeLock);
+ void dispatchAdjustVolumeBy(int suggestedStream, int delta, int flags);
}
\ No newline at end of file
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 90ccf68..7972639 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -124,9 +124,21 @@
*/
public static final int DISCONNECT_REASON_SESSION_DESTROYED = 5;
- private static final String KEY_COMMAND = "command";
- private static final String KEY_EXTRAS = "extras";
- private static final String KEY_CALLBACK = "callback";
+ /**
+ * The session uses local playback. Used for configuring volume handling
+ * with the system.
+ *
+ * @hide
+ */
+ public static final int VOLUME_TYPE_LOCAL = 1;
+
+ /**
+ * The session uses remote playback. Used for configuring volume handling
+ * with the system.
+ *
+ * @hide
+ */
+ public static final int VOLUME_TYPE_REMOTE = 2;
private final Object mLock = new Object();
@@ -143,6 +155,7 @@
= new ArrayMap<String, RouteInterface.EventListener>();
private Route mRoute;
+ private RemoteVolumeProvider mVolumeProvider;
private boolean mActive = false;;
@@ -242,7 +255,11 @@
* @param stream The {@link AudioManager} stream this session is playing on.
*/
public void setPlaybackToLocal(int stream) {
- // TODO
+ try {
+ mBinder.configureVolumeHandling(VOLUME_TYPE_LOCAL, stream, 0);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Failure in setPlaybackToLocal.", e);
+ }
}
/**
@@ -259,7 +276,14 @@
if (volumeProvider == null) {
throw new IllegalArgumentException("volumeProvider may not be null!");
}
- // TODO
+ mVolumeProvider = volumeProvider;
+
+ try {
+ mBinder.configureVolumeHandling(VOLUME_TYPE_REMOTE, volumeProvider.getVolumeControl(),
+ volumeProvider.getMaxVolume());
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Failure in setPlaybackToRemote.", e);
+ }
}
/**
@@ -942,6 +966,26 @@
}
+ /*
+ * (non-Javadoc)
+ * @see android.media.session.ISessionCallback#onAdjustVolumeBy(int)
+ */
+ @Override
+ public void onAdjustVolumeBy(int delta) throws RemoteException {
+ // TODO(epastern): Auto-generated method stub
+
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see android.media.session.ISessionCallback#onSetVolumeTo(int)
+ */
+ @Override
+ public void onSetVolumeTo(int value) throws RemoteException {
+ // TODO(epastern): Auto-generated method stub
+
+ }
+
}
private class CallbackMessageHandler extends Handler {
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index c303e77..099f601 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -76,6 +76,13 @@
}
}
+ public void sendAdjustVolumeBy(int suggestedStream, int delta, int flags) {
+ mSessionManager.dispatchAdjustVolumeBy(suggestedStream, delta, flags);
+ if (DEBUG) {
+ Log.d(TAG, "dispatched volume adjustment");
+ }
+ }
+
public void addRccListener(PendingIntent pi,
MediaSession.TransportControlsCallback listener) {
if (pi == null) {
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 8d5e338..9e8b0d3 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -166,4 +166,22 @@
Log.e(TAG, "Failed to send key event.", e);
}
}
+
+ /**
+ * Dispatch an adjust volume request to the system. It will be routed to the
+ * most relevant stream/session.
+ *
+ * @param suggestedStream The stream to fall back to if there isn't a
+ * relevant stream
+ * @param delta The amount to adjust the volume by.
+ * @param flags Any flags to include with the volume change.
+ * @hide
+ */
+ public void dispatchAdjustVolumeBy(int suggestedStream, int delta, int flags) {
+ try {
+ mService.dispatchAdjustVolumeBy(suggestedStream, delta, flags);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to send adjust volume.", e);
+ }
+ }
}
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 7460c73..bfbdcf3 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -318,4 +318,7 @@
<!-- Volume panel z depth -->
<dimen name="volume_panel_z">3dp</dimen>
+
+ <!-- Move distance for the hint animations on the lockscreen (unlock, phone, camera)-->
+ <dimen name="hint_move_distance">75dp</dimen>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BounceInterpolator.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BounceInterpolator.java
new file mode 100644
index 0000000..367d326
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BounceInterpolator.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.view.animation.Interpolator;
+
+/**
+ * An implementation of a bouncer interpolator optimized for unlock hinting.
+ */
+public class BounceInterpolator implements Interpolator {
+
+ private final static float SCALE_FACTOR = 7.5625f;
+
+ @Override
+ public float getInterpolation(float t) {
+ if (t < 4f / 11f) {
+ return SCALE_FACTOR * t * t;
+ } else if (t < 8f / 11f) {
+ float t2 = t - 6f / 11f;
+ return SCALE_FACTOR * t2 * t2 + 3f / 4f;
+ } else if (t < 10f / 11f) {
+ float t2 = t - 9f / 11f;
+ return SCALE_FACTOR * t2 * t2 + 15f / 16f;
+ } else {
+ float t2 = t - 21f / 22f;
+ return SCALE_FACTOR * t2 * t2 + 63f / 64f;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 03d164b..dfd5a88 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -51,7 +51,6 @@
private static float EXPANSION_RUBBER_BAND_EXTRA_FACTOR = 0.6f;
private KeyguardPageSwipeHelper mPageSwiper;
- PhoneStatusBar mStatusBar;
private StatusBarHeaderView mHeader;
private View mQsContainer;
private View mQsPanel;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 220b691..4686933 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -27,10 +27,13 @@
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import com.android.systemui.R;
import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.statusbar.StatusBarState;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -44,13 +47,16 @@
Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
}
+ protected PhoneStatusBar mStatusBar;
private float mPeekHeight;
+ private float mHintDistance;
private float mInitialOffsetOnTouch;
private float mExpandedFraction = 0;
private float mExpandedHeight = 0;
private boolean mJustPeeked;
private boolean mClosing;
private boolean mTracking;
+ private boolean mTouchSlopExceeded;
private int mTrackingPointer;
protected int mTouchSlop;
@@ -66,6 +72,9 @@
private float mInitialTouchY;
private float mInitialTouchX;
+ private Interpolator mLinearOutSlowInInterpolator;
+ private Interpolator mBounceInterpolator;
+
protected void onExpandingFinished() {
mBar.onExpandingFinished();
}
@@ -89,6 +98,9 @@
public PanelView(Context context, AttributeSet attrs) {
super(context, attrs);
mFlingAnimationUtils = new FlingAnimationUtils(context, 0.6f);
+ mLinearOutSlowInInterpolator =
+ AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
+ mBounceInterpolator = new BounceInterpolator();
}
protected void loadDimens() {
@@ -98,6 +110,7 @@
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();
+ mHintDistance = res.getDimension(R.dimen.hint_move_distance);
}
private void trackMovement(MotionEvent event) {
@@ -138,6 +151,7 @@
mInitialTouchY = y;
mInitialTouchX = x;
mInitialOffsetOnTouch = mExpandedHeight;
+ mTouchSlopExceeded = false;
if (mVelocityTracker == null) {
initVelocityTracker();
}
@@ -170,16 +184,18 @@
case MotionEvent.ACTION_MOVE:
float h = y - mInitialTouchY;
- if (waitForTouchSlop && !mTracking && Math.abs(h) > mTouchSlop
- && Math.abs(h) > Math.abs(x - mInitialTouchX)) {
- mInitialOffsetOnTouch = mExpandedHeight;
- mInitialTouchX = x;
- mInitialTouchY = y;
- if (mHeightAnimator != null) {
- mHeightAnimator.cancel(); // end any outstanding animations
+ if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)) {
+ mTouchSlopExceeded = true;
+ if (waitForTouchSlop && !mTracking) {
+ mInitialOffsetOnTouch = mExpandedHeight;
+ mInitialTouchX = x;
+ mInitialTouchY = y;
+ if (mHeightAnimator != null) {
+ mHeightAnimator.cancel(); // end any outstanding animations
+ }
+ onTrackingStarted();
+ h = 0;
}
- onTrackingStarted();
- h = 0;
}
final float newHeight = h + mInitialOffsetOnTouch;
if (newHeight > mPeekHeight) {
@@ -200,10 +216,15 @@
case MotionEvent.ACTION_CANCEL:
mTrackingPointer = -1;
trackMovement(event);
- float vel = getCurrentVelocity();
- boolean expand = flingExpands(vel);
- onTrackingStopped(expand);
- fling(vel, expand);
+ if (mTracking && mTouchSlopExceeded) {
+ float vel = getCurrentVelocity();
+ boolean expand = flingExpands(vel);
+ onTrackingStopped(expand);
+ fling(vel, expand);
+ } else {
+ boolean expands = onEmptySpaceClick();
+ onTrackingStopped(expands);
+ }
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
@@ -264,6 +285,7 @@
}
mInitialTouchY = y;
mInitialTouchX = x;
+ mTouchSlopExceeded = false;
initVelocityTracker();
trackMovement(event);
break;
@@ -287,6 +309,7 @@
mInitialTouchY = y;
mInitialTouchX = x;
mTracking = true;
+ mTouchSlopExceeded = true;
onTrackingStarted();
return true;
}
@@ -344,7 +367,7 @@
mBar.panelExpansionChanged(this, mExpandedFraction);
return;
}
- ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, target);
+ ValueAnimator animator = createHeightAnimator(target);
if (expand) {
mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, getHeight());
} else {
@@ -356,12 +379,6 @@
animator.setDuration((long) (animator.getDuration() / 1.75f));
}
}
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- setExpandedHeight((Float) animation.getAnimatedValue());
- }
- });
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -449,9 +466,7 @@
mOverExpansion = overExpansion;
}
- protected void onHeightUpdated(float expandedHeight) {
- requestLayout();
- }
+ protected abstract void onHeightUpdated(float expandedHeight);
/**
* This returns the maximum height of the panel. Children should override this if their
@@ -526,6 +541,101 @@
}
}
+ protected void startUnlockHintAnimation() {
+
+ // We don't need to hint the user if an animation is already running or the user is changing
+ // the expansion.
+ if (mHeightAnimator != null || mTracking) {
+ return;
+ }
+ cancelPeek();
+ onExpandingStarted();
+ startUnlockHintAnimationPhase1();
+ mStatusBar.onUnlockHintStarted();
+ }
+
+ /**
+ * Phase 1: Move everything upwards.
+ */
+ private void startUnlockHintAnimationPhase1() {
+ float target = Math.max(0, getMaxPanelHeight() - mHintDistance);
+ ValueAnimator animator = createHeightAnimator(target);
+ animator.setDuration(250);
+ animator.setInterpolator(mLinearOutSlowInInterpolator);
+ animator.addListener(new AnimatorListenerAdapter() {
+ private boolean mCancelled;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mCancelled) {
+ mHeightAnimator = null;
+ onExpandingFinished();
+ mStatusBar.onUnlockHintFinished();
+ } else {
+ startUnlockHintAnimationPhase2();
+ }
+ }
+ });
+ animator.start();
+ mHeightAnimator = animator;
+ }
+
+ /**
+ * Phase 2: Bounce down.
+ */
+ private void startUnlockHintAnimationPhase2() {
+ ValueAnimator animator = createHeightAnimator(getMaxPanelHeight());
+ animator.setDuration(450);
+ animator.setInterpolator(mBounceInterpolator);
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mHeightAnimator = null;
+ onExpandingFinished();
+ mStatusBar.onUnlockHintFinished();
+ }
+ });
+ animator.start();
+ mHeightAnimator = animator;
+ }
+
+ private ValueAnimator createHeightAnimator(float targetHeight) {
+ ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, targetHeight);
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ setExpandedHeight((Float) animation.getAnimatedValue());
+ }
+ });
+ return animator;
+ }
+
+ /**
+ * Gets called when the user performs a click anywhere in the empty area of the panel.
+ *
+ * @return whether the panel will be expanded after the action performed by this method
+ */
+ private boolean onEmptySpaceClick() {
+ switch (mStatusBar.getBarState()) {
+ case StatusBarState.KEYGUARD:
+ startUnlockHintAnimation();
+ return true;
+ case StatusBarState.SHADE_LOCKED:
+ // TODO: Go to Keyguard again.
+ return true;
+ case StatusBarState.SHADE:
+ collapse();
+ return false;
+ default:
+ return true;
+ }
+ }
+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(String.format("[PanelView(%s): expandedHeight=%f maxPanelHeight=%d closing=%s"
+ " tracking=%s justPeeked=%s peekAnim=%s%s timeAnim=%s%s"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index f6e6fa8..b1216e69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -2957,15 +2957,17 @@
}
public void onTrackingStarted() {
- if (mState == StatusBarState.KEYGUARD) {
- mKeyguardIndicationTextView.switchIndication(R.string.keyguard_unlock);
- }
+ }
+
+ public void onUnlockHintStarted() {
+ mKeyguardIndicationTextView.switchIndication(R.string.keyguard_unlock);
+ }
+
+ public void onUnlockHintFinished() {
+ mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
}
public void onTrackingStopped(boolean expand) {
- if (mState == StatusBarState.KEYGUARD) {
- mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
- }
if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
if (!expand && !mUnlockMethodCache.isMethodInsecure()) {
showBouncer();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index f41ab3a..2edd7d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -715,6 +715,9 @@
public void animateOverScrollToAmount(float targetAmount, final boolean onTop) {
final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop);
+ if (targetAmount == startOverScrollAmount) {
+ return;
+ }
cancelOverScrollAnimators(onTop);
ValueAnimator overScrollAnimator = ValueAnimator.ofFloat(startOverScrollAmount,
targetAmount);
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index c909a54..737ffda 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -25,6 +25,7 @@
import android.media.session.ISession;
import android.media.session.ISessionCallback;
import android.media.session.MediaController;
+import android.media.session.RemoteVolumeProvider;
import android.media.session.RouteCommand;
import android.media.session.RouteInfo;
import android.media.session.RouteOptions;
@@ -33,6 +34,7 @@
import android.media.session.MediaSessionInfo;
import android.media.session.RouteInterface;
import android.media.session.PlaybackState;
+import android.media.AudioManager;
import android.media.MediaMetadata;
import android.media.Rating;
import android.os.Bundle;
@@ -112,6 +114,14 @@
private long mLastActiveTime;
// End TransportPerformer fields
+ // Volume handling fields
+ private int mPlaybackType = MediaSession.VOLUME_TYPE_LOCAL;
+ private int mAudioStream = AudioManager.STREAM_MUSIC;
+ private int mVolumeControlType = RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE;
+ private int mMaxVolume = 0;
+ private int mCurrentVolume = 0;
+ // End volume handling fields
+
private boolean mIsActive = false;
private boolean mDestroyed = false;
@@ -248,6 +258,27 @@
}
/**
+ * Send a volume adjustment to the session owner.
+ *
+ * @param delta The amount to adjust the volume by.
+ */
+ public void adjustVolumeBy(int delta) {
+ if (mVolumeControlType == RemoteVolumeProvider.VOLUME_CONTROL_FIXED) {
+ // Nothing to do, the volume cannot be changed
+ return;
+ }
+ mSessionCb.adjustVolumeBy(delta);
+ }
+
+ public void setVolumeTo(int value) {
+ if (mVolumeControlType != RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE) {
+ // Nothing to do. The volume can't be set directly.
+ return;
+ }
+ mSessionCb.setVolumeTo(value);
+ }
+
+ /**
* Set the connection to use for the selected route and notify the app it is
* now connected.
*
@@ -294,14 +325,16 @@
* Check if the session is currently performing playback. This will also
* return true if the session was recently paused.
*
+ * @param includeRecentlyActive True if playback that was recently paused
+ * should count, false if it shouldn't.
* @return True if the session is performing playback, false otherwise.
*/
- public boolean isPlaybackActive() {
+ public boolean isPlaybackActive(boolean includeRecentlyActive) {
int state = mPlaybackState == null ? 0 : mPlaybackState.getState();
if (isActiveState(state)) {
return true;
}
- if (state == mPlaybackState.STATE_PAUSED) {
+ if (includeRecentlyActive && state == mPlaybackState.STATE_PAUSED) {
long inactiveTime = SystemClock.uptimeMillis() - mLastActiveTime;
if (inactiveTime < ACTIVE_BUFFER) {
return true;
@@ -311,6 +344,54 @@
}
/**
+ * Get the type of playback, either local or remote.
+ *
+ * @return The current type of playback.
+ */
+ public int getPlaybackType() {
+ return mPlaybackType;
+ }
+
+ /**
+ * Get the local audio stream being used. Only valid if playback type is
+ * local.
+ *
+ * @return The audio stream the session is using.
+ */
+ public int getAudioStream() {
+ return mAudioStream;
+ }
+
+ /**
+ * Get the type of volume control. Only valid if playback type is remote.
+ *
+ * @return The volume control type being used.
+ */
+ public int getVolumeControl() {
+ return mVolumeControlType;
+ }
+
+ /**
+ * Get the max volume that can be set. Only valid if playback type is
+ * remote.
+ *
+ * @return The max volume that can be set.
+ */
+ public int getMaxVolume() {
+ return mMaxVolume;
+ }
+
+ /**
+ * Get the current volume for this session. Only valid if playback type is
+ * remote.
+ *
+ * @return The current volume of the remote playback.
+ */
+ public int getCurrentVolume() {
+ return mCurrentVolume;
+ }
+
+ /**
* @return True if this session is currently connected to a route.
*/
public boolean isConnected() {
@@ -640,6 +721,40 @@
mRequests.add(request);
}
}
+
+ @Override
+ public void setCurrentVolume(int volume) {
+ mCurrentVolume = volume;
+ }
+
+ @Override
+ public void configureVolumeHandling(int type, int arg1, int arg2) throws RemoteException {
+ switch(type) {
+ case MediaSession.VOLUME_TYPE_LOCAL:
+ mPlaybackType = type;
+ int audioStream = arg1;
+ if (isValidStream(audioStream)) {
+ mAudioStream = audioStream;
+ } else {
+ Log.e(TAG, "Cannot set stream to " + audioStream + ". Using music stream");
+ mAudioStream = AudioManager.STREAM_MUSIC;
+ }
+ break;
+ case MediaSession.VOLUME_TYPE_REMOTE:
+ mPlaybackType = type;
+ mVolumeControlType = arg1;
+ mMaxVolume = arg2;
+ break;
+ default:
+ throw new IllegalArgumentException("Volume handling type " + type
+ + " not recognized.");
+ }
+ }
+
+ private boolean isValidStream(int stream) {
+ return stream >= AudioManager.STREAM_VOICE_CALL
+ && stream <= AudioManager.STREAM_NOTIFICATION;
+ }
}
class SessionCb {
@@ -780,6 +895,22 @@
Slog.e(TAG, "Remote failure in rate.", e);
}
}
+
+ public void adjustVolumeBy(int delta) {
+ try {
+ mCb.onAdjustVolumeBy(delta);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in adjustVolumeBy.", e);
+ }
+ }
+
+ public void setVolumeTo(int value) {
+ try {
+ mCb.onSetVolumeTo(value);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in adjustVolumeBy.", e);
+ }
+ }
}
class ControllerStub extends ISessionController.Stub {
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 9d85167..87665e1 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -26,6 +26,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.media.AudioManager;
+import android.media.IAudioService;
import android.media.routeprovider.RouteRequest;
import android.media.session.ISession;
import android.media.session.ISessionCallback;
@@ -40,6 +42,7 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.speech.RecognizerIntent;
@@ -79,6 +82,7 @@
private final PowerManager.WakeLock mMediaEventWakeLock;
private KeyguardManager mKeyguardManager;
+ private IAudioService mAudioService;
private MediaSessionRecord mPrioritySession;
private int mCurrentUserId = -1;
@@ -105,6 +109,12 @@
updateUser();
mKeyguardManager =
(KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
+ mAudioService = getAudioService();
+ }
+
+ private IAudioService getAudioService() {
+ IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
+ return IAudioService.Stub.asInterface(b);
}
/**
@@ -703,6 +713,23 @@
}
@Override
+ public void dispatchAdjustVolumeBy(int suggestedStream, int delta, int flags)
+ throws RemoteException {
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ MediaSessionRecord session = mPriorityStack
+ .getDefaultVolumeSession(mCurrentUserId);
+ dispatchAdjustVolumeByLocked(suggestedStream, delta, flags, session);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -737,6 +764,49 @@
}
}
+ private void dispatchAdjustVolumeByLocked(int suggestedStream, int delta, int flags,
+ MediaSessionRecord session) {
+ int direction = 0;
+ int steps = delta;
+ if (delta > 0) {
+ direction = 1;
+ } else if (delta < 0) {
+ direction = -1;
+ steps = -delta;
+ }
+ if (DEBUG) {
+ String sessionInfo = session == null ? null : session.getSessionInfo().toString();
+ Log.d(TAG, "Adjusting session " + sessionInfo + " by " + delta + ". flags=" + flags
+ + ", suggestedStream=" + suggestedStream);
+
+ }
+ if (session == null) {
+ for (int i = 0; i < steps; i++) {
+ try {
+ mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
+ flags, getContext().getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error adjusting default volume.", e);
+ }
+ }
+ } else {
+ if (session.getPlaybackType() == MediaSession.VOLUME_TYPE_LOCAL) {
+ for (int i = 0; i < steps; i++) {
+ try {
+ mAudioService.adjustSuggestedStreamVolume(direction,
+ session.getAudioStream(), flags,
+ getContext().getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error adjusting volume for stream "
+ + session.getAudioStream(), e);
+ }
+ }
+ } else if (session.getPlaybackType() == MediaSession.VOLUME_TYPE_REMOTE) {
+ session.adjustVolumeBy(delta);
+ }
+ }
+ }
+
private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
MediaSessionRecord session) {
if (session != null && session.hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index 56236f8..803dee2 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -52,6 +52,7 @@
private MediaSessionRecord mCachedButtonReceiver;
private MediaSessionRecord mCachedDefault;
+ private MediaSessionRecord mCachedVolumeDefault;
private ArrayList<MediaSessionRecord> mCachedActiveList;
private ArrayList<MediaSessionRecord> mCachedTransportControlList;
@@ -93,6 +94,9 @@
mSessions.remove(record);
mSessions.add(0, record);
clearCache();
+ } else if (newState == PlaybackState.STATE_PAUSED) {
+ // Just clear the volume cache in this case
+ mCachedVolumeDefault = null;
}
}
@@ -177,6 +181,25 @@
return mCachedButtonReceiver;
}
+ public MediaSessionRecord getDefaultVolumeSession(int userId) {
+ if (mGlobalPrioritySession != null && mGlobalPrioritySession.isActive()) {
+ return mGlobalPrioritySession;
+ }
+ if (mCachedVolumeDefault != null) {
+ return mCachedVolumeDefault;
+ }
+ ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId);
+ int size = records.size();
+ for (int i = 0; i < size; i++) {
+ MediaSessionRecord record = records.get(i);
+ if (record.isPlaybackActive(false)) {
+ mCachedVolumeDefault = record;
+ return record;
+ }
+ }
+ return null;
+ }
+
public void dump(PrintWriter pw, String prefix) {
ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0,
UserHandle.USER_ALL);
@@ -237,7 +260,7 @@
lastLocalIndex++;
lastActiveIndex++;
lastPublishedIndex++;
- } else if (session.isPlaybackActive()) {
+ } else if (session.isPlaybackActive(true)) {
// TODO replace getRoute() == null with real local route check
if(session.getRoute() == null) {
// Active local sessions get top priority
@@ -284,6 +307,7 @@
private void clearCache() {
mCachedDefault = null;
+ mCachedVolumeDefault = null;
mCachedButtonReceiver = null;
mCachedActiveList = null;
mCachedTransportControlList = null;
diff --git a/telecomm/java/android/telecomm/CallService.java b/telecomm/java/android/telecomm/CallService.java
index 51f10c1..d452172 100644
--- a/telecomm/java/android/telecomm/CallService.java
+++ b/telecomm/java/android/telecomm/CallService.java
@@ -27,6 +27,8 @@
import com.android.internal.telecomm.ICallService;
import com.android.internal.telecomm.ICallServiceAdapter;
+import java.util.List;
+
/**
* Base implementation of CallService which can be used to provide calls for the system
* in-call UI. CallService is a one-way service from the framework's CallsManager to any app
@@ -59,6 +61,8 @@
private static final int MSG_ON_AUDIO_STATE_CHANGED = 11;
private static final int MSG_PLAY_DTMF_TONE = 12;
private static final int MSG_STOP_DTMF_TONE = 13;
+ private static final int MSG_ADD_TO_CONFERENCE = 14;
+ private static final int MSG_SPLIT_FROM_CONFERENCE = 15;
/**
* Default Handler used to consolidate binder method calls onto a single thread.
@@ -123,6 +127,29 @@
case MSG_STOP_DTMF_TONE:
stopDtmfTone((String) msg.obj);
break;
+ case MSG_ADD_TO_CONFERENCE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ @SuppressWarnings("unchecked")
+ List<String> callIds = (List<String>) args.arg2;
+ String conferenceCallId = (String) args.arg1;
+ addToConference(conferenceCallId, callIds);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_SPLIT_FROM_CONFERENCE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ String conferenceCallId = (String) args.arg1;
+ String callId = (String) args.arg2;
+ splitFromConference(conferenceCallId, callId);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
default:
break;
}
@@ -204,6 +231,22 @@
args.arg2 = audioState;
mMessageHandler.obtainMessage(MSG_ON_AUDIO_STATE_CHANGED, args).sendToTarget();
}
+
+ @Override
+ public void addToConference(String conferenceCallId, List<String> callsToConference) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = conferenceCallId;
+ args.arg2 = callsToConference;
+ mMessageHandler.obtainMessage(MSG_ADD_TO_CONFERENCE, args).sendToTarget();
+ }
+
+ @Override
+ public void splitFromConference(String conferenceCallId, String callId) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = conferenceCallId;
+ args.arg2 = callId;
+ mMessageHandler.obtainMessage(MSG_SPLIT_FROM_CONFERENCE, args).sendToTarget();
+ }
}
/**
@@ -359,4 +402,24 @@
* @param audioState The new {@link CallAudioState}.
*/
public abstract void onAudioStateChanged(String activeCallId, CallAudioState audioState);
+
+ /**
+ * Adds the specified calls to the specified conference call.
+ *
+ * @param conferenceCallId The unique ID of the conference call onto which the specified calls
+ * should be added.
+ * @param callIds The calls to add to the conference call.
+ * @hide
+ */
+ public abstract void addToConference(String conferenceCallId, List<String> callIds);
+
+ /**
+ * Removes the specified call from the specified conference call. This is a no-op if the call
+ * is not already part of the conference call.
+ *
+ * @param conferenceCallId The conference call.
+ * @param callId The call to remove from the conference call
+ * @hide
+ */
+ public abstract void splitFromConference(String conferenceCallId, String callId);
}
diff --git a/telecomm/java/android/telecomm/CallServiceAdapter.java b/telecomm/java/android/telecomm/CallServiceAdapter.java
index dafc310..7396808 100644
--- a/telecomm/java/android/telecomm/CallServiceAdapter.java
+++ b/telecomm/java/android/telecomm/CallServiceAdapter.java
@@ -20,6 +20,8 @@
import com.android.internal.telecomm.ICallServiceAdapter;
+import java.util.List;
+
/**
* Provides methods for ICallService implementations to interact with the system phone app.
* TODO(santoscordon): Need final public-facing comments in this file.
@@ -169,4 +171,46 @@
}
}
+ /**
+ * Indicates that the specified call can conference with any of the specified list of calls.
+ *
+ * @param callId The unique ID of the call.
+ * @param conferenceCapableCallIds The unique IDs of the calls which can be conferenced.
+ * @hide
+ */
+ public void setCanConferenceWith(String callId, List<String> conferenceCapableCallIds) {
+ try {
+ mAdapter.setCanConferenceWith(callId, conferenceCapableCallIds);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Indicates whether or not the specified call is currently conferenced into the specified
+ * conference call.
+ *
+ * @param conferenceCallId The unique ID of the conference call.
+ * @param callId The unique ID of the call being conferenced.
+ * @hide
+ */
+ public void setIsConferenced(String conferenceCallId, String callId, boolean isConferenced) {
+ try {
+ mAdapter.setIsConferenced(conferenceCallId, callId, isConferenced);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Indicates that the call no longer exists. Can be used with either a call or a conference
+ * call.
+ *
+ * @param callId The unique ID of the call.
+ * @hide
+ */
+ public void removeCall(String callId) {
+ try {
+ mAdapter.removeCall(callId);
+ } catch (RemoteException ignored) {
+ }
+ }
}
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index 8d02842..aeb1c33 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -20,6 +20,8 @@
import android.os.Bundle;
import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
/**
@@ -248,6 +250,39 @@
findConnectionForAction(callId, "onAudioStateChanged").setAudioState(audioState);
}
+ /** @hide */
+ @Override
+ public final void addToConference(String conferenceCallId, List<String> callIds) {
+ Log.d(this, "addToConference %s, %s", conferenceCallId, callIds);
+
+ List<Connection> connections = new LinkedList<>();
+ for (String id : callIds) {
+ Connection connection = findConnectionForAction(id, "addToConference");
+ if (connection == NULL_CONNECTION) {
+ Log.w(this, "Connection missing in conference request %s.", id);
+ return;
+ }
+ connections.add(connection);
+ }
+
+ // TODO(santoscordon): Find an existing conference call or create a new one. Then call
+ // conferenceWith on it.
+ }
+
+ /** @hide */
+ @Override
+ public final void splitFromConference(String conferenceCallId, String callId) {
+ Log.d(this, "splitFromConference(%s, %s)", conferenceCallId, callId);
+
+ Connection connection = findConnectionForAction(callId, "splitFromConference");
+ if (connection == NULL_CONNECTION) {
+ Log.w(this, "Connection missing in conference request %s.", callId);
+ return;
+ }
+
+ // TODO(santoscordon): Find existing conference call and invoke split(connection).
+ }
+
/**
* Find a set of Subscriptions matching a given handle (e.g. phone number).
*
@@ -342,4 +377,4 @@
Log.w(this, "%s - Cannot find Connection %s", action, callId);
return NULL_CONNECTION;
}
-}
\ No newline at end of file
+}
diff --git a/telecomm/java/android/telecomm/InCallAdapter.java b/telecomm/java/android/telecomm/InCallAdapter.java
index e41d3f6..6838ede 100644
--- a/telecomm/java/android/telecomm/InCallAdapter.java
+++ b/telecomm/java/android/telecomm/InCallAdapter.java
@@ -196,4 +196,32 @@
} catch (RemoteException e) {
}
}
+
+ /**
+ * Instructs Telecomm to conference the specified calls together.
+ *
+ * @param callId The unique ID of the call.
+ * @param callIdToConference The unique ID of the call to conference with.
+ * @hide
+ */
+ void conferenceWith(String callId, String callIdToConference) {
+ try {
+ mAdapter.conferenceWith(callId, callIdToConference);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Instructs Telecomm to split the specified call from any conference call with which it may be
+ * connected.
+ *
+ * @param callId The unique ID of the call.
+ * @hide
+ */
+ void splitFromConference(String callId) {
+ try {
+ mAdapter.splitFromConference(callId);
+ } catch (RemoteException ignored) {
+ }
+ }
}
diff --git a/telecomm/java/android/telecomm/InCallCall.java b/telecomm/java/android/telecomm/InCallCall.java
index c3b2ae7..346d2077 100644
--- a/telecomm/java/android/telecomm/InCallCall.java
+++ b/telecomm/java/android/telecomm/InCallCall.java
@@ -17,13 +17,13 @@
package android.telecomm;
import android.net.Uri;
-import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.DisconnectCause;
-import java.util.Date;
-import java.util.UUID;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
/**
* Information about a call that is used between InCallService and Telecomm.
@@ -38,6 +38,26 @@
private final GatewayInfo mGatewayInfo;
private final CallServiceDescriptor mCurrentCallServiceDescriptor;
private final CallServiceDescriptor mHandoffCallServiceDescriptor;
+ private final List<String> mConferenceCapableCallIds;
+ private final String mParentCallId;
+ private final List<String> mChildCallIds;
+
+ /** @hide */
+ @SuppressWarnings("unchecked")
+ public InCallCall(
+ String id,
+ CallState state,
+ int disconnectCause,
+ int capabilities,
+ long connectTimeMillis,
+ Uri handle,
+ GatewayInfo gatewayInfo,
+ CallServiceDescriptor descriptor,
+ CallServiceDescriptor handoffDescriptor) {
+ this(id, state, disconnectCause, capabilities, connectTimeMillis, handle, gatewayInfo,
+ descriptor, handoffDescriptor, Collections.EMPTY_LIST, null,
+ Collections.EMPTY_LIST);
+ }
/** @hide */
public InCallCall(
@@ -49,7 +69,10 @@
Uri handle,
GatewayInfo gatewayInfo,
CallServiceDescriptor descriptor,
- CallServiceDescriptor handoffDescriptor) {
+ CallServiceDescriptor handoffDescriptor,
+ List<String> conferenceCapableCallIds,
+ String parentCallId,
+ List<String> childCallIds) {
mId = id;
mState = state;
mDisconnectCause = disconnectCause;
@@ -59,6 +82,9 @@
mGatewayInfo = gatewayInfo;
mCurrentCallServiceDescriptor = descriptor;
mHandoffCallServiceDescriptor = handoffDescriptor;
+ mConferenceCapableCallIds = conferenceCapableCallIds;
+ mParentCallId = parentCallId;
+ mChildCallIds = childCallIds;
}
/** The unique ID of the call. */
@@ -112,6 +138,31 @@
return mHandoffCallServiceDescriptor;
}
+ /**
+ * The calls with which this call can conference.
+ * @hide
+ */
+ public List<String> getConferenceCapableCallIds() {
+ return mConferenceCapableCallIds;
+ }
+
+ /**
+ * The conference call to which this call is conferenced. Null if not conferenced.
+ * @hide
+ */
+ public String getParentCallId() {
+ return mParentCallId;
+ }
+
+ /**
+ * The child call-IDs if this call is a conference call. Returns an empty list if this is not
+ * a conference call or if the conference call contains no children.
+ * @hide
+ */
+ public List<String> getChildCallIds() {
+ return mChildCallIds;
+ }
+
/** Responsible for creating InCallCall objects for deserialized Parcels. */
public static final Parcelable.Creator<InCallCall> CREATOR =
new Parcelable.Creator<InCallCall> () {
@@ -127,8 +178,14 @@
GatewayInfo gatewayInfo = source.readParcelable(classLoader);
CallServiceDescriptor descriptor = source.readParcelable(classLoader);
CallServiceDescriptor handoffDescriptor = source.readParcelable(classLoader);
+ List<String> conferenceCapableCallIds = new ArrayList<>();
+ source.readList(conferenceCapableCallIds, classLoader);
+ String parentCallId = source.readString();
+ List<String> childCallIds = new ArrayList<>();
+ source.readList(childCallIds, classLoader);
return new InCallCall(id, state, disconnectCause, capabilities, connectTimeMillis,
- handle, gatewayInfo, descriptor, handoffDescriptor);
+ handle, gatewayInfo, descriptor, handoffDescriptor, conferenceCapableCallIds,
+ parentCallId, childCallIds);
}
@Override
@@ -155,5 +212,8 @@
destination.writeParcelable(mGatewayInfo, 0);
destination.writeParcelable(mCurrentCallServiceDescriptor, 0);
destination.writeParcelable(mHandoffCallServiceDescriptor, 0);
+ destination.writeList(mConferenceCapableCallIds);
+ destination.writeString(mParentCallId);
+ destination.writeList(mChildCallIds);
}
}
diff --git a/telecomm/java/com/android/internal/telecomm/ICallService.aidl b/telecomm/java/com/android/internal/telecomm/ICallService.aidl
index cc0641c..771a3ae 100644
--- a/telecomm/java/com/android/internal/telecomm/ICallService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ICallService.aidl
@@ -55,4 +55,8 @@
void playDtmfTone(String callId, char digit);
void stopDtmfTone(String callId);
+
+ void addToConference(String conferenceCallId, in List<String> callIds);
+
+ void splitFromConference(String conferenceCallId, String callId);
}
diff --git a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
index 6d36494..a92b176 100644
--- a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
@@ -45,4 +45,10 @@
void setOnHold(String callId);
void setRequestingRingback(String callId, boolean ringing);
+
+ void setCanConferenceWith(String callId, in List<String> conferenceCapableCallIds);
+
+ void setIsConferenced(String conferenceCallId, String callId, boolean isConferenced);
+
+ void removeCall(String callId);
}
diff --git a/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl
index 512e898..6a27217 100644
--- a/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl
@@ -47,4 +47,8 @@
void postDialContinue(String callId);
void handoffCall(String callId);
+
+ void conferenceWith(String callId, String callIdToConference);
+
+ void splitFromConference(String callId);
}
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
index c1fa74f..d6f8118 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
@@ -82,7 +82,7 @@
Log.d(TAG, "Creating session for package " + mContext.getBasePackageName());
mSession = man.createSession("OneMedia");
mSession.addCallback(mCallback);
- mSession.addTransportControlsCallback(new TransportListener());
+ mSession.addTransportControlsCallback(new TransportCallback());
mSession.setPlaybackState(mPlaybackState);
mSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
mSession.setRouteOptions(mRouteOptions);
@@ -255,7 +255,7 @@
}
}
- private class TransportListener extends MediaSession.TransportControlsCallback {
+ private class TransportCallback extends MediaSession.TransportControlsCallback {
@Override
public void onPlay() {
mRenderer.onPlay();