Merge "Move zen-mode systemui affordance to quick settings."
diff --git a/api/current.txt b/api/current.txt
index 0a0f148..eb6de96 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6772,6 +6772,7 @@
field public static final java.lang.String CATEGORY_HOME = "android.intent.category.HOME";
field public static final java.lang.String CATEGORY_INFO = "android.intent.category.INFO";
field public static final java.lang.String CATEGORY_LAUNCHER = "android.intent.category.LAUNCHER";
+ field public static final java.lang.String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER";
field public static final java.lang.String CATEGORY_LE_DESK_DOCK = "android.intent.category.LE_DESK_DOCK";
field public static final java.lang.String CATEGORY_MONKEY = "android.intent.category.MONKEY";
field public static final java.lang.String CATEGORY_OPENABLE = "android.intent.category.OPENABLE";
@@ -13815,6 +13816,7 @@
field public static final int RATING_4_STARS = 4; // 0x4
field public static final int RATING_5_STARS = 5; // 0x5
field public static final int RATING_HEART = 1; // 0x1
+ field public static final int RATING_NONE = 0; // 0x0
field public static final int RATING_PERCENTAGE = 6; // 0x6
field public static final int RATING_THUMB_UP_DOWN = 2; // 0x2
}
@@ -14455,34 +14457,76 @@
package android.media.session {
public final class MediaController {
- ctor public MediaController(android.media.session.MediaSessionToken);
method public void addCallback(android.media.session.MediaController.Callback);
method public void addCallback(android.media.session.MediaController.Callback, android.os.Handler);
+ method public static android.media.session.MediaController fromToken(android.media.session.MediaSessionToken);
+ method public android.media.session.TransportController getTransportController();
method public void removeCallback(android.media.session.MediaController.Callback);
- method public void sendCommand(java.lang.String, android.os.Bundle);
+ method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
method public void sendMediaButton(int);
}
public static abstract class MediaController.Callback {
ctor public MediaController.Callback();
method public void onEvent(java.lang.String, android.os.Bundle);
- method public void onMetadataUpdate(android.os.Bundle);
- method public void onPlaybackStateChange(int);
method public void onRouteChanged(android.os.Bundle);
}
+ public final class MediaMetadata implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.graphics.Bitmap getBitmap(java.lang.String);
+ method public long getLong(java.lang.String);
+ method public android.media.Rating getRating(java.lang.String);
+ method public java.lang.String getString(java.lang.String);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final java.lang.String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
+ field public static final java.lang.String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
+ field public static final java.lang.String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
+ field public static final java.lang.String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
+ field public static final java.lang.String METADATA_KEY_ART = "android.media.metadata.ART";
+ field public static final java.lang.String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
+ field public static final java.lang.String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
+ field public static final java.lang.String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
+ field public static final java.lang.String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
+ field public static final java.lang.String METADATA_KEY_DATE = "android.media.metadata.DATE";
+ field public static final java.lang.String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
+ field public static final java.lang.String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
+ field public static final java.lang.String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
+ field public static final java.lang.String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
+ field public static final java.lang.String METADATA_KEY_RATING = "android.media.metadata.RATING";
+ field public static final java.lang.String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
+ field public static final java.lang.String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
+ field public static final java.lang.String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
+ field public static final java.lang.String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
+ field public static final java.lang.String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
+ }
+
+ public static final class MediaMetadata.Builder {
+ ctor public MediaMetadata.Builder();
+ ctor public MediaMetadata.Builder(android.media.session.MediaMetadata);
+ method public android.media.session.MediaMetadata build();
+ method public android.media.session.MediaMetadata.Builder putBitmap(java.lang.String, android.graphics.Bitmap);
+ method public android.media.session.MediaMetadata.Builder putLong(java.lang.String, long);
+ method public android.media.session.MediaMetadata.Builder putRating(java.lang.String, android.media.Rating);
+ method public android.media.session.MediaMetadata.Builder putString(java.lang.String, java.lang.String);
+ }
+
public final class MediaSession {
method public void addCallback(android.media.session.MediaSession.Callback);
method public void addCallback(android.media.session.MediaSession.Callback, android.os.Handler);
method public android.media.session.MediaSessionToken getSessionToken();
+ method public android.media.session.TransportPerformer getTransportPerformer();
+ method public void publish();
method public void release();
method public void removeCallback(android.media.session.MediaSession.Callback);
- method public void setPlaybackState(int);
+ method public void sendEvent(java.lang.String, android.os.Bundle);
+ method public android.media.session.TransportPerformer setTransportPerformerEnabled();
}
public static abstract class MediaSession.Callback {
ctor public MediaSession.Callback();
- method public void onCommand(java.lang.String, android.os.Bundle);
+ method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
method public void onMediaButton(android.content.Intent);
method public void onRequestRouteChange(android.os.Bundle);
}
@@ -14498,6 +14542,137 @@
field public static final android.os.Parcelable.Creator CREATOR;
}
+ public final class PlaybackState implements android.os.Parcelable {
+ ctor public PlaybackState();
+ ctor public PlaybackState(android.media.session.PlaybackState);
+ method public int describeContents();
+ method public long getActions();
+ method public long getBufferPosition();
+ method public java.lang.String getErrorMessage();
+ method public long getPosition();
+ method public float getSpeed();
+ method public int getState();
+ method public void setActions(long);
+ method public void setBufferPosition(long);
+ method public void setErrorMessage(java.lang.String);
+ method public void setPosition(long);
+ method public void setSpeed(float);
+ method public void setState(int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final long ACTION_FASTFORWARD = 64L; // 0x40L
+ field public static final long ACTION_NEXT_ITEM = 32L; // 0x20L
+ field public static final long ACTION_PAUSE = 2L; // 0x2L
+ field public static final long ACTION_PLAY = 4L; // 0x4L
+ field public static final long ACTION_PREVIOUS_ITEM = 16L; // 0x10L
+ field public static final long ACTION_RATING = 128L; // 0x80L
+ field public static final long ACTION_REWIND = 8L; // 0x8L
+ field public static final long ACTION_SEEK_TO = 256L; // 0x100L
+ field public static final long ACTION_STOP = 1L; // 0x1L
+ field public static final android.os.Parcelable.Creator CREATOR;
+ field public static final int PLAYSTATE_BUFFERING = 6; // 0x6
+ field public static final int PLAYSTATE_ERROR = 7; // 0x7
+ field public static final int PLAYSTATE_FAST_FORWARDING = 4; // 0x4
+ field public static final int PLAYSTATE_NONE = 0; // 0x0
+ field public static final int PLAYSTATE_PAUSED = 2; // 0x2
+ field public static final int PLAYSTATE_PLAYING = 3; // 0x3
+ field public static final int PLAYSTATE_REWINDING = 5; // 0x5
+ field public static final int PLAYSTATE_STOPPED = 1; // 0x1
+ }
+
+ public final class RouteInterface {
+ method public void addListener(android.media.session.RouteInterface.EventListener);
+ method public void addListener(android.media.session.RouteInterface.EventListener, android.os.Handler);
+ method public void removeListener(android.media.session.RouteInterface.EventListener);
+ method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+ }
+
+ public static abstract class RouteInterface.EventListener {
+ ctor public RouteInterface.EventListener();
+ method public abstract void onEvent(java.lang.String, android.os.Bundle);
+ }
+
+ public static abstract class RouteInterface.Stub {
+ ctor public RouteInterface.Stub();
+ method public abstract java.lang.String getName();
+ method public abstract void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+ method public final void sendEvent(android.media.session.MediaSession, java.lang.String, android.os.Bundle);
+ }
+
+ public final class RouteTransportControls {
+ method public void addListener(android.media.session.RouteTransportControls.Listener);
+ method public void addListener(android.media.session.RouteTransportControls.Listener, android.os.Handler);
+ method public void fastForward(float);
+ method public static android.media.session.RouteTransportControls from(android.media.session.MediaController);
+ method public void getCapabilities(android.os.ResultReceiver);
+ method public void getCurrentPosition(android.os.ResultReceiver);
+ method public void pause();
+ method public void play();
+ method public void removeListener(android.media.session.RouteTransportControls.Listener);
+ field public static final java.lang.String NAME = "android.media.session.RouteTransportControls";
+ }
+
+ public static abstract class RouteTransportControls.Listener {
+ ctor public RouteTransportControls.Listener();
+ method public void onMetadataUpdate(android.os.Bundle);
+ method public void onPlaybackStateChange(int);
+ }
+
+ public static abstract class RouteTransportControls.Stub extends android.media.session.RouteInterface.Stub {
+ ctor public RouteTransportControls.Stub(android.media.session.MediaSession);
+ method public void fastForward(float);
+ method public long getCapabilities();
+ method public long getCurrentPosition();
+ method public java.lang.String getName();
+ method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
+ method public final void updatePlaybackState(int);
+ }
+
+ public final class TransportController {
+ method public void addStateListener(android.media.session.TransportController.TransportStateListener);
+ method public void addStateListener(android.media.session.TransportController.TransportStateListener, android.os.Handler);
+ method public void fastForward();
+ method public android.media.session.MediaMetadata getMetadata();
+ method public android.media.session.PlaybackState getPlaybackState();
+ method public int getRatingType();
+ method public void next();
+ method public void pause();
+ method public void play();
+ method public void previous();
+ method public void rate(android.media.Rating);
+ method public void removeStateListener(android.media.session.TransportController.TransportStateListener);
+ method public void rewind();
+ method public void seekTo(long);
+ method public void stop();
+ }
+
+ public static abstract class TransportController.TransportStateListener {
+ ctor public TransportController.TransportStateListener();
+ method public void onMetadataChanged(android.media.session.MediaMetadata);
+ method public void onPlaybackStateChanged(android.media.session.PlaybackState);
+ }
+
+ public final class TransportPerformer {
+ method public void addListener(android.media.session.TransportPerformer.Listener);
+ method public void addListener(android.media.session.TransportPerformer.Listener, android.os.Handler);
+ method public void removeListener(android.media.session.TransportPerformer.Listener);
+ method public final void setMetadata(android.media.session.MediaMetadata);
+ method public final void setPlaybackState(android.media.session.PlaybackState);
+ }
+
+ public static abstract class TransportPerformer.Listener {
+ ctor public TransportPerformer.Listener();
+ method public void onFastForward();
+ method public void onNext();
+ method public void onPause();
+ method public void onPlay();
+ method public void onPrevious();
+ method public void onRate(android.media.Rating);
+ method public void onRewind();
+ method public void onRouteFocusChange(int);
+ method public void onSeekTo(long);
+ method public void onStop();
+ }
+
}
package android.mtp {
@@ -38419,6 +38594,11 @@
method public abstract java.net.SocketImpl createSocketImpl();
}
+ public abstract interface SocketOption {
+ method public abstract java.lang.String name();
+ method public abstract java.lang.Class<T> type();
+ }
+
public abstract interface SocketOptions {
method public abstract java.lang.Object getOption(int) throws java.net.SocketException;
method public abstract void setOption(int, java.lang.Object) throws java.net.SocketException;
@@ -38449,6 +38629,21 @@
ctor public SocketTimeoutException(java.lang.String);
}
+ public final class StandardSocketOptions {
+ ctor public StandardSocketOptions();
+ field public static final java.net.SocketOption IP_MULTICAST_IF;
+ field public static final java.net.SocketOption IP_MULTICAST_LOOP;
+ field public static final java.net.SocketOption IP_MULTICAST_TTL;
+ field public static final java.net.SocketOption IP_TOS;
+ field public static final java.net.SocketOption SO_BROADCAST;
+ field public static final java.net.SocketOption SO_KEEPALIVE;
+ field public static final java.net.SocketOption SO_LINGER;
+ field public static final java.net.SocketOption SO_RCVBUF;
+ field public static final java.net.SocketOption SO_REUSEADDR;
+ field public static final java.net.SocketOption SO_SNDBUF;
+ field public static final java.net.SocketOption TCP_NODELAY;
+ }
+
public final class URI implements java.lang.Comparable java.io.Serializable {
ctor public URI(java.lang.String) throws java.net.URISyntaxException;
ctor public URI(java.lang.String, java.lang.String, java.lang.String) throws java.net.URISyntaxException;
@@ -38544,6 +38739,7 @@
method public java.lang.Object getContent(java.lang.Class[]) throws java.io.IOException;
method public java.lang.String getContentEncoding();
method public int getContentLength();
+ method public long getContentLengthLong();
method public java.lang.String getContentType();
method public long getDate();
method public static boolean getDefaultAllowUserInteraction();
@@ -38558,6 +38754,7 @@
method public long getHeaderFieldDate(java.lang.String, long);
method public int getHeaderFieldInt(java.lang.String, int);
method public java.lang.String getHeaderFieldKey(int);
+ method public long getHeaderFieldLong(java.lang.String, long);
method public java.util.Map<java.lang.String, java.util.List<java.lang.String>> getHeaderFields();
method public long getIfModifiedSince();
method public java.io.InputStream getInputStream() throws java.io.IOException;
@@ -38908,6 +39105,10 @@
package java.nio.channels {
+ public class AlreadyBoundException extends java.lang.IllegalStateException {
+ ctor public AlreadyBoundException();
+ }
+
public class AlreadyConnectedException extends java.lang.IllegalStateException {
ctor public AlreadyConnectedException();
}
@@ -38957,8 +39158,11 @@
public abstract class DatagramChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
ctor protected DatagramChannel(java.nio.channels.spi.SelectorProvider);
+ method public java.nio.channels.DatagramChannel bind(java.net.SocketAddress) throws java.io.IOException;
method public abstract java.nio.channels.DatagramChannel connect(java.net.SocketAddress) throws java.io.IOException;
method public abstract java.nio.channels.DatagramChannel disconnect() throws java.io.IOException;
+ method public java.net.SocketAddress getLocalAddress() throws java.io.IOException;
+ method public T getOption(java.net.SocketOption<T>) throws java.io.IOException;
method public abstract boolean isConnected();
method public static java.nio.channels.DatagramChannel open() throws java.io.IOException;
method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
@@ -38966,14 +39170,16 @@
method public final synchronized long read(java.nio.ByteBuffer[]) throws java.io.IOException;
method public abstract java.net.SocketAddress receive(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract int send(java.nio.ByteBuffer, java.net.SocketAddress) throws java.io.IOException;
+ method public java.nio.channels.DatagramChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
method public abstract java.net.DatagramSocket socket();
+ method public java.util.Set<java.net.SocketOption<?>> supportedOptions();
method public final int validOps();
method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract long write(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
method public final synchronized long write(java.nio.ByteBuffer[]) throws java.io.IOException;
}
- public abstract class FileChannel extends java.nio.channels.spi.AbstractInterruptibleChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
+ public abstract class FileChannel extends java.nio.channels.spi.AbstractInterruptibleChannel implements java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel java.nio.channels.SeekableByteChannel {
ctor protected FileChannel();
method public abstract void force(boolean) throws java.io.IOException;
method public final java.nio.channels.FileLock lock() throws java.io.IOException;
@@ -39037,6 +39243,14 @@
method public abstract void close() throws java.io.IOException;
}
+ public abstract interface NetworkChannel implements java.lang.AutoCloseable java.nio.channels.Channel java.io.Closeable {
+ method public abstract java.nio.channels.NetworkChannel bind(java.net.SocketAddress) throws java.io.IOException;
+ method public abstract java.net.SocketAddress getLocalAddress() throws java.io.IOException;
+ method public abstract T getOption(java.net.SocketOption<T>) throws java.io.IOException;
+ method public abstract java.nio.channels.NetworkChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
+ method public abstract java.util.Set<java.net.SocketOption<?>> supportedOptions();
+ }
+
public class NoConnectionPendingException extends java.lang.IllegalStateException {
ctor public NoConnectionPendingException();
}
@@ -39087,6 +39301,15 @@
method public abstract long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
}
+ public abstract interface SeekableByteChannel implements java.nio.channels.ByteChannel {
+ method public abstract long position() throws java.io.IOException;
+ method public abstract java.nio.channels.SeekableByteChannel position(long) throws java.io.IOException;
+ method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
+ method public abstract long size() throws java.io.IOException;
+ method public abstract java.nio.channels.SeekableByteChannel truncate(long) throws java.io.IOException;
+ method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
+ }
+
public abstract class SelectableChannel extends java.nio.channels.spi.AbstractInterruptibleChannel implements java.nio.channels.Channel {
ctor protected SelectableChannel();
method public abstract java.lang.Object blockingLock();
@@ -39135,18 +39358,27 @@
method public abstract java.nio.channels.Selector wakeup();
}
- public abstract class ServerSocketChannel extends java.nio.channels.spi.AbstractSelectableChannel {
+ public abstract class ServerSocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.NetworkChannel {
ctor protected ServerSocketChannel(java.nio.channels.spi.SelectorProvider);
method public abstract java.nio.channels.SocketChannel accept() throws java.io.IOException;
+ method public final java.nio.channels.ServerSocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
+ method public java.nio.channels.ServerSocketChannel bind(java.net.SocketAddress, int) throws java.io.IOException;
+ method public java.net.SocketAddress getLocalAddress() throws java.io.IOException;
+ method public T getOption(java.net.SocketOption<T>) throws java.io.IOException;
method public static java.nio.channels.ServerSocketChannel open() throws java.io.IOException;
+ method public java.nio.channels.ServerSocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
method public abstract java.net.ServerSocket socket();
+ method public java.util.Set<java.net.SocketOption<?>> supportedOptions();
method public final int validOps();
}
- public abstract class SocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.ScatteringByteChannel {
+ public abstract class SocketChannel extends java.nio.channels.spi.AbstractSelectableChannel implements java.nio.channels.ByteChannel java.nio.channels.GatheringByteChannel java.nio.channels.NetworkChannel java.nio.channels.ScatteringByteChannel {
ctor protected SocketChannel(java.nio.channels.spi.SelectorProvider);
+ method public java.nio.channels.SocketChannel bind(java.net.SocketAddress) throws java.io.IOException;
method public abstract boolean connect(java.net.SocketAddress) throws java.io.IOException;
method public abstract boolean finishConnect() throws java.io.IOException;
+ method public java.net.SocketAddress getLocalAddress() throws java.io.IOException;
+ method public T getOption(java.net.SocketOption<T>) throws java.io.IOException;
method public abstract boolean isConnected();
method public abstract boolean isConnectionPending();
method public static java.nio.channels.SocketChannel open() throws java.io.IOException;
@@ -39154,7 +39386,9 @@
method public abstract int read(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract long read(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
method public final synchronized long read(java.nio.ByteBuffer[]) throws java.io.IOException;
+ method public java.nio.channels.SocketChannel setOption(java.net.SocketOption<T>, T) throws java.io.IOException;
method public abstract java.net.Socket socket();
+ method public java.util.Set<java.net.SocketOption<?>> supportedOptions();
method public final int validOps();
method public abstract int write(java.nio.ByteBuffer) throws java.io.IOException;
method public abstract long write(java.nio.ByteBuffer[], int, int) throws java.io.IOException;
@@ -45204,6 +45438,7 @@
method public final int getWaitQueueLength(java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject);
method public final java.util.Collection<java.lang.Thread> getWaitingThreads(java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject);
method public final boolean hasContended();
+ method public final boolean hasQueuedPredecessors();
method public final boolean hasQueuedThreads();
method public final boolean hasWaiters(java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject);
method protected boolean isHeldExclusively();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 9cfd85a..69ada6a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -161,7 +161,7 @@
private static final int LOG_ON_PAUSE_CALLED = 30021;
private static final int LOG_ON_RESUME_CALLED = 30022;
- static ContextImpl mSystemContext = null;
+ private ContextImpl mSystemContext;
static IPackageManager sPackageManager;
@@ -1709,7 +1709,7 @@
? mBoundApplication.processName : null)
+ ")");
packageInfo =
- new LoadedApk(this, aInfo, compatInfo, this, baseLoader,
+ new LoadedApk(this, aInfo, compatInfo, baseLoader,
securityViolation, includeCode &&
(aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0);
if (includeCode) {
@@ -1762,26 +1762,15 @@
public ContextImpl getSystemContext() {
synchronized (this) {
if (mSystemContext == null) {
- ContextImpl context =
- ContextImpl.createSystemContext(this);
- LoadedApk info = new LoadedApk(this, "android", context, null,
- CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO);
- context.init(info, null, this);
- context.getResources().updateConfiguration(mResourcesManager.getConfiguration(),
- mResourcesManager.getDisplayMetricsLocked(Display.DEFAULT_DISPLAY));
- mSystemContext = context;
- //Slog.i(TAG, "Created system resources " + context.getResources()
- // + ": " + context.getResources().getConfiguration());
+ mSystemContext = ContextImpl.createSystemContext(this);
}
+ return mSystemContext;
}
- return mSystemContext;
}
public void installSystemApplicationInfo(ApplicationInfo info) {
synchronized (this) {
- ContextImpl context = getSystemContext();
- context.init(new LoadedApk(this, "android", context, info,
- CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO), null, this);
+ getSystemContext().installSystemApplicationInfo(info);
// give ourselves a default profiler
mProfiler = new Profiler();
@@ -2268,8 +2257,7 @@
private Context createBaseContextForActivity(ActivityClientRecord r,
final Activity activity) {
- ContextImpl appContext = new ContextImpl();
- appContext.init(r.packageInfo, r.token, this);
+ ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token);
appContext.setOuterContext(activity);
Context baseContext = appContext;
@@ -2567,8 +2555,7 @@
agent = (BackupAgent) cl.loadClass(classname).newInstance();
// set up the agent's context
- ContextImpl context = new ContextImpl();
- context.init(packageInfo, null, this);
+ ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(agent);
agent.attach(context);
@@ -2640,11 +2627,10 @@
try {
if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
- ContextImpl context = new ContextImpl();
- context.init(packageInfo, null, this);
+ ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
+ context.setOuterContext(service);
Application app = packageInfo.makeApplication(false, mInstrumentation);
- context.setOuterContext(service);
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
service.onCreate();
@@ -4249,8 +4235,7 @@
}
updateDefaultDensity();
- final ContextImpl appContext = new ContextImpl();
- appContext.init(data.info, null, this);
+ final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
if (!Process.isIsolated()) {
final File cacheDir = appContext.getCacheDir();
@@ -4366,8 +4351,7 @@
instrApp.nativeLibraryDir = ii.nativeLibraryDir;
LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
appContext.getClassLoader(), false, true);
- ContextImpl instrContext = new ContextImpl();
- instrContext.init(pi, null, this);
+ ContextImpl instrContext = ContextImpl.createAppContext(this, pi);
try {
java.lang.ClassLoader cl = instrContext.getClassLoader();
@@ -4982,8 +4966,8 @@
UserHandle.myUserId());
try {
mInstrumentation = new Instrumentation();
- ContextImpl context = new ContextImpl();
- context.init(getSystemContext().mPackageInfo, null, this);
+ ContextImpl context = ContextImpl.createAppContext(
+ this, getSystemContext().mPackageInfo);
Application app = Instrumentation.newApplication(Application.class, context);
mAllApplications.add(app);
mInitialApplication = app;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 9b3643c..0351292 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -185,22 +185,31 @@
*/
private static ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>> sSharedPrefs;
- /*package*/ LoadedApk mPackageInfo;
- private String mBasePackageName;
- private String mOpPackageName;
- private Resources mResources;
- /*package*/ ActivityThread mMainThread;
+ final ActivityThread mMainThread;
+ final LoadedApk mPackageInfo;
+
+ private final IBinder mActivityToken;
+
+ private final UserHandle mUser;
+
+ private final ApplicationContentResolver mContentResolver;
+
+ private final String mBasePackageName;
+ private final String mOpPackageName;
+
+ private final ResourcesManager mResourcesManager;
+ private final Resources mResources;
+ private final Display mDisplay; // may be null if default display
+ private final DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments();
+ private final Configuration mOverrideConfiguration;
+
+ private final boolean mRestricted;
+
private Context mOuterContext;
- private IBinder mActivityToken = null;
- private ApplicationContentResolver mContentResolver;
private int mThemeResource = 0;
private Resources.Theme mTheme = null;
private PackageManager mPackageManager;
- private Display mDisplay; // may be null if default display
private Context mReceiverRestrictedContext = null;
- private boolean mRestricted;
- private UserHandle mUser;
- private ResourcesManager mResourcesManager;
private final Object mSync = new Object();
@@ -222,8 +231,6 @@
private static final String[] EMPTY_FILE_LIST = {};
- final private DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments();
-
/**
* Override this class when the system service constructor needs a
* ContextImpl. Else, use StaticServiceFetcher below.
@@ -1887,20 +1894,17 @@
@Override
public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
throws NameNotFoundException {
+ final boolean restricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED;
if (packageName.equals("system") || packageName.equals("android")) {
- final ContextImpl context = new ContextImpl(mMainThread.getSystemContext());
- context.mRestricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED;
- context.init(mPackageInfo, null, mMainThread, mResources, mBasePackageName, user);
- return context;
+ return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
+ user, restricted, mDisplay, mOverrideConfiguration);
}
- LoadedApk pi =
- mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(), flags,
- user.getIdentifier());
+ LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
+ flags, user.getIdentifier());
if (pi != null) {
- ContextImpl c = new ContextImpl();
- c.mRestricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED;
- c.init(pi, null, mMainThread, mResources, mBasePackageName, user);
+ ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken,
+ user, restricted, mDisplay, mOverrideConfiguration);
if (c.mResources != null) {
return c;
}
@@ -1908,7 +1912,7 @@
// Should be a better exception.
throw new PackageManager.NameNotFoundException(
- "Application package " + packageName + " not found");
+ "Application package " + packageName + " not found");
}
@Override
@@ -1917,12 +1921,8 @@
throw new IllegalArgumentException("overrideConfiguration must not be null");
}
- ContextImpl c = new ContextImpl();
- c.init(mPackageInfo, null, mMainThread);
- c.mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(),
- mPackageInfo.getOverlayDirs(), getDisplayId(), overrideConfiguration,
- mResources.getCompatibilityInfo(), mActivityToken);
- return c;
+ return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
+ mUser, mRestricted, mDisplay, overrideConfiguration);
}
@Override
@@ -1931,15 +1931,8 @@
throw new IllegalArgumentException("display must not be null");
}
- int displayId = display.getDisplayId();
-
- ContextImpl context = new ContextImpl();
- context.init(mPackageInfo, null, mMainThread);
- context.mDisplay = display;
- DisplayAdjustments daj = getDisplayAdjustments(displayId);
- context.mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(),
- mPackageInfo.getOverlayDirs(), displayId, null, daj.getCompatibilityInfo(), null);
- return context;
+ return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
+ mUser, mRestricted, display, mOverrideConfiguration);
}
private int getDisplayId() {
@@ -1981,43 +1974,76 @@
}
static ContextImpl createSystemContext(ActivityThread mainThread) {
- final ContextImpl context = new ContextImpl();
- context.init(Resources.getSystem(), mainThread, Process.myUserHandle());
+ LoadedApk packageInfo = new LoadedApk(mainThread);
+ ContextImpl context = new ContextImpl(null, mainThread,
+ packageInfo, null, null, false, null, null);
+ context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
+ context.mResourcesManager.getDisplayMetricsLocked(Display.DEFAULT_DISPLAY));
return context;
}
- ContextImpl() {
+ static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
+ if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
+ return new ContextImpl(null, mainThread,
+ packageInfo, null, null, false, null, null);
+ }
+
+ static ContextImpl createActivityContext(ActivityThread mainThread,
+ LoadedApk packageInfo, IBinder activityToken) {
+ if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
+ if (activityToken == null) throw new IllegalArgumentException("activityInfo");
+ return new ContextImpl(null, mainThread,
+ packageInfo, activityToken, null, false, null, null);
+ }
+
+ private ContextImpl(ContextImpl container, ActivityThread mainThread,
+ LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,
+ Display display, Configuration overrideConfiguration) {
mOuterContext = this;
- }
- /**
- * Create a new ApplicationContext from an existing one. The new one
- * works and operates the same as the one it is copying.
- *
- * @param context Existing application context.
- */
- public ContextImpl(ContextImpl context) {
- mPackageInfo = context.mPackageInfo;
- mBasePackageName = context.mBasePackageName;
- mOpPackageName = context.mOpPackageName;
- mResources = context.mResources;
- mMainThread = context.mMainThread;
- mContentResolver = context.mContentResolver;
- mUser = context.mUser;
- mDisplay = context.mDisplay;
- mOuterContext = this;
- mDisplayAdjustments.setCompatibilityInfo(mPackageInfo.getCompatibilityInfo());
- }
+ mMainThread = mainThread;
+ mActivityToken = activityToken;
+ mRestricted = restricted;
- final void init(LoadedApk packageInfo, IBinder activityToken, ActivityThread mainThread) {
- init(packageInfo, activityToken, mainThread, null, null, Process.myUserHandle());
- }
+ if (user == null) {
+ user = Process.myUserHandle();
+ }
+ mUser = user;
- final void init(LoadedApk packageInfo, IBinder activityToken, ActivityThread mainThread,
- Resources container, String basePackageName, UserHandle user) {
mPackageInfo = packageInfo;
- if (basePackageName != null) {
- mBasePackageName = mOpPackageName = basePackageName;
+ mContentResolver = new ApplicationContentResolver(this, mainThread, user);
+ mResourcesManager = ResourcesManager.getInstance();
+ mDisplay = display;
+ mOverrideConfiguration = overrideConfiguration;
+
+ final int displayId = getDisplayId();
+ CompatibilityInfo compatInfo = null;
+ if (container != null) {
+ compatInfo = container.getDisplayAdjustments(displayId).getCompatibilityInfo();
+ }
+ if (compatInfo == null && displayId == Display.DEFAULT_DISPLAY) {
+ compatInfo = packageInfo.getCompatibilityInfo();
+ }
+ mDisplayAdjustments.setCompatibilityInfo(compatInfo);
+ mDisplayAdjustments.setActivityToken(activityToken);
+
+ Resources resources = packageInfo.getResources(mainThread);
+ if (resources != null) {
+ if (activityToken != null
+ || displayId != Display.DEFAULT_DISPLAY
+ || overrideConfiguration != null
+ || (compatInfo != null && compatInfo.applicationScale
+ != resources.getCompatibilityInfo().applicationScale)) {
+ resources = mResourcesManager.getTopLevelResources(
+ packageInfo.getResDir(), packageInfo.getOverlayDirs(), displayId,
+ overrideConfiguration, compatInfo, activityToken);
+ }
+ }
+ mResources = resources;
+
+ if (container != null) {
+ mBasePackageName = container.mBasePackageName;
+ mOpPackageName = container.mOpPackageName;
} else {
mBasePackageName = packageInfo.mPackageName;
ApplicationInfo ainfo = packageInfo.getApplicationInfo();
@@ -2031,45 +2057,10 @@
mOpPackageName = mBasePackageName;
}
}
- mResources = mPackageInfo.getResources(mainThread);
- mResourcesManager = ResourcesManager.getInstance();
-
- CompatibilityInfo compatInfo =
- container == null ? null : container.getCompatibilityInfo();
- if (mResources != null &&
- ((compatInfo != null && compatInfo.applicationScale !=
- mResources.getCompatibilityInfo().applicationScale)
- || activityToken != null)) {
- if (DEBUG) {
- Log.d(TAG, "loaded context has different scaling. Using container's" +
- " compatiblity info:" + container.getDisplayMetrics());
- }
- if (compatInfo == null) {
- compatInfo = packageInfo.getCompatibilityInfo();
- }
- mDisplayAdjustments.setCompatibilityInfo(compatInfo);
- mDisplayAdjustments.setActivityToken(activityToken);
- mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(),
- mPackageInfo.getOverlayDirs(), Display.DEFAULT_DISPLAY, null, compatInfo,
- activityToken);
- } else {
- mDisplayAdjustments.setCompatibilityInfo(packageInfo.getCompatibilityInfo());
- mDisplayAdjustments.setActivityToken(activityToken);
- }
- mMainThread = mainThread;
- mActivityToken = activityToken;
- mContentResolver = new ApplicationContentResolver(this, mainThread, user);
- mUser = user;
}
- final void init(Resources resources, ActivityThread mainThread, UserHandle user) {
- mPackageInfo = null;
- mBasePackageName = null;
- mOpPackageName = null;
- mResources = resources;
- mMainThread = mainThread;
- mContentResolver = new ApplicationContentResolver(this, mainThread, user);
- mUser = user;
+ void installSystemApplicationInfo(ApplicationInfo info) {
+ mPackageInfo.installSystemApplicationInfo(info);
}
final void scheduleFinalCleanup(String who, String what) {
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 0115d1b..d409352 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -72,7 +72,7 @@
private static final String TAG = "LoadedApk";
private final ActivityThread mActivityThread;
- private final ApplicationInfo mApplicationInfo;
+ private ApplicationInfo mApplicationInfo;
final String mPackageName;
private final String mAppDir;
private final String mResDir;
@@ -111,8 +111,7 @@
* so MUST NOT call back out to the activity manager.
*/
public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,
- CompatibilityInfo compatInfo,
- ActivityThread mainThread, ClassLoader baseLoader,
+ CompatibilityInfo compatInfo, ClassLoader baseLoader,
boolean securityViolation, boolean includeCode) {
mActivityThread = activityThread;
mApplicationInfo = aInfo;
@@ -134,31 +133,17 @@
mSecurityViolation = securityViolation;
mIncludeCode = includeCode;
mDisplayAdjustments.setCompatibilityInfo(compatInfo);
-
- if (mAppDir == null) {
- if (ActivityThread.mSystemContext == null) {
- ActivityThread.mSystemContext =
- ContextImpl.createSystemContext(mainThread);
- ResourcesManager resourcesManager = ResourcesManager.getInstance();
- ActivityThread.mSystemContext.getResources().updateConfiguration(
- resourcesManager.getConfiguration(),
- resourcesManager.getDisplayMetricsLocked(
- Display.DEFAULT_DISPLAY, mDisplayAdjustments), compatInfo);
- //Slog.i(TAG, "Created system resources "
- // + mSystemContext.getResources() + ": "
- // + mSystemContext.getResources().getConfiguration());
- }
- mClassLoader = ActivityThread.mSystemContext.getClassLoader();
- mResources = ActivityThread.mSystemContext.getResources();
- }
}
- public LoadedApk(ActivityThread activityThread, String name,
- Context systemContext, ApplicationInfo info, CompatibilityInfo compatInfo) {
+ /**
+ * Create information about the system package.
+ * Must call {@link #installSystemApplicationInfo} later.
+ */
+ LoadedApk(ActivityThread activityThread) {
mActivityThread = activityThread;
- mApplicationInfo = info != null ? info : new ApplicationInfo();
- mApplicationInfo.packageName = name;
- mPackageName = name;
+ mApplicationInfo = new ApplicationInfo();
+ mApplicationInfo.packageName = "android";
+ mPackageName = "android";
mAppDir = null;
mResDir = null;
mOverlayDirs = null;
@@ -169,9 +154,16 @@
mBaseClassLoader = null;
mSecurityViolation = false;
mIncludeCode = true;
- mClassLoader = systemContext.getClassLoader();
- mResources = systemContext.getResources();
- mDisplayAdjustments.setCompatibilityInfo(compatInfo);
+ mClassLoader = ClassLoader.getSystemClassLoader();
+ mResources = Resources.getSystem();
+ }
+
+ /**
+ * Sets application info about the system package.
+ */
+ void installSystemApplicationInfo(ApplicationInfo info) {
+ assert info.packageName.equals("android");
+ mApplicationInfo = info;
}
public String getPackageName() {
@@ -513,8 +505,7 @@
try {
java.lang.ClassLoader cl = getClassLoader();
- ContextImpl appContext = new ContextImpl();
- appContext.init(this, null, mActivityThread);
+ ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index f0b7ca8..96479e2 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2768,6 +2768,12 @@
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_LAUNCHER = "android.intent.category.LAUNCHER";
/**
+ * Indicates an activity optimized for Leanback mode, and that should
+ * be displayed in the Leanback launcher.
+ */
+ @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+ public static final String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER";
+ /**
* Provides information about the package it is in; typically used if
* a package does not contain a {@link #CATEGORY_LAUNCHER} to provide
* a front-door to the user without having to be shown in the all apps list.
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 6650fca..5d55143 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -21,11 +21,11 @@
/**
* Class that operates the vibrator on the device.
* <p>
- * If your process exits, any vibration you started with will stop.
+ * If your process exits, any vibration you started will stop.
* </p>
*
* To obtain an instance of the system vibrator, call
- * {@link Context#getSystemService} with {@link Context#VIBRATOR_SERVICE} as argument.
+ * {@link Context#getSystemService} with {@link Context#VIBRATOR_SERVICE} as the argument.
*/
public abstract class Vibrator {
/**
diff --git a/core/java/android/view/ContextThemeWrapper.java b/core/java/android/view/ContextThemeWrapper.java
index 1de9c35..0afbde9 100644
--- a/core/java/android/view/ContextThemeWrapper.java
+++ b/core/java/android/view/ContextThemeWrapper.java
@@ -26,7 +26,6 @@
* wrapped context.
*/
public class ContextThemeWrapper extends ContextWrapper {
- private Context mBase;
private int mThemeResource;
private Resources.Theme mTheme;
private LayoutInflater mInflater;
@@ -39,13 +38,11 @@
public ContextThemeWrapper(Context base, int themeres) {
super(base);
- mBase = base;
mThemeResource = themeres;
}
@Override protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
- mBase = newBase;
}
/**
@@ -109,11 +106,11 @@
@Override public Object getSystemService(String name) {
if (LAYOUT_INFLATER_SERVICE.equals(name)) {
if (mInflater == null) {
- mInflater = LayoutInflater.from(mBase).cloneInContext(this);
+ mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
}
return mInflater;
}
- return mBase.getSystemService(name);
+ return getBaseContext().getSystemService(name);
}
/**
@@ -135,7 +132,7 @@
final boolean first = mTheme == null;
if (first) {
mTheme = getResources().newTheme();
- Resources.Theme theme = mBase.getTheme();
+ Resources.Theme theme = getBaseContext().getTheme();
if (theme != null) {
mTheme.setTo(theme);
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index e51345c..45ca7fc 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -75,7 +75,7 @@
boolean switchToNextInputMethod(in IBinder token, boolean onlyCurrentIme);
boolean shouldOfferSwitchingToNextInputMethod(in IBinder token);
boolean setInputMethodEnabled(String id, boolean enabled);
- oneway void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes);
+ void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes);
int getInputMethodWindowVisibleHeight();
oneway void notifyTextCommitted();
}
diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java
index cc8ce2c..467d42e 100644
--- a/core/java/com/android/internal/widget/SwipeDismissLayout.java
+++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java
@@ -126,6 +126,20 @@
mVelocityTracker.addMovement(ev);
break;
+ case MotionEvent.ACTION_POINTER_DOWN:
+ int actionIndex = ev.getActionIndex();
+ mActiveTouchId = ev.getPointerId(actionIndex);
+ break;
+ case MotionEvent.ACTION_POINTER_UP:
+ actionIndex = ev.getActionIndex();
+ int pointerId = ev.getPointerId(actionIndex);
+ if (pointerId == mActiveTouchId) {
+ // This was our active pointer going up. Choose a new active pointer.
+ int newActionIndex = actionIndex == 0 ? 1 : 0;
+ mActiveTouchId = ev.getPointerId(newActionIndex);
+ }
+ break;
+
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
resetMembers();
@@ -137,6 +151,11 @@
}
int pointerIndex = ev.findPointerIndex(mActiveTouchId);
+ if (pointerIndex == -1) {
+ Log.e(TAG, "Invalid pointer index: ignoring.");
+ mDiscardIntercept = true;
+ break;
+ }
float dx = ev.getRawX() - mDownX;
float x = ev.getX(pointerIndex);
float y = ev.getY(pointerIndex);
@@ -228,11 +247,11 @@
}
private void updateDismiss(MotionEvent ev) {
+ float deltaX = ev.getRawX() - mDownX;
if (!mDismissed) {
mVelocityTracker.addMovement(ev);
mVelocityTracker.computeCurrentVelocity(1000);
- float deltaX = ev.getRawX() - mDownX;
float velocityX = mVelocityTracker.getXVelocity();
float absVelocityX = Math.abs(velocityX);
float absVelocityY = Math.abs(mVelocityTracker.getYVelocity());
@@ -247,6 +266,13 @@
mDismissed = true;
}
}
+ // Check if the user tried to undo this.
+ if (mDismissed && mSwiping) {
+ // Check if the user's finger is actually back
+ if (deltaX < getWidth() / 2) {
+ mDismissed = false;
+ }
+ }
}
/**
diff --git a/core/jni/android_view_DisplayList.cpp b/core/jni/android_view_DisplayList.cpp
index c8952c1..96f52e7 100644
--- a/core/jni/android_view_DisplayList.cpp
+++ b/core/jni/android_view_DisplayList.cpp
@@ -43,7 +43,7 @@
static void android_view_DisplayList_setDisplayListName(JNIEnv* env,
jobject clazz, jlong displayListPtr, jstring name) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
if (name != NULL) {
const char* textArray = env->GetStringUTFChars(name, NULL);
displayList->setName(textArray);
@@ -53,19 +53,19 @@
static void android_view_DisplayList_output(JNIEnv* env,
jobject clazz, jlong displayListPtr) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->output();
}
static jlong android_view_DisplayList_create(JNIEnv* env, jobject clazz) {
- DisplayList* displayList = new DisplayList();
+ RenderNode* displayList = new RenderNode();
return reinterpret_cast<jlong>(displayList);
}
static void android_view_DisplayList_destroyDisplayList(JNIEnv* env,
jobject clazz, jlong displayListPtr) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
- DisplayList::destroyDisplayListDeferred(displayList);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
+ RenderNode::destroyDisplayListDeferred(displayList);
}
// ----------------------------------------------------------------------------
@@ -74,130 +74,129 @@
static void android_view_DisplayList_setCaching(JNIEnv* env,
jobject clazz, jlong displayListPtr, jboolean caching) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setCaching(caching);
}
static void android_view_DisplayList_setStaticMatrix(JNIEnv* env,
jobject clazz, jlong displayListPtr, jlong matrixPtr) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
displayList->setStaticMatrix(matrix);
}
static void android_view_DisplayList_setAnimationMatrix(JNIEnv* env,
jobject clazz, jlong displayListPtr, jlong matrixPtr) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
displayList->setAnimationMatrix(matrix);
}
static void android_view_DisplayList_setClipToBounds(JNIEnv* env,
jobject clazz, jlong displayListPtr, jboolean clipToBounds) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setClipToBounds(clipToBounds);
}
static void android_view_DisplayList_setIsolatedZVolume(JNIEnv* env,
jobject clazz, jlong displayListPtr, jboolean shouldIsolate) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
- displayList->setIsolatedZVolume(shouldIsolate);
+ // No-op, TODO: Remove Java usage of this method
}
static void android_view_DisplayList_setProjectBackwards(JNIEnv* env,
jobject clazz, jlong displayListPtr, jboolean shouldProject) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setProjectBackwards(shouldProject);
}
static void android_view_DisplayList_setProjectionReceiver(JNIEnv* env,
jobject clazz, jlong displayListPtr, jboolean shouldRecieve) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setProjectionReceiver(shouldRecieve);
}
static void android_view_DisplayList_setOutline(JNIEnv* env,
jobject clazz, jlong displayListPtr, jlong outlinePathPtr) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
SkPath* outline = reinterpret_cast<SkPath*>(outlinePathPtr);
displayList->setOutline(outline);
}
static void android_view_DisplayList_setClipToOutline(JNIEnv* env,
jobject clazz, jlong displayListPtr, jboolean clipToOutline) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setClipToOutline(clipToOutline);
}
static void android_view_DisplayList_setCastsShadow(JNIEnv* env,
jobject clazz, jlong displayListPtr, jboolean castsShadow) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setCastsShadow(castsShadow);
}
static void android_view_DisplayList_setUsesGlobalCamera(JNIEnv* env,
jobject clazz, jlong displayListPtr, jboolean usesGlobalCamera) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setUsesGlobalCamera(usesGlobalCamera);
}
static void android_view_DisplayList_setAlpha(JNIEnv* env,
jobject clazz, jlong displayListPtr, float alpha) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setAlpha(alpha);
}
static void android_view_DisplayList_setHasOverlappingRendering(JNIEnv* env,
jobject clazz, jlong displayListPtr, bool hasOverlappingRendering) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setHasOverlappingRendering(hasOverlappingRendering);
}
static void android_view_DisplayList_setTranslationX(JNIEnv* env,
jobject clazz, jlong displayListPtr, float tx) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setTranslationX(tx);
}
static void android_view_DisplayList_setTranslationY(JNIEnv* env,
jobject clazz, jlong displayListPtr, float ty) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setTranslationY(ty);
}
static void android_view_DisplayList_setTranslationZ(JNIEnv* env,
jobject clazz, jlong displayListPtr, float tz) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setTranslationZ(tz);
}
static void android_view_DisplayList_setRotation(JNIEnv* env,
jobject clazz, jlong displayListPtr, float rotation) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setRotation(rotation);
}
static void android_view_DisplayList_setRotationX(JNIEnv* env,
jobject clazz, jlong displayListPtr, float rx) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setRotationX(rx);
}
static void android_view_DisplayList_setRotationY(JNIEnv* env,
jobject clazz, jlong displayListPtr, float ry) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setRotationY(ry);
}
static void android_view_DisplayList_setScaleX(JNIEnv* env,
jobject clazz, jlong displayListPtr, float sx) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setScaleX(sx);
}
static void android_view_DisplayList_setScaleY(JNIEnv* env,
jobject clazz, jlong displayListPtr, float sy) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setScaleY(sy);
}
@@ -205,7 +204,7 @@
jobject clazz, jlong displayListPtr, float alpha,
float translationX, float translationY, float translationZ,
float rotation, float rotationX, float rotationY, float scaleX, float scaleY) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setAlpha(alpha);
displayList->setTranslationX(translationX);
displayList->setTranslationY(translationY);
@@ -219,158 +218,158 @@
static void android_view_DisplayList_setPivotX(JNIEnv* env,
jobject clazz, jlong displayListPtr, float px) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setPivotX(px);
}
static void android_view_DisplayList_setPivotY(JNIEnv* env,
jobject clazz, jlong displayListPtr, float py) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setPivotY(py);
}
static void android_view_DisplayList_setCameraDistance(JNIEnv* env,
jobject clazz, jlong displayListPtr, float distance) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setCameraDistance(distance);
}
static void android_view_DisplayList_setLeft(JNIEnv* env,
jobject clazz, jlong displayListPtr, int left) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setLeft(left);
}
static void android_view_DisplayList_setTop(JNIEnv* env,
jobject clazz, jlong displayListPtr, int top) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setTop(top);
}
static void android_view_DisplayList_setRight(JNIEnv* env,
jobject clazz, jlong displayListPtr, int right) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setRight(right);
}
static void android_view_DisplayList_setBottom(JNIEnv* env,
jobject clazz, jlong displayListPtr, int bottom) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setBottom(bottom);
}
static void android_view_DisplayList_setLeftTopRightBottom(JNIEnv* env,
jobject clazz, jlong displayListPtr, int left, int top,
int right, int bottom) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->setLeftTopRightBottom(left, top, right, bottom);
}
static void android_view_DisplayList_offsetLeftAndRight(JNIEnv* env,
jobject clazz, jlong displayListPtr, float offset) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->offsetLeftRight(offset);
}
static void android_view_DisplayList_offsetTopAndBottom(JNIEnv* env,
jobject clazz, jlong displayListPtr, float offset) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
displayList->offsetTopBottom(offset);
}
static jboolean android_view_DisplayList_hasOverlappingRendering(JNIEnv* env,
jobject clazz, jlong displayListPtr) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
return displayList->hasOverlappingRendering();
}
static jfloat android_view_DisplayList_getAlpha(JNIEnv* env,
jobject clazz, jlong displayListPtr) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
return displayList->getAlpha();
}
static jfloat android_view_DisplayList_getLeft(JNIEnv* env,
jobject clazz, jlong displayListPtr) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
return displayList->getLeft();
}
static jfloat android_view_DisplayList_getTop(JNIEnv* env,
jobject clazz, jlong displayListPtr) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
return displayList->getTop();
}
static jfloat android_view_DisplayList_getRight(JNIEnv* env,
jobject clazz, jlong displayListPtr) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
return displayList->getRight();
}
static jfloat android_view_DisplayList_getBottom(JNIEnv* env,
jobject clazz, jlong displayListPtr) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
return displayList->getBottom();
}
static jfloat android_view_DisplayList_getCameraDistance(JNIEnv* env,
jobject clazz, jlong displayListPtr) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
return displayList->getCameraDistance();
}
static jfloat android_view_DisplayList_getScaleX(JNIEnv* env,
jobject clazz, jlong displayListPtr) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
return displayList->getScaleX();
}
static jfloat android_view_DisplayList_getScaleY(JNIEnv* env,
jobject clazz, jlong displayListPtr) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
return displayList->getScaleY();
}
static jfloat android_view_DisplayList_getTranslationX(JNIEnv* env,
jobject clazz, jlong displayListPtr) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
return displayList->getTranslationX();
}
static jfloat android_view_DisplayList_getTranslationY(JNIEnv* env,
jobject clazz, jlong displayListPtr) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
return displayList->getTranslationY();
}
static jfloat android_view_DisplayList_getRotation(JNIEnv* env,
jobject clazz, jlong displayListPtr) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
return displayList->getRotation();
}
static jfloat android_view_DisplayList_getRotationX(JNIEnv* env,
jobject clazz, jlong displayListPtr) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
return displayList->getRotationX();
}
static jfloat android_view_DisplayList_getRotationY(JNIEnv* env,
jobject clazz, jlong displayListPtr) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
return displayList->getRotationY();
}
static jfloat android_view_DisplayList_getPivotX(JNIEnv* env,
jobject clazz, jlong displayListPtr) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
return displayList->getPivotX();
}
static jfloat android_view_DisplayList_getPivotY(JNIEnv* env,
jobject clazz, jlong displayListPtr) {
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
return displayList->getPivotY();
}
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index a4e6679..aa6a035 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -879,7 +879,7 @@
jobject clazz, jlong rendererPtr, jlong displayListPtr,
jobject dirty, jint flags) {
OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
android::uirenderer::Rect bounds;
status_t status = renderer->drawDisplayList(displayList, bounds, flags);
if (status != DrawGlInfo::kStatusDone && dirty != NULL) {
@@ -975,7 +975,7 @@
android_app_ActivityThread_dumpGraphics(JNIEnv* env, jobject clazz, jobject javaFileDescriptor) {
#ifdef USE_OPENGL_RENDERER
int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
- android::uirenderer::DisplayList::outputLogBuffer(fd);
+ android::uirenderer::RenderNode::outputLogBuffer(fd);
#endif // USE_OPENGL_RENDERER
}
diff --git a/core/jni/android_view_GLRenderer.cpp b/core/jni/android_view_GLRenderer.cpp
index b7e795e..228a92e 100644
--- a/core/jni/android_view_GLRenderer.cpp
+++ b/core/jni/android_view_GLRenderer.cpp
@@ -143,7 +143,7 @@
static void android_view_GLRenderer_setDisplayListData(JNIEnv* env, jobject clazz,
jlong displayListPtr, jlong newDataPtr) {
using namespace android::uirenderer;
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
DisplayListData* newData = reinterpret_cast<DisplayListData*>(newDataPtr);
displayList->setData(newData);
}
diff --git a/core/jni/android_view_HardwareLayer.cpp b/core/jni/android_view_HardwareLayer.cpp
index 5b21e94..ad2e9ff 100644
--- a/core/jni/android_view_HardwareLayer.cpp
+++ b/core/jni/android_view_HardwareLayer.cpp
@@ -120,7 +120,7 @@
jlong layerUpdaterPtr, jlong displayListPtr,
jint left, jint top, jint right, jint bottom) {
DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr);
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
layer->setDisplayList(displayList, left, top, right, bottom);
}
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 2b20758..28cee4b 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -106,7 +106,7 @@
static void android_view_ThreadedRenderer_setDisplayListData(JNIEnv* env, jobject clazz,
jlong proxyPtr, jlong displayListPtr, jlong newDataPtr) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
DisplayListData* newData = reinterpret_cast<DisplayListData*>(newDataPtr);
proxy->setDisplayListData(displayList, newData);
}
@@ -115,7 +115,7 @@
jlong proxyPtr, jlong displayListPtr, jint dirtyLeft, jint dirtyTop,
jint dirtyRight, jint dirtyBottom) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
- DisplayList* displayList = reinterpret_cast<DisplayList*>(displayListPtr);
+ RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
proxy->drawDisplayList(displayList, dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
}
diff --git a/docs/html/google/auth/api-client.jd b/docs/html/google/auth/api-client.jd
index fda3310..402a95f 100644
--- a/docs/html/google/auth/api-client.jd
+++ b/docs/html/google/auth/api-client.jd
@@ -112,7 +112,7 @@
href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html"
>{@code ConnectionCallbacks}</a> and <a
href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.OnConnectionFailedListener.html"
->{@code onConnectionFailedListener}</a>. These interfaces receive callbacks in
+>{@code OnConnectionFailedListener}</a>. These interfaces receive callbacks in
response to the asynchronous <a
href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html#connect()"
>{@code connect()}</a> method when the connection to Google Play services
@@ -512,7 +512,7 @@
new GetFileTask().execute(filename);
}
-private class GetFileTask extends AsyncTask<String, Void, Void> {
+private class GetFileTask extends AsyncTask<String, Void, Void> {
protected void doInBackground(String filename) {
Query query = new Query.Builder()
.addFilter(Filters.eq(SearchableField.TITLE, filename))
diff --git a/docs/html/guide/topics/ui/controls/button.jd b/docs/html/guide/topics/ui/controls/button.jd
index 02597c8..b52c3e9 100644
--- a/docs/html/guide/topics/ui/controls/button.jd
+++ b/docs/html/guide/topics/ui/controls/button.jd
@@ -113,7 +113,7 @@
<h3 id="ClickListener">Using an OnClickListener</h3>
-<p>You can also declare the click event handler pragmatically rather than in an XML layout. This
+<p>You can also declare the click event handler programmatically rather than in an XML layout. This
might be necessary if you instantiate the {@link android.widget.Button} at runtime or you need to
declare the click behavior in a {@link android.app.Fragment} subclass.</p>
diff --git a/docs/html/guide/topics/ui/controls/togglebutton.jd b/docs/html/guide/topics/ui/controls/togglebutton.jd
index c57b510..09af516 100644
--- a/docs/html/guide/topics/ui/controls/togglebutton.jd
+++ b/docs/html/guide/topics/ui/controls/togglebutton.jd
@@ -99,7 +99,7 @@
<h3 id="ClickListener">Using an OnCheckedChangeListener</h3>
-<p>You can also declare a click event handler pragmatically rather than in an XML layout. This
+<p>You can also declare a click event handler programmatically rather than in an XML layout. This
might be necessary if you instantiate the {@link android.widget.ToggleButton} or {@link
android.widget.Switch} at runtime or you need to
declare the click behavior in a {@link android.app.Fragment} subclass.</p>
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 69d3328..2dfc873 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -315,7 +315,7 @@
pathCache.clearGarbage();
patchCache.clearGarbage();
- Vector<DisplayList*> displayLists;
+ Vector<RenderNode*> displayLists;
Vector<Layer*> layers;
{ // scope for the lock
@@ -328,7 +328,7 @@
size_t count = displayLists.size();
for (size_t i = 0; i < count; i++) {
- DisplayList* displayList = displayLists.itemAt(i);
+ RenderNode* displayList = displayLists.itemAt(i);
delete displayList;
}
@@ -345,7 +345,7 @@
mLayerGarbage.push(layer);
}
-void Caches::deleteDisplayListDeferred(DisplayList* displayList) {
+void Caches::deleteDisplayListDeferred(RenderNode* displayList) {
Mutex::Autolock _l(mGarbageLock);
mDisplayListGarbage.push(displayList);
}
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 6f3d8fb..50c5fef 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -102,7 +102,7 @@
// Caches
///////////////////////////////////////////////////////////////////////////////
-class DisplayList;
+class RenderNode;
class ANDROID_API Caches: public Singleton<Caches> {
Caches();
@@ -169,7 +169,7 @@
/*
* Can be used to delete a display list from a non EGL thread.
*/
- void deleteDisplayListDeferred(DisplayList* layer);
+ void deleteDisplayListDeferred(RenderNode* layer);
/**
* Binds the VBO used to render simple textured quads.
@@ -420,7 +420,7 @@
mutable Mutex mGarbageLock;
Vector<Layer*> mLayerGarbage;
- Vector<DisplayList*> mDisplayListGarbage;
+ Vector<RenderNode*> mDisplayListGarbage;
DebugLevel mDebugLevel;
bool mInitialized;
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index 7a2e288..7a83967 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -54,7 +54,7 @@
SkRefCnt_SafeAssign(mColorFilter, colorFilter);
}
-void DeferredLayerUpdater::setDisplayList(DisplayList* displayList,
+void DeferredLayerUpdater::setDisplayList(RenderNode* displayList,
int left, int top, int right, int bottom) {
mDisplayList = displayList;
if (mDirtyRect.isEmpty()) {
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index 65f225c..d124cde 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -72,7 +72,7 @@
mTransform = matrix ? new SkMatrix(*matrix) : 0;
}
- ANDROID_API void setDisplayList(DisplayList* displayList,
+ ANDROID_API void setDisplayList(RenderNode* displayList,
int left, int top, int right, int bottom);
ANDROID_API void setPaint(const SkPaint* paint);
@@ -101,7 +101,7 @@
// Layer type specific properties
// displayList and surfaceTexture are mutually exclusive, only 1 may be set
// dirtyRect is only valid if displayList is set
- DisplayList* mDisplayList;
+ RenderNode* mDisplayList;
Rect mDirtyRect;
sp<GLConsumer> mSurfaceTexture;
SkMatrix* mTransform;
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 5c5a0425..0a52cd0 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -29,7 +29,7 @@
namespace android {
namespace uirenderer {
-void DisplayList::outputLogBuffer(int fd) {
+void RenderNode::outputLogBuffer(int fd) {
DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
if (logBuffer.isEmpty()) {
return;
@@ -48,7 +48,7 @@
fflush(file);
}
-DisplayList::DisplayList() :
+RenderNode::RenderNode() :
mDisplayListData(0), mDestroyed(false), mTransformMatrix(NULL), mTransformCamera(NULL),
mTransformMatrix3D(NULL), mStaticMatrix(NULL), mAnimationMatrix(NULL) {
@@ -57,7 +57,6 @@
mRight = 0;
mBottom = 0;
mClipToBounds = true;
- mIsolatedZVolume = true;
mProjectBackwards = false;
mProjectionReceiver = false;
mOutline.rewind();
@@ -87,7 +86,7 @@
mCaching = false;
}
-DisplayList::~DisplayList() {
+RenderNode::~RenderNode() {
LOG_ALWAYS_FATAL_IF(mDestroyed, "Double destroyed DisplayList %p", this);
mDestroyed = true;
@@ -99,14 +98,14 @@
delete mAnimationMatrix;
}
-void DisplayList::destroyDisplayListDeferred(DisplayList* displayList) {
+void RenderNode::destroyDisplayListDeferred(RenderNode* displayList) {
if (displayList) {
DISPLAY_LIST_LOGD("Deferring display list destruction");
Caches::getInstance().deleteDisplayListDeferred(displayList);
}
}
-void DisplayList::setData(DisplayListData* data) {
+void RenderNode::setData(DisplayListData* data) {
delete mDisplayListData;
mDisplayListData = data;
if (mDisplayListData) {
@@ -118,7 +117,7 @@
* This function is a simplified version of replay(), where we simply retrieve and log the
* display list. This function should remain in sync with the replay() function.
*/
-void DisplayList::output(uint32_t level) {
+void RenderNode::output(uint32_t level) {
ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this,
mName.string(), isRenderable());
ALOGD("%*s%s %d", level * 2, "", "Save",
@@ -133,17 +132,17 @@
ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, mName.string());
}
-float DisplayList::getPivotX() {
+float RenderNode::getPivotX() {
updateMatrix();
return mPivotX;
}
-float DisplayList::getPivotY() {
+float RenderNode::getPivotY() {
updateMatrix();
return mPivotY;
}
-void DisplayList::updateMatrix() {
+void RenderNode::updateMatrix() {
if (mMatrixDirty) {
// NOTE: mTransformMatrix won't be up to date if a DisplayList goes from a complex transform
// to a pure translate. This is safe because the matrix isn't read in pure translate cases.
@@ -195,7 +194,7 @@
}
}
-void DisplayList::outputViewProperties(const int level) {
+void RenderNode::outputViewProperties(const int level) {
updateMatrix();
if (mLeft != 0 || mTop != 0) {
ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", mLeft, mTop);
@@ -249,7 +248,7 @@
#define PROPERTY_SAVECOUNT 0
template <class T>
-void DisplayList::setViewProperties(OpenGLRenderer& renderer, T& handler,
+void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler,
const int level) {
#if DEBUG_DISPLAY_LIST
outputViewProperties(level);
@@ -304,8 +303,11 @@
/**
* Apply property-based transformations to input matrix
+ *
+ * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4
+ * matrix computation instead of the Skia 3x3 matrix + camera hackery.
*/
-void DisplayList::applyViewPropertyTransforms(mat4& matrix) {
+void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) {
if (mLeft != 0 || mTop != 0) {
matrix.translate(mLeft, mTop);
}
@@ -319,24 +321,39 @@
if (mMatrixFlags != 0) {
updateMatrix();
if (mMatrixFlags == TRANSLATION) {
- matrix.translate(mTranslationX, mTranslationY, mTranslationZ);
+ matrix.translate(mTranslationX, mTranslationY,
+ true3dTransform ? mTranslationZ : 0.0f);
} else {
- matrix.multiply(*mTransformMatrix);
+ if (!true3dTransform) {
+ matrix.multiply(*mTransformMatrix);
+ } else {
+ mat4 true3dMat;
+ true3dMat.loadTranslate(
+ mPivotX + mTranslationX,
+ mPivotY + mTranslationY,
+ mTranslationZ);
+ true3dMat.rotate(mRotationX, 1, 0, 0);
+ true3dMat.rotate(mRotationY, 0, 1, 0);
+ true3dMat.rotate(mRotation, 0, 0, 1);
+ true3dMat.scale(mScaleX, mScaleY, 1);
+ true3dMat.translate(-mPivotX, -mPivotY);
+
+ matrix.multiply(true3dMat);
+ }
}
}
}
/**
- * Organizes the DisplayList hierarchy to prepare for Z-based draw order.
+ * Organizes the DisplayList hierarchy to prepare for background projection reordering.
*
* This should be called before a call to defer() or drawDisplayList()
*
* Each DisplayList that serves as a 3d root builds its list of composited children,
* which are flagged to not draw in the standard draw loop.
*/
-void DisplayList::computeOrdering() {
+void RenderNode::computeOrdering() {
ATRACE_CALL();
- m3dNodes.clear();
mProjectedNodes.clear();
// TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
@@ -345,40 +362,23 @@
for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
DrawDisplayListOp* childOp = mDisplayListData->children[i];
childOp->mDisplayList->computeOrderingImpl(childOp,
- &m3dNodes, &mat4::identity(),
&mProjectedNodes, &mat4::identity());
}
}
-void DisplayList::computeOrderingImpl(
+void RenderNode::computeOrderingImpl(
DrawDisplayListOp* opState,
- Vector<ZDrawDisplayListOpPair>* compositedChildrenOf3dRoot,
- const mat4* transformFrom3dRoot,
Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
const mat4* transformFromProjectionSurface) {
- m3dNodes.clear();
mProjectedNodes.clear();
if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return;
// TODO: should avoid this calculation in most cases
// TODO: just calculate single matrix, down to all leaf composited elements
- Matrix4 localTransformFrom3dRoot(*transformFrom3dRoot);
- localTransformFrom3dRoot.multiply(opState->mTransformFromParent);
Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface);
localTransformFromProjectionSurface.multiply(opState->mTransformFromParent);
- if (mTranslationZ != 0.0f) { // TODO: other signals for 3d compositing, such as custom matrix4
- // composited 3d layer, flag for out of order draw and save matrix...
- opState->mSkipInOrderDraw = true;
- opState->mTransformFromCompositingAncestor.load(localTransformFrom3dRoot);
-
- // ... and insert into current 3d root, keyed with pivot z for later sorting
- Vector3 pivot(mPivotX, mPivotY, 0.0f);
- mat4 totalTransform(localTransformFrom3dRoot);
- applyViewPropertyTransforms(totalTransform);
- totalTransform.mapPoint3d(pivot);
- compositedChildrenOf3dRoot->add(ZDrawDisplayListOpPair(pivot.z, opState));
- } else if (mProjectBackwards) {
+ if (mProjectBackwards) {
// composited projectee, flag for out of order draw, save matrix, and store in proj surface
opState->mSkipInOrderDraw = true;
opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface);
@@ -389,20 +389,11 @@
}
if (mDisplayListData->children.size() > 0) {
- if (mIsolatedZVolume) {
- // create a new 3d space for descendents by collecting them
- compositedChildrenOf3dRoot = &m3dNodes;
- transformFrom3dRoot = &mat4::identity();
- } else {
- applyViewPropertyTransforms(localTransformFrom3dRoot);
- transformFrom3dRoot = &localTransformFrom3dRoot;
- }
-
const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0;
bool haveAppliedPropertiesToProjection = false;
for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
DrawDisplayListOp* childOp = mDisplayListData->children[i];
- DisplayList* child = childOp->mDisplayList;
+ RenderNode* child = childOp->mDisplayList;
Vector<DrawDisplayListOp*>* projectionChildren = NULL;
const mat4* projectionTransform = NULL;
@@ -422,9 +413,7 @@
projectionChildren = compositedChildrenOfProjectionSurface;
projectionTransform = &localTransformFromProjectionSurface;
}
- child->computeOrderingImpl(childOp,
- compositedChildrenOf3dRoot, transformFrom3dRoot,
- projectionChildren, projectionTransform);
+ child->computeOrderingImpl(childOp, projectionChildren, projectionTransform);
}
}
@@ -444,7 +433,7 @@
const int mLevel;
};
-void DisplayList::defer(DeferStateStruct& deferStruct, const int level) {
+void RenderNode::defer(DeferStateStruct& deferStruct, const int level) {
DeferOperationHandler handler(deferStruct, level);
iterate<DeferOperationHandler>(deferStruct.mRenderer, handler, level);
}
@@ -466,7 +455,7 @@
const int mLevel;
};
-void DisplayList::replay(ReplayStateStruct& replayStruct, const int level) {
+void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) {
ReplayOperationHandler handler(replayStruct, level);
replayStruct.mRenderer.startMark(mName.string());
@@ -477,14 +466,36 @@
replayStruct.mDrawGlStatus);
}
+void RenderNode::buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes) {
+ if (mDisplayListData == NULL || mDisplayListData->children.size() == 0) return;
+
+ for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
+ DrawDisplayListOp* childOp = mDisplayListData->children[i];
+ RenderNode* child = childOp->mDisplayList;
+ float childZ = child->mTranslationZ;
+
+ if (childZ != 0.0f) {
+ zTranslatedNodes.add(ZDrawDisplayListOpPair(childZ, childOp));
+ childOp->mSkipInOrderDraw = true;
+ } else if (!child->mProjectBackwards) {
+ // regular, in order drawing DisplayList
+ childOp->mSkipInOrderDraw = false;
+ }
+ }
+
+ // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order)
+ std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
+}
+
#define SHADOW_DELTA 0.1f
template <class T>
-void DisplayList::iterate3dChildren(ChildrenSelectMode mode, OpenGLRenderer& renderer,
- T& handler, const int level) {
- if (m3dNodes.size() == 0 ||
- (mode == kNegativeZChildren && m3dNodes[0].key > 0.0f) ||
- (mode == kPositiveZChildren && m3dNodes[m3dNodes.size() - 1].key < 0.0f)) {
+void RenderNode::iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
+ ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) {
+ const int size = zTranslatedNodes.size();
+ if (size == 0
+ || (mode == kNegativeZChildren && zTranslatedNodes[0].key > 0.0f)
+ || (mode == kPositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) {
// no 3d children to draw
return;
}
@@ -502,7 +513,7 @@
* This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
* underneath both, and neither's shadow is drawn on top of the other.
*/
- const size_t nonNegativeIndex = findNonNegativeIndex(m3dNodes);
+ const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
size_t drawIndex, shadowIndex, endIndex;
if (mode == kNegativeZChildren) {
drawIndex = 0;
@@ -510,24 +521,29 @@
shadowIndex = endIndex; // draw no shadows
} else {
drawIndex = nonNegativeIndex;
- endIndex = m3dNodes.size();
+ endIndex = size;
shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
}
float lastCasterZ = 0.0f;
while (shadowIndex < endIndex || drawIndex < endIndex) {
if (shadowIndex < endIndex) {
- DrawDisplayListOp* casterOp = m3dNodes[shadowIndex].value;
- DisplayList* caster = casterOp->mDisplayList;
- const float casterZ = m3dNodes[shadowIndex].key;
+ DrawDisplayListOp* casterOp = zTranslatedNodes[shadowIndex].value;
+ RenderNode* caster = casterOp->mDisplayList;
+ const float casterZ = zTranslatedNodes[shadowIndex].key;
// attempt to render the shadow if the caster about to be drawn is its caster,
// OR if its caster's Z value is similar to the previous potential caster
if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
if (caster->mCastsShadow && caster->mAlpha > 0.0f) {
- mat4 shadowMatrix(casterOp->mTransformFromCompositingAncestor);
- caster->applyViewPropertyTransforms(shadowMatrix);
+ mat4 shadowMatrixXY(casterOp->mTransformFromParent);
+ caster->applyViewPropertyTransforms(shadowMatrixXY);
- DisplayListOp* shadowOp = new (alloc) DrawShadowOp(shadowMatrix,
+ // Z matrix needs actual 3d transformation, so mapped z values will be correct
+ mat4 shadowMatrixZ(casterOp->mTransformFromParent);
+ caster->applyViewPropertyTransforms(shadowMatrixZ, true);
+
+ DisplayListOp* shadowOp = new (alloc) DrawShadowOp(
+ shadowMatrixXY, shadowMatrixZ,
caster->mAlpha, &(caster->mOutline), caster->mWidth, caster->mHeight);
handler(shadowOp, PROPERTY_SAVECOUNT, mClipToBounds);
}
@@ -542,10 +558,10 @@
// since it modifies the renderer's matrix
int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
- DrawDisplayListOp* childOp = m3dNodes[drawIndex].value;
- DisplayList* child = childOp->mDisplayList;
+ DrawDisplayListOp* childOp = zTranslatedNodes[drawIndex].value;
+ RenderNode* child = childOp->mDisplayList;
- renderer.concatMatrix(childOp->mTransformFromCompositingAncestor);
+ renderer.concatMatrix(childOp->mTransformFromParent);
childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
handler(childOp, renderer.getSaveCount() - 1, mClipToBounds);
childOp->mSkipInOrderDraw = true;
@@ -557,7 +573,7 @@
}
template <class T>
-void DisplayList::iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level) {
+void RenderNode::iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level) {
int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
LinearAllocator& alloc = handler.allocator();
ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, mWidth, mHeight,
@@ -588,7 +604,7 @@
* defer vs replay logic, per operation
*/
template <class T>
-void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level) {
+void RenderNode::iterate(OpenGLRenderer& renderer, T& handler, const int level) {
if (CC_UNLIKELY(mDestroyed)) { // temporary debug logging
ALOGW("Error: %s is drawing after destruction", getName());
CRASH();
@@ -617,11 +633,11 @@
bool quickRejected = mClipToBounds && renderer.quickRejectConservative(0, 0, mWidth, mHeight);
if (!quickRejected) {
- // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order)
- std::stable_sort(m3dNodes.begin(), m3dNodes.end());
+ Vector<ZDrawDisplayListOpPair> zTranslatedNodes;
+ buildZSortedChildList(zTranslatedNodes);
// for 3d root, draw children with negative z values
- iterate3dChildren(kNegativeZChildren, renderer, handler, level);
+ iterate3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler);
DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
const int saveCountOffset = renderer.getSaveCount() - 1;
@@ -642,7 +658,7 @@
}
// for 3d root, draw children with positive z values
- iterate3dChildren(kPositiveZChildren, renderer, handler, level);
+ iterate3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
}
DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index a3577d4..b141fd4 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -160,17 +160,17 @@
* recorded stream of canvas operations is refreshed. The DisplayList (and its properties) stay
* attached.
*/
-class DisplayList {
+class RenderNode {
public:
- ANDROID_API DisplayList();
- ANDROID_API ~DisplayList();
+ ANDROID_API RenderNode();
+ ANDROID_API ~RenderNode();
// See flags defined in DisplayList.java
enum ReplayFlag {
kReplayFlag_ClipChildren = 0x1
};
- ANDROID_API static void destroyDisplayListDeferred(DisplayList* displayList);
+ ANDROID_API static void destroyDisplayListDeferred(RenderNode* displayList);
ANDROID_API static void outputLogBuffer(int fd);
ANDROID_API void setData(DisplayListData* newData);
@@ -204,10 +204,6 @@
mClipToBounds = clipToBounds;
}
- void setIsolatedZVolume(bool shouldIsolate) {
- mIsolatedZVolume = shouldIsolate;
- }
-
void setCastsShadow(bool castsShadow) {
mCastsShadow = castsShadow;
}
@@ -573,20 +569,20 @@
void outputViewProperties(const int level);
- void applyViewPropertyTransforms(mat4& matrix);
+ void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false);
void computeOrderingImpl(DrawDisplayListOp* opState,
- Vector<ZDrawDisplayListOpPair>* compositedChildrenOf3dRoot,
- const mat4* transformFrom3dRoot,
Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
const mat4* transformFromProjectionSurface);
template <class T>
inline void setViewProperties(OpenGLRenderer& renderer, T& handler, const int level);
+ void buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes);
+
template <class T>
- inline void iterate3dChildren(ChildrenSelectMode mode, OpenGLRenderer& renderer,
- T& handler, const int level);
+ inline void iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
+ ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler);
template <class T>
inline void iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level);
@@ -617,7 +613,6 @@
// Rendering properties
bool mClipToBounds;
- bool mIsolatedZVolume;
bool mProjectBackwards;
bool mProjectionReceiver;
SkPath mOutline;
@@ -657,9 +652,6 @@
* Draw time state - these properties are only set and used during rendering
*/
- // for 3d roots, contains a z sorted list of all children items
- Vector<ZDrawDisplayListOpPair> m3dNodes;
-
// for projection surfaces, contains a list of all children items
Vector<DrawDisplayListOp*> mProjectedNodes;
}; // class DisplayList
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 65eda29..549b786 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -1488,9 +1488,9 @@
};
class DrawDisplayListOp : public DrawBoundedOp {
- friend class DisplayList; // grant DisplayList access to info of child
+ friend class RenderNode; // grant DisplayList access to info of child
public:
- DrawDisplayListOp(DisplayList* displayList, int flags, const mat4& transformFromParent)
+ DrawDisplayListOp(RenderNode* displayList, int flags, const mat4& transformFromParent)
: DrawBoundedOp(0, 0, displayList->getWidth(), displayList->getHeight(), 0),
mDisplayList(displayList), mFlags(flags), mTransformFromParent(transformFromParent) {}
@@ -1522,7 +1522,7 @@
virtual const char* name() { return "DrawDisplayList"; }
private:
- DisplayList* mDisplayList;
+ RenderNode* mDisplayList;
const int mFlags;
///////////////////////////
@@ -1534,10 +1534,10 @@
const mat4 mTransformFromParent;
/**
- * Holds the transformation between the 3d root OR projection surface ViewGroup and this
- * DisplayList drawing instance. Represents any translations / transformations done within the
- * drawing of the compositing ancestor ViewGroup's draw, before the draw of the View represented
- * by this DisplayList draw instance.
+ * Holds the transformation between the projection surface ViewGroup and this DisplayList
+ * drawing instance. Represents any translations / transformations done within the drawing of
+ * the compositing ancestor ViewGroup's draw, before the draw of the View represented by this
+ * DisplayList draw instance.
*
* Note: doesn't include any transformation recorded within the DisplayList and its properties.
*/
@@ -1550,19 +1550,20 @@
*/
class DrawShadowOp : public DrawOp {
public:
- DrawShadowOp(const mat4& transform, float alpha, const SkPath* outline,
+ DrawShadowOp(const mat4& transformXY, const mat4& transformZ, float alpha, const SkPath* outline,
float fallbackWidth, float fallbackHeight)
- : DrawOp(NULL), mTransform(transform), mAlpha(alpha), mOutline(outline),
+ : DrawOp(NULL), mTransformXY(transformXY), mTransformZ(transformZ),
+ mAlpha(alpha), mOutline(outline),
mFallbackWidth(fallbackWidth), mFallbackHeight(fallbackHeight) {}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- if (!mOutline->isEmpty()) {
- return renderer.drawShadow(mTransform, mAlpha, mOutline);
+ if (mOutline->isEmpty()) {
+ SkPath fakeOutline;
+ fakeOutline.addRect(0, 0, mFallbackWidth, mFallbackHeight);
+ return renderer.drawShadow(mTransformXY, mTransformZ, mAlpha, &fakeOutline);
}
- SkPath fakeOutline;
- fakeOutline.addRect(0, 0, mFallbackWidth, mFallbackHeight);
- return renderer.drawShadow(mTransform, mAlpha, &fakeOutline);
+ return renderer.drawShadow(mTransformXY, mTransformZ, mAlpha, mOutline);
}
virtual void output(int level, uint32_t logFlags) const {
@@ -1572,7 +1573,8 @@
virtual const char* name() { return "DrawShadow"; }
private:
- const mat4 mTransform;
+ const mat4 mTransformXY;
+ const mat4 mTransformZ;
const float mAlpha;
const SkPath* mOutline;
const float mFallbackWidth;
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 3b1d567..e69e08e 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -179,7 +179,7 @@
return StatefulBaseRenderer::clipRegion(region, op);
}
-status_t DisplayListRenderer::drawDisplayList(DisplayList* displayList,
+status_t DisplayListRenderer::drawDisplayList(RenderNode* displayList,
Rect& dirty, int32_t flags) {
// dirty is an out parameter and should not be recorded,
// it matters only when replaying the display list
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 1fb72ce..65498a5 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -156,7 +156,7 @@
// Canvas draw operations - special
// ----------------------------------------------------------------------------
virtual status_t drawLayer(Layer* layer, float x, float y);
- virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty,
+ virtual status_t drawDisplayList(RenderNode* displayList, Rect& dirty,
int32_t replayFlags);
// TODO: rename for consistency
@@ -309,7 +309,7 @@
int mRestoreSaveCount;
- friend class DisplayList;
+ friend class RenderNode;
}; // class DisplayListRenderer
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 8992a13..52176d4 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -194,7 +194,7 @@
deferredList = new DeferredDisplayList(dirtyRect);
DeferStateStruct deferredState(*deferredList, *renderer,
- DisplayList::kReplayFlag_ClipChildren);
+ RenderNode::kReplayFlag_ClipChildren);
renderer->initViewport(width, height);
renderer->setupFrameState(dirtyRect.left, dirtyRect.top,
@@ -238,7 +238,7 @@
renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
!isBlend());
- renderer->drawDisplayList(displayList, dirtyRect, DisplayList::kReplayFlag_ClipChildren);
+ renderer->drawDisplayList(displayList, dirtyRect, RenderNode::kReplayFlag_ClipChildren);
renderer->finish();
renderer = NULL;
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index f6538f2..d8440ea 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -43,7 +43,7 @@
// Forward declarations
class Caches;
class OpenGLRenderer;
-class DisplayList;
+class RenderNode;
class DeferredDisplayList;
class DeferStateStruct;
@@ -84,7 +84,7 @@
regionRect.translate(layer.left, layer.top);
}
- void updateDeferred(OpenGLRenderer* renderer, DisplayList* displayList,
+ void updateDeferred(OpenGLRenderer* renderer, RenderNode* displayList,
int left, int top, int right, int bottom) {
this->renderer = renderer;
this->displayList = displayList;
@@ -294,7 +294,7 @@
*/
bool deferredUpdateScheduled;
OpenGLRenderer* renderer;
- DisplayList* displayList;
+ RenderNode* displayList;
Rect dirtyRect;
bool debugDrawUpdate;
bool hasDrawnSinceUpdate;
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 3d6188a..f06106b 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -385,9 +385,14 @@
mType = kTypeTranslate | kTypeScale | kTypeRectToRect;
}
+float Matrix4::mapZ(const Vector3& orig) const {
+ // duplicates logic for mapPoint3d's z coordinate
+ return orig.x * data[2] + orig.y * data[6] + orig.z * data[kScaleZ] + data[kTranslateZ];
+}
+
void Matrix4::mapPoint3d(Vector3& vec) const {
//TODO: optimize simple case
- Vector3 orig(vec);
+ const Vector3 orig(vec);
vec.x = orig.x * data[kScaleX] + orig.y * data[kSkewX] + orig.z * data[8] + data[kTranslateX];
vec.y = orig.x * data[kSkewY] + orig.y * data[kScaleY] + orig.z * data[9] + data[kTranslateY];
vec.z = orig.x * data[2] + orig.y * data[6] + orig.z * data[kScaleZ] + data[kTranslateZ];
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index 8b586f0..26cb05f 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -199,6 +199,7 @@
void copyTo(float* v) const;
void copyTo(SkMatrix& v) const;
+ float mapZ(const Vector3& orig) const;
void mapPoint3d(Vector3& vec) const;
void mapPoint(float& x, float& y) const; // 2d only
void mapRect(Rect& r) const; // 2d only
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 7002e26..1475953 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1912,7 +1912,7 @@
// Drawing
///////////////////////////////////////////////////////////////////////////////
-status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty,
+status_t OpenGLRenderer::drawDisplayList(RenderNode* displayList, Rect& dirty,
int32_t replayFlags) {
status_t status;
// All the usual checks and setup operations (quickReject, setupDraw, etc.)
@@ -3190,8 +3190,16 @@
return drawColorRects(rects, count, paint, false, true, true);
}
-status_t OpenGLRenderer::drawShadow(const mat4& casterTransform, float casterAlpha,
- const SkPath* casterOutline) {
+static void mapPointFakeZ(Vector3& point, const mat4& transformXY, const mat4& transformZ) {
+ // map z coordinate with true 3d matrix
+ point.z = transformZ.mapZ(point);
+
+ // map x,y coordinates with draw/Skia matrix
+ transformXY.mapPoint(point.x, point.y);
+}
+
+status_t OpenGLRenderer::drawShadow(const mat4& casterTransformXY, const mat4& casterTransformZ,
+ float casterAlpha, const SkPath* casterOutline) {
if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone;
// TODO: use quickRejectWithScissor. For now, always force enable scissor.
@@ -3214,10 +3222,12 @@
// map 2d caster poly into 3d
const int casterVertexCount = casterVertices2d.size();
Vector3 casterPolygon[casterVertexCount];
+ float minZ = FLT_MAX;
for (int i = 0; i < casterVertexCount; i++) {
const Vertex& point2d = casterVertices2d[i];
casterPolygon[i] = Vector3(point2d.x, point2d.y, 0);
- casterTransform.mapPoint3d(casterPolygon[i]);
+ mapPointFakeZ(casterPolygon[i], casterTransformXY, casterTransformZ);
+ minZ = fmin(minZ, casterPolygon[i].z);
}
// map the centroid of the caster into 3d
@@ -3225,7 +3235,16 @@
reinterpret_cast<const Vector2*>(casterVertices2d.array()),
casterVertexCount);
Vector3 centroid3d(centroid.x, centroid.y, 0);
- casterTransform.mapPoint3d(centroid3d);
+ mapPointFakeZ(centroid3d, casterTransformXY, casterTransformZ);
+
+ // if the caster intersects the z=0 plane, lift it in Z so it doesn't
+ if (minZ < SHADOW_MIN_CASTER_Z) {
+ float casterLift = SHADOW_MIN_CASTER_Z - minZ;
+ for (int i = 0; i < casterVertexCount; i++) {
+ casterPolygon[i].z += casterLift;
+ }
+ centroid3d.z += casterLift;
+ }
// draw caster's shadows
if (mCaches.propertyAmbientShadowStrength > 0) {
@@ -3244,7 +3263,6 @@
ShadowTessellator::tessellateSpotShadow(casterPolygon, casterVertexCount,
lightPosScale, *currentTransform(), getWidth(), getHeight(),
spotShadowVertexBuffer);
-
drawVertexBuffer(kVertexBufferMode_Shadow, spotShadowVertexBuffer, &paint);
}
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 03beae3..94abfa7 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -54,7 +54,7 @@
namespace uirenderer {
class DeferredDisplayState;
-class DisplayList;
+class RenderNode;
class TextSetupFunctor;
class VertexBuffer;
class SkiaShader;
@@ -165,7 +165,7 @@
int saveLayerDeferred(float left, float top, float right, float bottom,
const SkPaint* paint, int flags);
- virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t replayFlags = 1);
+ virtual status_t drawDisplayList(RenderNode* displayList, Rect& dirty, int32_t replayFlags = 1);
virtual status_t drawLayer(Layer* layer, float x, float y);
virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top,
const SkPaint* paint);
@@ -208,8 +208,8 @@
DrawOpMode drawOpMode = kDrawOpMode_Immediate);
virtual status_t drawRects(const float* rects, int count, const SkPaint* paint);
- status_t drawShadow(const mat4& casterTransform, float casterAlpha,
- const SkPath* casterOutline);
+ status_t drawShadow(const mat4& casterTransformXY, const mat4& casterTransformZ,
+ float casterAlpha, const SkPath* casterOutline);
virtual void resetShader();
virtual void setupShader(SkiaShader* shader);
diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h
index 4754bad..efcea5f 100644
--- a/libs/hwui/Renderer.h
+++ b/libs/hwui/Renderer.h
@@ -31,7 +31,7 @@
namespace uirenderer {
-class DisplayList;
+class RenderNode;
class Layer;
class Matrix4;
class SkiaColorFilter;
@@ -232,7 +232,7 @@
// Canvas draw operations - special
// ----------------------------------------------------------------------------
virtual status_t drawLayer(Layer* layer, float x, float y) = 0;
- virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty,
+ virtual status_t drawDisplayList(RenderNode* displayList, Rect& dirty,
int32_t replayFlags) = 0;
// TODO: rename for consistency
diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h
index 120774b..c558460 100644
--- a/libs/hwui/ShadowTessellator.h
+++ b/libs/hwui/ShadowTessellator.h
@@ -54,6 +54,8 @@
// The total number of indices used for drawing the shadow geometry as triangle strips.
#define SHADOW_INDEX_COUNT (2 * SHADOW_RAY_COUNT + 1 + 2 * (SHADOW_RAY_COUNT + 1))
+#define SHADOW_MIN_CASTER_Z 0.001f
+
class ShadowTessellator {
public:
static void tessellateAmbientShadow(const Vector3* casterPolygon,
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index 54039c01..8538b29 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -35,7 +35,7 @@
* Calculate the angle between and x and a y coordinate.
* The atan2 range from -PI to PI.
*/
-float angle(const Vector2& point, const Vector2& center) {
+static float angle(const Vector2& point, const Vector2& center) {
return atan2(point.y - center.y, point.x - center.x);
}
@@ -51,7 +51,7 @@
* @param p2 The second point defining the line segment
* @return The distance along the ray if it intersects with the line segment, negative if otherwise
*/
-float rayIntersectPoints(const Vector2& rayOrigin, float dx, float dy,
+static float rayIntersectPoints(const Vector2& rayOrigin, float dx, float dy,
const Vector2& p1, const Vector2& p2) {
// The math below is derived from solving this formula, basically the
// intersection point should stay on both the ray and the edge of (p1, p2).
@@ -548,14 +548,9 @@
// Validate input, receiver is always at z = 0 plane.
bool inputPolyPositionValid = true;
for (int i = 0; i < polyLength; i++) {
- if (poly[i].z <= 0.00001) {
- inputPolyPositionValid = false;
- ALOGE("polygon below the surface");
- break;
- }
if (poly[i].z >= lightPoly[0].z) {
inputPolyPositionValid = false;
- ALOGE("polygon above the light");
+ ALOGW("polygon above the light");
break;
}
}
diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h
index 15b9d6b..c61cb61 100644
--- a/libs/hwui/Vector.h
+++ b/libs/hwui/Vector.h
@@ -124,6 +124,10 @@
Vector3(float px, float py, float pz) :
x(px), y(py), z(pz) {
}
+
+ void dump() {
+ ALOGD("Vector3[%.2f, %.2f, %.2f]", x, y, z);
+ }
};
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index ce66d8f..5ed9f1d 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -373,7 +373,7 @@
mCanvas->setViewport(width, height);
}
-void CanvasContext::setDisplayListData(DisplayList* displayList, DisplayListData* newData) {
+void CanvasContext::setDisplayListData(RenderNode* displayList, DisplayListData* newData) {
displayList->setData(newData);
}
@@ -388,7 +388,7 @@
}
}
-void CanvasContext::drawDisplayList(DisplayList* displayList, Rect* dirty) {
+void CanvasContext::drawDisplayList(RenderNode* displayList, Rect* dirty) {
LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
"drawDisplayList called on a context with no canvas or surface!");
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 649ffb6..e3fdf97 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -31,7 +31,7 @@
namespace uirenderer {
class DeferredLayerUpdater;
-class DisplayList;
+class RenderNode;
class DisplayListData;
class OpenGLRenderer;
class Rect;
@@ -63,9 +63,9 @@
bool initialize(EGLNativeWindowType window);
void updateSurface(EGLNativeWindowType window);
void setup(int width, int height);
- void setDisplayListData(DisplayList* displayList, DisplayListData* newData);
+ void setDisplayListData(RenderNode* displayList, DisplayListData* newData);
void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters);
- void drawDisplayList(DisplayList* displayList, Rect* dirty);
+ void drawDisplayList(RenderNode* displayList, Rect* dirty);
void destroyCanvas();
bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 200c21f..93360fc 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -117,13 +117,13 @@
post(task);
}
-CREATE_BRIDGE3(setDisplayListData, CanvasContext* context, DisplayList* displayList,
+CREATE_BRIDGE3(setDisplayListData, CanvasContext* context, RenderNode* displayList,
DisplayListData* newData) {
args->context->setDisplayListData(args->displayList, args->newData);
return NULL;
}
-void RenderProxy::setDisplayListData(DisplayList* displayList, DisplayListData* newData) {
+void RenderProxy::setDisplayListData(RenderNode* displayList, DisplayListData* newData) {
SETUP_TASK(setDisplayListData);
args->context = mContext;
args->displayList = displayList;
@@ -131,7 +131,7 @@
post(task);
}
-CREATE_BRIDGE4(drawDisplayList, CanvasContext* context, DisplayList* displayList,
+CREATE_BRIDGE4(drawDisplayList, CanvasContext* context, RenderNode* displayList,
Rect dirty, const Vector<DeferredLayerUpdater*>* layerUpdates) {
Rect* dirty = &args->dirty;
if (dirty->bottom == -1 && dirty->left == -1 &&
@@ -143,7 +143,7 @@
return NULL;
}
-void RenderProxy::drawDisplayList(DisplayList* displayList,
+void RenderProxy::drawDisplayList(RenderNode* displayList,
int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) {
SETUP_TASK(drawDisplayList);
args->context = mContext;
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 83a8a8f..73e9805 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -32,7 +32,7 @@
namespace uirenderer {
class DeferredLayerUpdater;
-class DisplayList;
+class RenderNode;
class DisplayListData;
class Layer;
class Rect;
@@ -60,8 +60,8 @@
ANDROID_API bool initialize(EGLNativeWindowType window);
ANDROID_API void updateSurface(EGLNativeWindowType window);
ANDROID_API void setup(int width, int height);
- ANDROID_API void setDisplayListData(DisplayList* displayList, DisplayListData* newData);
- ANDROID_API void drawDisplayList(DisplayList* displayList,
+ ANDROID_API void setDisplayListData(RenderNode* displayList, DisplayListData* newData);
+ ANDROID_API void drawDisplayList(RenderNode* displayList,
int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
ANDROID_API void destroyCanvas();
diff --git a/media/java/android/media/Rating.java b/media/java/android/media/Rating.java
index b94db18..f4fbe2c 100644
--- a/media/java/android/media/Rating.java
+++ b/media/java/android/media/Rating.java
@@ -29,8 +29,13 @@
* through one of the factory methods.
*/
public final class Rating implements Parcelable {
-
private final static String TAG = "Rating";
+ /**
+ * Indicates a rating style is not supported. A Rating will never have this
+ * type, but can be used by other classes to indicate they do not support
+ * Rating.
+ */
+ public final static int RATING_NONE = 0;
/**
* A rating style with a single degree of rating, "heart" vs "no heart". Can be used to
diff --git a/media/java/android/media/session/IMediaController.aidl b/media/java/android/media/session/IMediaController.aidl
index 8ca0e45..d34e973 100644
--- a/media/java/android/media/session/IMediaController.aidl
+++ b/media/java/android/media/session/IMediaController.aidl
@@ -16,9 +16,12 @@
package android.media.session;
import android.content.Intent;
+import android.media.Rating;
import android.media.session.IMediaControllerCallback;
+import android.media.session.MediaMetadata;
+import android.media.session.PlaybackState;
import android.os.Bundle;
-import android.os.IBinder;
+import android.os.ResultReceiver;
import android.view.KeyEvent;
/**
@@ -26,9 +29,23 @@
* @hide
*/
interface IMediaController {
- void sendCommand(String command, in Bundle extras);
+ void sendCommand(String command, in Bundle extras, in ResultReceiver cb);
void sendMediaButton(in KeyEvent mediaButton);
void registerCallbackListener(in IMediaControllerCallback cb);
void unregisterCallbackListener(in IMediaControllerCallback cb);
- int getPlaybackState();
+ boolean isTransportControlEnabled();
+
+ // These commands are for the TransportController
+ void play();
+ void pause();
+ void stop();
+ void next();
+ void previous();
+ void fastForward();
+ void rewind();
+ void seekTo(long pos);
+ void rate(in Rating rating);
+ MediaMetadata getMetadata();
+ PlaybackState getPlaybackState();
+ int getRatingType();
}
\ No newline at end of file
diff --git a/media/java/android/media/session/IMediaControllerCallback.aidl b/media/java/android/media/session/IMediaControllerCallback.aidl
index 3aa0ee4..3651f1b 100644
--- a/media/java/android/media/session/IMediaControllerCallback.aidl
+++ b/media/java/android/media/session/IMediaControllerCallback.aidl
@@ -15,6 +15,8 @@
package android.media.session;
+import android.media.session.MediaMetadata;
+import android.media.session.PlaybackState;
import android.os.Bundle;
/**
@@ -22,7 +24,9 @@
*/
oneway interface IMediaControllerCallback {
void onEvent(String event, in Bundle extras);
- void onMetadataUpdate(in Bundle metadata);
- void onPlaybackUpdate(int newState);
void onRouteChanged(in Bundle route);
+
+ // These callbacks are for the TransportController
+ void onPlaybackStateChanged(in PlaybackState state);
+ void onMetadataChanged(in MediaMetadata metadata);
}
\ No newline at end of file
diff --git a/media/java/android/media/session/IMediaSession.aidl b/media/java/android/media/session/IMediaSession.aidl
index 19f7092..aed7641 100644
--- a/media/java/android/media/session/IMediaSession.aidl
+++ b/media/java/android/media/session/IMediaSession.aidl
@@ -16,6 +16,8 @@
package android.media.session;
import android.media.session.IMediaController;
+import android.media.session.MediaMetadata;
+import android.media.session.PlaybackState;
import android.os.Bundle;
/**
@@ -23,11 +25,17 @@
* @hide
*/
interface IMediaSession {
- void sendEvent(in Bundle data);
- IMediaController getMediaSessionToken();
- void setPlaybackState(int state);
- void setMetadata(in Bundle metadata);
+ void sendEvent(String event, in Bundle data);
+ IMediaController getMediaController();
+ void setTransportPerformerEnabled();
void setRouteState(in Bundle routeState);
void setRoute(in Bundle mediaRouteDescriptor);
+ List<String> getSupportedInterfaces();
+ void publish();
void destroy();
+
+ // These commands are for the TransportPerformer
+ void setMetadata(in MediaMetadata metadata);
+ void setPlaybackState(in PlaybackState state);
+ void setRatingType(int type);
}
\ No newline at end of file
diff --git a/media/java/android/media/session/IMediaSessionCallback.aidl b/media/java/android/media/session/IMediaSessionCallback.aidl
index eb5f222..7c183e0 100644
--- a/media/java/android/media/session/IMediaSessionCallback.aidl
+++ b/media/java/android/media/session/IMediaSessionCallback.aidl
@@ -15,15 +15,27 @@
package android.media.session;
+import android.media.Rating;
import android.content.Intent;
import android.os.Bundle;
-import android.os.IBinder;
+import android.os.ResultReceiver;
/**
* @hide
*/
oneway interface IMediaSessionCallback {
- void onCommand(String command, in Bundle extras);
+ void onCommand(String command, in Bundle extras, in ResultReceiver cb);
void onMediaButton(in Intent mediaRequestIntent);
void onRequestRouteChange(in Bundle route);
+
+ // These callbacks are for the TransportPerformer
+ void onPlay();
+ void onPause();
+ void onStop();
+ void onNext();
+ void onPrevious();
+ void onFastForward();
+ void onRewind();
+ void onSeekTo(long pos);
+ void onRate(in Rating rating);
}
\ No newline at end of file
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 09de859..afd8b11 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -16,20 +16,17 @@
package android.media.session;
-import android.content.Intent;
-import android.media.session.IMediaController;
-import android.media.session.IMediaControllerCallback;
-import android.media.MediaMetadataRetriever;
-import android.media.RemoteControlClient;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
/**
@@ -46,58 +43,62 @@
public final class MediaController {
private static final String TAG = "MediaController";
- private static final int MESSAGE_EVENT = 1;
+ private static final int MSG_EVENT = 1;
private static final int MESSAGE_PLAYBACK_STATE = 2;
private static final int MESSAGE_METADATA = 3;
- private static final int MESSAGE_ROUTE = 4;
-
- private static final String KEY_EVENT = "event";
- private static final String KEY_EXTRAS = "extras";
+ private static final int MSG_ROUTE = 4;
private final IMediaController mSessionBinder;
- private final CallbackStub mCbStub = new CallbackStub();
- private final ArrayList<Callback> mCbs = new ArrayList<Callback>();
+ private final CallbackStub mCbStub = new CallbackStub(this);
+ private final ArrayList<MessageHandler> mCallbacks = new ArrayList<MessageHandler>();
private final Object mLock = new Object();
private boolean mCbRegistered = false;
- /**
- * If you have a {@link MediaSessionToken} from the owner of the session a
- * controller can be created directly. It is up to the session creator to
- * handle token distribution if desired.
- *
- * @see MediaSession#getSessionToken()
- * @param token A token from the creator of the session
- */
- public MediaController(MediaSessionToken token) {
- mSessionBinder = token.getBinder();
+ private TransportController mTransportController;
+
+ private MediaController(IMediaController sessionBinder) {
+ mSessionBinder = sessionBinder;
}
/**
* @hide
*/
- public MediaController(IMediaController sessionBinder) {
- mSessionBinder = sessionBinder;
+ public static MediaController fromBinder(IMediaController sessionBinder) {
+ MediaController controller = new MediaController(sessionBinder);
+ try {
+ controller.mSessionBinder.registerCallbackListener(controller.mCbStub);
+ if (controller.mSessionBinder.isTransportControlEnabled()) {
+ controller.mTransportController = new TransportController(sessionBinder);
+ }
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "MediaController created with expired token", e);
+ controller = null;
+ }
+ return controller;
}
/**
- * Sends a generic command to the session. It is up to the session creator
- * to decide what commands and parameters they will support. As such,
- * commands should only be sent to sessions that the controller owns.
+ * Get a new MediaController for a MediaSessionToken. If successful the
+ * controller returned will be connected to the session that generated the
+ * token.
*
- * @param command The command to send
- * @param params Any parameters to include with the command
+ * @param token The session token to use
+ * @return A controller for the session or null
*/
- public void sendCommand(String command, Bundle params) {
- if (TextUtils.isEmpty(command)) {
- throw new IllegalArgumentException("command cannot be null or empty");
- }
- try {
- mSessionBinder.sendCommand(command, params);
- } catch (RemoteException e) {
- Log.d(TAG, "Dead object in sendCommand.", e);
- }
+ public static MediaController fromToken(MediaSessionToken token) {
+ return fromBinder(token.getBinder());
+ }
+
+ /**
+ * Get a TransportController if the session supports it. If it is not
+ * supported null will be returned.
+ *
+ * @return A TransportController or null
+ */
+ public TransportController getTransportController() {
+ return mTransportController;
}
/**
@@ -133,10 +134,10 @@
/**
* Adds a callback to receive updates from the session. Updates will be
- * posted on the specified handler.
+ * posted on the specified handler's thread.
*
* @param cb Cannot be null.
- * @param handler The handler to post updates on, if null the callers thread
+ * @param handler The handler to post updates on. If null the callers thread
* will be used
*/
public void addCallback(Callback cb, Handler handler) {
@@ -160,6 +161,26 @@
}
}
+ /**
+ * Sends a generic command to the session. It is up to the session creator
+ * to decide what commands and parameters they will support. As such,
+ * commands should only be sent to sessions that the controller owns.
+ *
+ * @param command The command to send
+ * @param params Any parameters to include with the command
+ * @param cb The callback to receive the result on
+ */
+ public void sendCommand(String command, Bundle params, ResultReceiver cb) {
+ if (TextUtils.isEmpty(command)) {
+ throw new IllegalArgumentException("command cannot be null or empty");
+ }
+ try {
+ mSessionBinder.sendCommand(command, params, cb);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Dead object in sendCommand.", e);
+ }
+ }
+
/*
* @hide
*/
@@ -174,14 +195,13 @@
if (handler == null) {
throw new IllegalArgumentException("Handler cannot be null");
}
- if (mCbs.contains(cb)) {
+ if (getHandlerForCallbackLocked(cb) != null) {
Log.w(TAG, "Callback is already added, ignoring");
return;
}
- cb.setHandler(handler);
- mCbs.add(cb);
+ MessageHandler holder = new MessageHandler(handler.getLooper(), cb);
+ mCallbacks.add(holder);
- // Only register one cb binder, track callbacks internally and notify
if (!mCbRegistered) {
try {
mSessionBinder.registerCallbackListener(mCbStub);
@@ -192,56 +212,58 @@
}
}
- private void removeCallbackLocked(Callback cb) {
+ private boolean removeCallbackLocked(Callback cb) {
if (cb == null) {
throw new IllegalArgumentException("Callback cannot be null");
}
- mCbs.remove(cb);
-
- if (mCbs.size() == 0 && mCbRegistered) {
- try {
- mSessionBinder.unregisterCallbackListener(mCbStub);
- } catch (RemoteException e) {
- Log.d(TAG, "Dead object in unregisterCallback", e);
+ for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+ MessageHandler handler = mCallbacks.get(i);
+ if (cb == handler.mCallback) {
+ mCallbacks.remove(i);
+ return true;
}
- mCbRegistered = false;
+ }
+ return false;
+ }
+
+ private MessageHandler getHandlerForCallbackLocked(Callback cb) {
+ if (cb == null) {
+ throw new IllegalArgumentException("Callback cannot be null");
+ }
+ for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+ MessageHandler handler = mCallbacks.get(i);
+ if (cb == handler.mCallback) {
+ return handler;
+ }
+ }
+ return null;
+ }
+
+ private void postEvent(String event, Bundle extras) {
+ synchronized (mLock) {
+ for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+ mCallbacks.get(i).post(MSG_EVENT, event, extras);
+ }
}
}
- private void pushOnEventLocked(String event, Bundle extras) {
- for (int i = mCbs.size() - 1; i >= 0; i--) {
- mCbs.get(i).postEvent(event, extras);
- }
- }
-
- private void pushOnMetadataUpdateLocked(Bundle metadata) {
- for (int i = mCbs.size() - 1; i >= 0; i--) {
- mCbs.get(i).postMetadataUpdate(metadata);
- }
- }
-
- private void pushOnPlaybackUpdateLocked(int newState) {
- for (int i = mCbs.size() - 1; i >= 0; i--) {
- mCbs.get(i).postPlaybackStateChange(newState);
- }
- }
-
- private void pushOnRouteChangedLocked(Bundle routeDescriptor) {
- for (int i = mCbs.size() - 1; i >= 0; i--) {
- mCbs.get(i).postRouteChanged(routeDescriptor);
+ private void postRouteChanged(Bundle routeDescriptor) {
+ synchronized (mLock) {
+ for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+ mCallbacks.get(i).post(MSG_ROUTE, null, routeDescriptor);
+ }
}
}
/**
- * MediaSession callbacks will be posted on the thread that created the
- * Callback object.
+ * Callback for receiving updates on from the session. A Callback can be
+ * registered using {@link #addCallback}
*/
public static abstract class Callback {
- private Handler mHandler;
-
/**
- * Override to handle custom events sent by the session owner.
- * Controllers should only handle these for sessions they own.
+ * Override to handle custom events sent by the session owner without a
+ * specified interface. Controllers should only handle these for
+ * sessions they own.
*
* @param event
*/
@@ -249,119 +271,83 @@
}
/**
- * Override to handle updates to the playback state. Valid values are in
- * {@link RemoteControlClient}. TODO put playstate values somewhere more
- * generic.
- *
- * @param state
- */
- public void onPlaybackStateChange(int state) {
- }
-
- /**
- * Override to handle metadata changes for this session's media. The
- * default supported fields are those in {@link MediaMetadataRetriever}.
- *
- * @param metadata
- */
- public void onMetadataUpdate(Bundle metadata) {
- }
-
- /**
* Override to handle route changes for this session.
*
* @param route
*/
public void onRouteChanged(Bundle route) {
}
-
- private void setHandler(Handler handler) {
- mHandler = new MessageHandler(handler.getLooper(), this);
- }
-
- private void postEvent(String event, Bundle extras) {
- Bundle eventBundle = new Bundle();
- eventBundle.putString(KEY_EVENT, event);
- eventBundle.putBundle(KEY_EXTRAS, extras);
- Message msg = mHandler.obtainMessage(MESSAGE_EVENT, eventBundle);
- mHandler.sendMessage(msg);
- }
-
- private void postPlaybackStateChange(final int state) {
- Message msg = mHandler.obtainMessage(MESSAGE_PLAYBACK_STATE, state, 0);
- mHandler.sendMessage(msg);
- }
-
- private void postMetadataUpdate(final Bundle metadata) {
- Message msg = mHandler.obtainMessage(MESSAGE_METADATA, metadata);
- mHandler.sendMessage(msg);
- }
-
- private void postRouteChanged(final Bundle descriptor) {
- Message msg = mHandler.obtainMessage(MESSAGE_ROUTE, descriptor);
- mHandler.sendMessage(msg);
- }
}
- private final class CallbackStub extends IMediaControllerCallback.Stub {
+ private final static class CallbackStub extends IMediaControllerCallback.Stub {
+ private final WeakReference<MediaController> mController;
+
+ public CallbackStub(MediaController controller) {
+ mController = new WeakReference<MediaController>(controller);
+ }
@Override
- public void onEvent(String event, Bundle extras) throws RemoteException {
- synchronized (mLock) {
- pushOnEventLocked(event, extras);
+ public void onEvent(String event, Bundle extras) {
+ MediaController controller = mController.get();
+ if (controller != null) {
+ controller.postEvent(event, extras);
}
}
@Override
- public void onMetadataUpdate(Bundle metadata) throws RemoteException {
- synchronized (mLock) {
- pushOnMetadataUpdateLocked(metadata);
+ public void onRouteChanged(Bundle mediaRouteDescriptor) {
+ MediaController controller = mController.get();
+ if (controller != null) {
+ controller.postRouteChanged(mediaRouteDescriptor);
}
}
@Override
- public void onPlaybackUpdate(final int newState) throws RemoteException {
- synchronized (mLock) {
- pushOnPlaybackUpdateLocked(newState);
+ public void onPlaybackStateChanged(PlaybackState state) {
+ MediaController controller = mController.get();
+ if (controller != null) {
+ TransportController tc = controller.getTransportController();
+ if (tc != null) {
+ tc.postPlaybackStateChanged(state);
+ }
}
}
@Override
- public void onRouteChanged(Bundle mediaRouteDescriptor) throws RemoteException {
- synchronized (mLock) {
- pushOnRouteChangedLocked(mediaRouteDescriptor);
+ public void onMetadataChanged(MediaMetadata metadata) {
+ MediaController controller = mController.get();
+ if (controller != null) {
+ TransportController tc = controller.getTransportController();
+ if (tc != null) {
+ tc.postMetadataChanged(metadata);
+ }
}
}
}
private final static class MessageHandler extends Handler {
- private final MediaController.Callback mCb;
+ private final MediaController.Callback mCallback;
public MessageHandler(Looper looper, MediaController.Callback cb) {
- super(looper);
- mCb = cb;
+ super(looper, null, true);
+ mCallback = cb;
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MESSAGE_EVENT:
- Bundle eventBundle = (Bundle) msg.obj;
- String event = eventBundle.getString(KEY_EVENT);
- Bundle extras = eventBundle.getBundle(KEY_EXTRAS);
- mCb.onEvent(event, extras);
+ case MSG_EVENT:
+ mCallback.onEvent((String) msg.obj, msg.getData());
break;
- case MESSAGE_PLAYBACK_STATE:
- mCb.onPlaybackStateChange(msg.arg1);
- break;
- case MESSAGE_METADATA:
- mCb.onMetadataUpdate((Bundle) msg.obj);
- break;
- case MESSAGE_ROUTE:
- mCb.onRouteChanged((Bundle) msg.obj);
+ case MSG_ROUTE:
+ mCallback.onRouteChanged(msg.getData());
}
}
+
+ public void post(int what, Object obj, Bundle data) {
+ obtainMessage(what, obj).sendToTarget();
+ }
}
}
diff --git a/media/java/android/media/session/MediaMetadata.aidl b/media/java/android/media/session/MediaMetadata.aidl
new file mode 100644
index 0000000..4431d9d
--- /dev/null
+++ b/media/java/android/media/session/MediaMetadata.aidl
@@ -0,0 +1,18 @@
+/* Copyright 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 android.media.session;
+
+parcelable MediaMetadata;
diff --git a/media/java/android/media/session/MediaMetadata.java b/media/java/android/media/session/MediaMetadata.java
new file mode 100644
index 0000000..e2330f7
--- /dev/null
+++ b/media/java/android/media/session/MediaMetadata.java
@@ -0,0 +1,404 @@
+/*
+ * 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 android.media.session;
+
+import android.graphics.Bitmap;
+import android.media.Rating;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+import android.util.Log;
+
+/**
+ * Contains metadata about an item, such as the title, artist, etc.
+ */
+public final class MediaMetadata implements Parcelable {
+ private static final String TAG = "MediaMetadata";
+
+ /**
+ * The title of the media.
+ */
+ public static final String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
+
+ /**
+ * The artist of the media.
+ */
+ public static final String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
+
+ /**
+ * The duration of the media in ms. A duration of 0 is the default.
+ */
+ public static final String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
+
+ /**
+ * The album title for the media.
+ */
+ public static final String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
+
+ /**
+ * The author of the media.
+ */
+ public static final String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
+
+ /**
+ * The writer of the media.
+ */
+ public static final String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
+
+ /**
+ * The composer of the media.
+ */
+ public static final String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
+
+ /**
+ * The date the media was created or published as TODO determine format.
+ */
+ public static final String METADATA_KEY_DATE = "android.media.metadata.DATE";
+
+ /**
+ * The year the media was created or published as a numeric String.
+ */
+ public static final String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
+
+ /**
+ * The genre of the media.
+ */
+ public static final String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
+
+ /**
+ * The track number for the media.
+ */
+ public static final String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
+
+ /**
+ * The number of tracks in the media's original source.
+ */
+ public static final String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
+
+ /**
+ * The disc number for the media's original source.
+ */
+ public static final String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
+
+ /**
+ * The artist for the album of the media's original source.
+ */
+ public static final String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
+
+ /**
+ * The artwork for the media as a {@link Bitmap}.
+ */
+ public static final String METADATA_KEY_ART = "android.media.metadata.ART";
+
+ /**
+ * The artwork for the media as a Uri style String.
+ */
+ public static final String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
+
+ /**
+ * The artwork for the album of the media's original source as a
+ * {@link Bitmap}.
+ */
+ public static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
+
+ /**
+ * The artwork for the album of the media's original source as a Uri style
+ * String.
+ */
+ public static final String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
+
+ /**
+ * The user's rating for the media.
+ *
+ * @see Rating
+ */
+ public static final String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
+
+ /**
+ * The overall rating for the media.
+ *
+ * @see Rating
+ */
+ public static final String METADATA_KEY_RATING = "android.media.metadata.RATING";
+
+ private static final int METADATA_TYPE_INVALID = -1;
+ private static final int METADATA_TYPE_LONG = 0;
+ private static final int METADATA_TYPE_STRING = 1;
+ private static final int METADATA_TYPE_BITMAP = 2;
+ private static final int METADATA_TYPE_RATING = 3;
+ private static final ArrayMap<String, Integer> METADATA_KEYS_TYPE;
+
+ static {
+ METADATA_KEYS_TYPE = new ArrayMap<String, Integer>();
+ METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_STRING);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_STRING);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_DURATION, METADATA_TYPE_LONG);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_STRING);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_AUTHOR, METADATA_TYPE_STRING);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_WRITER, METADATA_TYPE_STRING);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_COMPOSER, METADATA_TYPE_STRING);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_DATE, METADATA_TYPE_STRING);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_YEAR, METADATA_TYPE_STRING);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_STRING);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_TRACK_NUMBER, METADATA_TYPE_LONG);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_NUM_TRACKS, METADATA_TYPE_LONG);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ARTIST, METADATA_TYPE_STRING);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_ART_URI, METADATA_TYPE_STRING);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART, METADATA_TYPE_BITMAP);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART_URI, METADATA_TYPE_STRING);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_USER_RATING, METADATA_TYPE_RATING);
+ METADATA_KEYS_TYPE.put(METADATA_KEY_RATING, METADATA_TYPE_RATING);
+ }
+ private final Bundle mBundle;
+
+ private MediaMetadata(Bundle bundle) {
+ mBundle = new Bundle(bundle);
+ }
+
+ private MediaMetadata(Parcel in) {
+ mBundle = in.readBundle();
+ }
+
+ /**
+ * Returns the value associated with the given key, or null if no mapping of
+ * the desired type exists for the given key or a null value is explicitly
+ * associated with the key.
+ *
+ * @param key The key the value is stored under
+ * @return a String value, or null
+ */
+ public String getString(String key) {
+ return mBundle.getString(key);
+ }
+
+ /**
+ * Returns the value associated with the given key, or 0L if no long exists
+ * for the given key.
+ *
+ * @param key The key the value is stored under
+ * @return a long value
+ */
+ public long getLong(String key) {
+ return mBundle.getLong(key);
+ }
+
+ /**
+ * Return a {@link Rating} for the given key or null if no rating exists for
+ * the given key.
+ *
+ * @param key The key the value is stored under
+ * @return A {@link Rating} or null
+ */
+ public Rating getRating(String key) {
+ Rating rating = null;
+ try {
+ rating = mBundle.getParcelable(key);
+ } catch (Exception e) {
+ // ignore, value was not a bitmap
+ Log.d(TAG, "Failed to retrieve a key as Rating.", e);
+ }
+ return rating;
+ }
+
+ /**
+ * Return a {@link Bitmap} for the given key or null if no bitmap exists for
+ * the given key.
+ *
+ * @param key The key the value is stored under
+ * @return A {@link Bitmap} or null
+ */
+ public Bitmap getBitmap(String key) {
+ Bitmap bmp = null;
+ try {
+ bmp = mBundle.getParcelable(key);
+ } catch (Exception e) {
+ // ignore, value was not a bitmap
+ Log.d(TAG, "Failed to retrieve a key as Bitmap.", e);
+ }
+ return bmp;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeBundle(mBundle);
+ }
+
+ public static final Parcelable.Creator<MediaMetadata> CREATOR
+ = new Parcelable.Creator<MediaMetadata>() {
+ @Override
+ public MediaMetadata createFromParcel(Parcel in) {
+ return new MediaMetadata(in);
+ }
+
+ @Override
+ public MediaMetadata[] newArray(int size) {
+ return new MediaMetadata[size];
+ }
+ };
+
+ /**
+ * Use to build MediaMetadata objects. The system defined metadata keys must
+ * use the appropriate data type.
+ */
+ public static final class Builder {
+ private final Bundle mBundle;
+
+ /**
+ * Create an empty Builder. Any field that should be included in the
+ * {@link MediaMetadata} must be added.
+ */
+ public Builder() {
+ mBundle = new Bundle();
+ }
+
+ /**
+ * Create a Builder using a {@link MediaMetadata} instance to set the
+ * initial values. All fields in the source metadata will be included in
+ * the new metadata. Fields can be overwritten by adding the same key.
+ *
+ * @param source
+ */
+ public Builder(MediaMetadata source) {
+ mBundle = new Bundle(source.mBundle);
+ }
+
+ /**
+ * Put a String value into the metadata. Custom keys may be used, but if
+ * the METADATA_KEYs defined in this class are used they may only be one
+ * of the following:
+ * <ul>
+ * <li>{@link #METADATA_KEY_TITLE}</li>
+ * <li>{@link #METADATA_KEY_ARTIST}</li>
+ * <li>{@link #METADATA_KEY_ALBUM}</li>
+ * <li>{@link #METADATA_KEY_AUTHOR}</li>
+ * <li>{@link #METADATA_KEY_WRITER}</li>
+ * <li>{@link #METADATA_KEY_COMPOSER}</li>
+ * <li>{@link #METADATA_KEY_DATE}</li>
+ * <li>{@link #METADATA_KEY_YEAR}</li>
+ * <li>{@link #METADATA_KEY_GENRE}</li>
+ * <li>{@link #METADATA_KEY_ALBUM_ARTIST}</li>li>
+ * <li>{@link #METADATA_KEY_ART_URI}</li>li>
+ * <li>{@link #METADATA_KEY_ALBUM_ART_URI}</li>
+ * </ul>
+ *
+ * @param key The key for referencing this value
+ * @param value The String value to store
+ * @return The Builder to allow chaining
+ */
+ public Builder putString(String key, String value) {
+ if (METADATA_KEYS_TYPE.containsKey(key)) {
+ if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_STRING) {
+ throw new IllegalArgumentException("The " + key
+ + " key cannot be used to put a String");
+ }
+ }
+ mBundle.putString(key, value);
+ return this;
+ }
+
+ /**
+ * Put a long value into the metadata. Custom keys may be used, but if
+ * the METADATA_KEYs defined in this class are used they may only be one
+ * of the following:
+ * <ul>
+ * <li>{@link #METADATA_KEY_DURATION}</li>
+ * <li>{@link #METADATA_KEY_TRACK_NUMBER}</li>
+ * <li>{@link #METADATA_KEY_NUM_TRACKS}</li>
+ * <li>{@link #METADATA_KEY_DISC_NUMBER}</li>
+ * </ul>
+ *
+ * @param key The key for referencing this value
+ * @param value The String value to store
+ * @return The Builder to allow chaining
+ */
+ public Builder putLong(String key, long value) {
+ if (METADATA_KEYS_TYPE.containsKey(key)) {
+ if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_LONG) {
+ throw new IllegalArgumentException("The " + key
+ + " key cannot be used to put a long");
+ }
+ }
+ mBundle.putLong(key, value);
+ return this;
+ }
+
+ /**
+ * Put a {@link Rating} into the metadata. Custom keys may be used, but
+ * if the METADATA_KEYs defined in this class are used they may only be
+ * one of the following:
+ * <ul>
+ * <li>{@link #METADATA_KEY_RATING}</li>
+ * <li>{@link #METADATA_KEY_USER_RATING}</li>
+ * </ul>
+ *
+ * @param key The key for referencing this value
+ * @param value The String value to store
+ * @return The Builder to allow chaining
+ */
+ public Builder putRating(String key, Rating value) {
+ if (METADATA_KEYS_TYPE.containsKey(key)) {
+ if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_RATING) {
+ throw new IllegalArgumentException("The " + key
+ + " key cannot be used to put a Rating");
+ }
+ }
+ mBundle.putParcelable(key, value);
+ return this;
+ }
+
+ /**
+ * Put a {@link Bitmap} into the metadata. Custom keys may be used, but
+ * if the METADATA_KEYs defined in this class are used they may only be
+ * one of the following:
+ * <ul>
+ * <li>{@link #METADATA_KEY_ART}</li>
+ * <li>{@link #METADATA_KEY_ALBUM_ART}</li>
+ * </ul>
+ *
+ * @param key The key for referencing this value
+ * @param value The Bitmap to store
+ * @return The Builder to allow chaining
+ */
+ public Builder putBitmap(String key, Bitmap value) {
+ if (METADATA_KEYS_TYPE.containsKey(key)) {
+ if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) {
+ throw new IllegalArgumentException("The " + key
+ + " key cannot be used to put a Bitmap");
+ }
+ }
+ mBundle.putParcelable(key, value);
+ return this;
+ }
+
+ /**
+ * Creates a {@link MediaMetadata} instance with the specified fields.
+ *
+ * @return The new MediaMetadata instance
+ */
+ public MediaMetadata build() {
+ return new MediaMetadata(mBundle);
+ }
+ }
+
+}
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 1f1533b..23c3035 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -17,17 +17,21 @@
package android.media.session;
import android.content.Intent;
+import android.media.Rating;
import android.media.session.IMediaController;
import android.media.session.IMediaSession;
import android.media.session.IMediaSessionCallback;
-import android.media.RemoteControlClient;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Log;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
/**
@@ -58,12 +62,13 @@
public final class MediaSession {
private static final String TAG = "MediaSession";
- private static final int MESSAGE_MEDIA_BUTTON = 1;
- private static final int MESSAGE_COMMAND = 2;
- private static final int MESSAGE_ROUTE_CHANGE = 3;
+ private static final int MSG_MEDIA_BUTTON = 1;
+ private static final int MSG_COMMAND = 2;
+ private static final int MSG_ROUTE_CHANGE = 3;
private static final String KEY_COMMAND = "command";
private static final String KEY_EXTRAS = "extras";
+ private static final String KEY_CALLBACK = "callback";
private final Object mLock = new Object();
@@ -71,7 +76,14 @@
private final IMediaSession mBinder;
private final CallbackStub mCbStub;
- private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
+ private final ArrayList<MessageHandler> mCallbacks = new ArrayList<MessageHandler>();
+ // TODO route interfaces
+ private final ArrayMap<String, RouteInterface.Stub> mInterfaces
+ = new ArrayMap<String, RouteInterface.Stub>();
+
+ private TransportPerformer mPerformer;
+
+ private boolean mPublished = false;;
/**
* @hide
@@ -81,7 +93,7 @@
mCbStub = cbStub;
IMediaController controllerBinder = null;
try {
- controllerBinder = mBinder.getMediaSessionToken();
+ controllerBinder = mBinder.getMediaController();
} catch (RemoteException e) {
throw new RuntimeException("Dead object in MediaSessionController constructor: ", e);
}
@@ -102,34 +114,117 @@
throw new IllegalArgumentException("Callback cannot be null");
}
synchronized (mLock) {
- if (mCallbacks.contains(callback)) {
+ if (getHandlerForCallbackLocked(callback) != null) {
Log.w(TAG, "Callback is already added, ignoring");
+ return;
}
if (handler == null) {
handler = new Handler();
}
MessageHandler msgHandler = new MessageHandler(handler.getLooper(), callback);
- callback.setHandler(msgHandler);
- mCallbacks.add(callback);
+ mCallbacks.add(msgHandler);
}
}
public void removeCallback(Callback callback) {
- mCallbacks.remove(callback);
+ synchronized (mLock) {
+ removeCallbackLocked(callback);
+ }
}
/**
- * Publish the current playback state to the system and any controllers.
- * Valid values are defined in {@link RemoteControlClient}. TODO move play
- * states somewhere else.
+ * Start using a TransportPerformer with this media session. This must be
+ * called before calling publish and cannot be called more than once.
+ * Calling this will allow MediaControllers to retrieve a
+ * TransportController.
*
- * @param state
+ * @see TransportController
+ * @return The TransportPerformer created for this session
*/
- public void setPlaybackState(int state) {
+ public TransportPerformer setTransportPerformerEnabled() {
+ if (mPerformer != null) {
+ throw new IllegalStateException("setTransportPerformer can only be called once.");
+ }
+ if (mPublished) {
+ throw new IllegalStateException("setTransportPerformer cannot be called after publish");
+ }
+
+ mPerformer = new TransportPerformer(mBinder);
try {
- mBinder.setPlaybackState(state);
+ mBinder.setTransportPerformerEnabled();
} catch (RemoteException e) {
- Log.e(TAG, "Dead object in setPlaybackState: ", e);
+ Log.wtf(TAG, "Failure in setTransportPerformerEnabled.", e);
+ }
+ return mPerformer;
+ }
+
+ /**
+ * Retrieves the TransportPerformer used by this session. If called before
+ * {@link #setTransportPerformerEnabled} null will be returned.
+ *
+ * @return The TransportPerformer associated with this session or null
+ */
+ public TransportPerformer getTransportPerformer() {
+ return mPerformer;
+ }
+
+ /**
+ * Call after you have finished setting up the session. This will make it
+ * available to listeners and begin pushing updates to MediaControllers.
+ * This can only be called once.
+ */
+ public void publish() {
+ if (mPublished) {
+ throw new RuntimeException("publish() may only be called once.");
+ }
+ try {
+ mBinder.publish();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Failure in publish.", e);
+ }
+ mPublished = true;
+ }
+
+ /**
+ * Add an interface that can be used by MediaSessions. TODO make this a
+ * route provider api
+ *
+ * @see RouteInterface
+ * @param iface The interface to add
+ * @hide
+ */
+ public void addInterface(RouteInterface.Stub iface) {
+ if (iface == null) {
+ throw new IllegalArgumentException("Stub cannot be null");
+ }
+ String name = iface.getName();
+ if (TextUtils.isEmpty(name)) {
+ throw new IllegalArgumentException("Stub must return a valid name");
+ }
+ if (mInterfaces.containsKey(iface)) {
+ throw new IllegalArgumentException("Interface is already added");
+ }
+ synchronized (mLock) {
+ mInterfaces.put(iface.getName(), iface);
+ }
+ }
+
+ /**
+ * Send a proprietary event to all MediaControllers listening to this
+ * Session. It's up to the Controller/Session owner to determine the meaning
+ * of any events.
+ *
+ * @param event The name of the event to send
+ * @param extras Any extras included with the event
+ */
+ public void sendEvent(String event, Bundle extras) {
+ if (TextUtils.isEmpty(event)) {
+ throw new IllegalArgumentException("event cannot be null or empty");
+ }
+ try {
+ mBinder.sendEvent(event, extras);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error sending event", e);
}
}
@@ -142,7 +237,7 @@
try {
mBinder.destroy();
} catch (RemoteException e) {
- Log.e(TAG, "Dead object in onDestroy: ", e);
+ Log.wtf(TAG, "Error releasing session: ", e);
}
}
@@ -158,15 +253,38 @@
return mSessionToken;
}
- private void postCommand(String command, Bundle extras) {
- Bundle commandBundle = new Bundle();
- commandBundle.putString(KEY_COMMAND, command);
- commandBundle.putBundle(KEY_EXTRAS, extras);
+ private MessageHandler getHandlerForCallbackLocked(Callback cb) {
+ if (cb == null) {
+ throw new IllegalArgumentException("Callback cannot be null");
+ }
+ for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+ MessageHandler handler = mCallbacks.get(i);
+ if (cb == handler.mCallback) {
+ return handler;
+ }
+ }
+ return null;
+ }
+
+ private boolean removeCallbackLocked(Callback cb) {
+ if (cb == null) {
+ throw new IllegalArgumentException("Callback cannot be null");
+ }
+ for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+ MessageHandler handler = mCallbacks.get(i);
+ if (cb == handler.mCallback) {
+ mCallbacks.remove(i);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void postCommand(String command, Bundle extras, ResultReceiver resultCb) {
+ Command cmd = new Command(command, extras, resultCb);
synchronized (mLock) {
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- Callback cb = mCallbacks.get(i);
- Message msg = cb.mHandler.obtainMessage(MESSAGE_COMMAND, commandBundle);
- cb.mHandler.sendMessage(msg);
+ mCallbacks.get(i).post(MSG_COMMAND, cmd);
}
}
}
@@ -174,9 +292,7 @@
private void postMediaButton(Intent mediaButtonIntent) {
synchronized (mLock) {
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- Callback cb = mCallbacks.get(i);
- Message msg = cb.mHandler.obtainMessage(MESSAGE_MEDIA_BUTTON, mediaButtonIntent);
- cb.mHandler.sendMessage(msg);
+ mCallbacks.get(i).post(MSG_MEDIA_BUTTON, mediaButtonIntent);
}
}
}
@@ -184,9 +300,7 @@
private void postRequestRouteChange(Bundle mediaRouteDescriptor) {
synchronized (mLock) {
for (int i = mCallbacks.size() - 1; i >= 0; i--) {
- Callback cb = mCallbacks.get(i);
- Message msg = cb.mHandler.obtainMessage(MESSAGE_ROUTE_CHANGE, mediaRouteDescriptor);
- cb.mHandler.sendMessage(msg);
+ mCallbacks.get(i).post(MSG_ROUTE_CHANGE, mediaRouteDescriptor);
}
}
}
@@ -197,7 +311,6 @@
* MediaSession (TODO).
*/
public abstract static class Callback {
- private MessageHandler mHandler;
public Callback() {
}
@@ -225,7 +338,7 @@
* @param command
* @param extras optional
*/
- public void onCommand(String command, Bundle extras) {
+ public void onCommand(String command, Bundle extras, ResultReceiver cb) {
}
/**
@@ -237,35 +350,140 @@
*/
public void onRequestRouteChange(Bundle descriptor) {
}
-
- private void setHandler(MessageHandler handler) {
- mHandler = handler;
- }
}
/**
* @hide
*/
public static class CallbackStub extends IMediaSessionCallback.Stub {
- private MediaSession mMediaSession;
+ private WeakReference<MediaSession> mMediaSession;
public void setMediaSession(MediaSession session) {
- mMediaSession = session;
+ mMediaSession = new WeakReference<MediaSession>(session);
}
@Override
- public void onCommand(String command, Bundle extras) throws RemoteException {
- mMediaSession.postCommand(command, extras);
+ public void onCommand(String command, Bundle extras, ResultReceiver cb)
+ throws RemoteException {
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ session.postCommand(command, extras, cb);
+ }
}
@Override
public void onMediaButton(Intent mediaButtonIntent) throws RemoteException {
- mMediaSession.postMediaButton(mediaButtonIntent);
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ session.postMediaButton(mediaButtonIntent);
+ }
}
@Override
public void onRequestRouteChange(Bundle mediaRouteDescriptor) throws RemoteException {
- mMediaSession.postRequestRouteChange(mediaRouteDescriptor);
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ session.postRequestRouteChange(mediaRouteDescriptor);
+ }
+ }
+
+ @Override
+ public void onPlay() throws RemoteException {
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ TransportPerformer tp = session.getTransportPerformer();
+ if (tp != null) {
+ tp.onPlay();
+ }
+ }
+ }
+
+ @Override
+ public void onPause() throws RemoteException {
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ TransportPerformer tp = session.getTransportPerformer();
+ if (tp != null) {
+ tp.onPause();
+ }
+ }
+ }
+
+ @Override
+ public void onStop() throws RemoteException {
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ TransportPerformer tp = session.getTransportPerformer();
+ if (tp != null) {
+ tp.onStop();
+ }
+ }
+ }
+
+ @Override
+ public void onNext() throws RemoteException {
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ TransportPerformer tp = session.getTransportPerformer();
+ if (tp != null) {
+ tp.onNext();
+ }
+ }
+ }
+
+ @Override
+ public void onPrevious() throws RemoteException {
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ TransportPerformer tp = session.getTransportPerformer();
+ if (tp != null) {
+ tp.onPrevious();
+ }
+ }
+ }
+
+ @Override
+ public void onFastForward() throws RemoteException {
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ TransportPerformer tp = session.getTransportPerformer();
+ if (tp != null) {
+ tp.onFastForward();
+ }
+ }
+ }
+
+ @Override
+ public void onRewind() throws RemoteException {
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ TransportPerformer tp = session.getTransportPerformer();
+ if (tp != null) {
+ tp.onRewind();
+ }
+ }
+ }
+
+ @Override
+ public void onSeekTo(long pos) throws RemoteException {
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ TransportPerformer tp = session.getTransportPerformer();
+ if (tp != null) {
+ tp.onSeekTo(pos);
+ }
+ }
+ }
+
+ @Override
+ public void onRate(Rating rating) throws RemoteException {
+ MediaSession session = mMediaSession.get();
+ if (session != null) {
+ TransportPerformer tp = session.getTransportPerformer();
+ if (tp != null) {
+ tp.onRate(rating);
+ }
+ }
}
}
@@ -274,7 +492,7 @@
private MediaSession.Callback mCallback;
public MessageHandler(Looper looper, MediaSession.Callback callback) {
- super(looper);
+ super(looper, null, true);
mCallback = callback;
}
@@ -285,21 +503,35 @@
return;
}
switch (msg.what) {
- case MESSAGE_MEDIA_BUTTON:
+ case MSG_MEDIA_BUTTON:
mCallback.onMediaButton((Intent) msg.obj);
break;
- case MESSAGE_COMMAND:
- Bundle commandBundle = (Bundle) msg.obj;
- String command = commandBundle.getString(KEY_COMMAND);
- Bundle extras = commandBundle.getBundle(KEY_EXTRAS);
- mCallback.onCommand(command, extras);
+ case MSG_COMMAND:
+ Command cmd = (Command) msg.obj;
+ mCallback.onCommand(cmd.command, cmd.extras, cmd.stub);
break;
- case MESSAGE_ROUTE_CHANGE:
+ case MSG_ROUTE_CHANGE:
mCallback.onRequestRouteChange((Bundle) msg.obj);
break;
}
}
msg.recycle();
}
+
+ public void post(int what, Object obj) {
+ obtainMessage(what, obj).sendToTarget();
+ }
+ }
+
+ private static final class Command {
+ public final String command;
+ public final Bundle extras;
+ public final ResultReceiver stub;
+
+ public Command(String command, Bundle extras, ResultReceiver stub) {
+ this.command = command;
+ this.extras = extras;
+ this.stub = stub;
+ }
}
}
diff --git a/media/java/android/media/session/PlaybackState.aidl b/media/java/android/media/session/PlaybackState.aidl
new file mode 100644
index 0000000..0876ebd
--- /dev/null
+++ b/media/java/android/media/session/PlaybackState.aidl
@@ -0,0 +1,18 @@
+/* Copyright 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 android.media.session;
+
+parcelable PlaybackState;
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
new file mode 100644
index 0000000..b3506b3
--- /dev/null
+++ b/media/java/android/media/session/PlaybackState.java
@@ -0,0 +1,351 @@
+/*
+ * 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 android.media.session;
+
+import android.media.RemoteControlClient;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Playback state for a {@link MediaSession}. This includes a state like
+ * {@link PlaybackState#PLAYSTATE_PLAYING}, the current playback position,
+ * and the current control capabilities.
+ */
+public final class PlaybackState implements Parcelable {
+ /**
+ * Indicates this performer supports the stop command.
+ *
+ * @see #setActions
+ */
+ public static final long ACTION_STOP = 1 << 0;
+
+ /**
+ * Indicates this performer supports the pause command.
+ *
+ * @see #setActions
+ */
+ public static final long ACTION_PAUSE = 1 << 1;
+
+ /**
+ * Indicates this performer supports the play command.
+ *
+ * @see #setActions
+ */
+ public static final long ACTION_PLAY = 1 << 2;
+
+ /**
+ * Indicates this performer supports the rewind command.
+ *
+ * @see #setActions
+ */
+ public static final long ACTION_REWIND = 1 << 3;
+
+ /**
+ * Indicates this performer supports the previous command.
+ *
+ * @see #setActions
+ */
+ public static final long ACTION_PREVIOUS_ITEM = 1 << 4;
+
+ /**
+ * Indicates this performer supports the next command.
+ *
+ * @see #setActions
+ */
+ public static final long ACTION_NEXT_ITEM = 1 << 5;
+
+ /**
+ * Indicates this performer supports the fast forward command.
+ *
+ * @see #setActions
+ */
+ public static final long ACTION_FASTFORWARD = 1 << 6;
+
+ /**
+ * Indicates this performer supports the set rating command.
+ *
+ * @see #setActions
+ */
+ public static final long ACTION_RATING = 1 << 7;
+
+ /**
+ * Indicates this performer supports the seek to command.
+ *
+ * @see #setActions
+ */
+ public static final long ACTION_SEEK_TO = 1 << 8;
+
+ /**
+ * This is the default playback state and indicates that no media has been
+ * added yet, or the performer has been reset and has no content to play.
+ *
+ * @see #setState
+ */
+ public final static int PLAYSTATE_NONE = 0;
+
+ /**
+ * State indicating this item is currently stopped.
+ *
+ * @see #setState
+ */
+ public final static int PLAYSTATE_STOPPED = 1;
+
+ /**
+ * State indicating this item is currently paused.
+ *
+ * @see #setState
+ */
+ public final static int PLAYSTATE_PAUSED = 2;
+
+ /**
+ * State indicating this item is currently playing.
+ *
+ * @see #setState
+ */
+ public final static int PLAYSTATE_PLAYING = 3;
+
+ /**
+ * State indicating this item is currently fast forwarding.
+ *
+ * @see #setState
+ */
+ public final static int PLAYSTATE_FAST_FORWARDING = 4;
+
+ /**
+ * State indicating this item is currently rewinding.
+ *
+ * @see #setState
+ */
+ public final static int PLAYSTATE_REWINDING = 5;
+
+ /**
+ * State indicating this item is currently buffering and will begin playing
+ * when enough data has buffered.
+ *
+ * @see #setState
+ */
+ public final static int PLAYSTATE_BUFFERING = 6;
+
+ /**
+ * State indicating this item is currently in an error state. The error
+ * message should also be set when entering this state.
+ *
+ * @see #setState
+ */
+ public final static int PLAYSTATE_ERROR = 7;
+
+ private int mState;
+ private long mPosition;
+ private long mBufferPosition;
+ private float mSpeed;
+ private long mCapabilities;
+ private String mErrorMessage;
+
+ /**
+ * Create an empty PlaybackState. At minimum a state and actions should be
+ * set before publishing a PlaybackState.
+ */
+ public PlaybackState() {
+ }
+
+ /**
+ * Create a new PlaybackState from an existing PlaybackState. All fields
+ * will be copied to the new state.
+ *
+ * @param from The PlaybackState to duplicate
+ */
+ public PlaybackState(PlaybackState from) {
+ this.setState(from.getState());
+ this.setPosition(from.getPosition());
+ this.setBufferPosition(from.getBufferPosition());
+ this.setSpeed(from.getSpeed());
+ this.setActions(from.getActions());
+ this.setErrorMessage(from.getErrorMessage());
+ }
+
+ private PlaybackState(Parcel in) {
+ this.setState(in.readInt());
+ this.setPosition(in.readLong());
+ this.setBufferPosition(in.readLong());
+ this.setSpeed(in.readFloat());
+ this.setActions(in.readLong());
+ this.setErrorMessage(in.readString());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(getState());
+ dest.writeLong(getPosition());
+ dest.writeLong(getBufferPosition());
+ dest.writeFloat(getSpeed());
+ dest.writeLong(getActions());
+ dest.writeString(getErrorMessage());
+ }
+
+ /**
+ * Get the current state of playback. One of the following:
+ * <ul>
+ * <li> {@link PlaybackState#PLAYSTATE_NONE}</li>
+ * <li> {@link PlaybackState#PLAYSTATE_STOPPED}</li>
+ * <li> {@link PlaybackState#PLAYSTATE_PLAYING}</li>
+ * <li> {@link PlaybackState#PLAYSTATE_PAUSED}</li>
+ * <li> {@link PlaybackState#PLAYSTATE_FAST_FORWARDING}</li>
+ * <li> {@link PlaybackState#PLAYSTATE_REWINDING}</li>
+ * <li> {@link PlaybackState#PLAYSTATE_BUFFERING}</li>
+ * <li> {@link PlaybackState#PLAYSTATE_ERROR}</li>
+ */
+ public int getState() {
+ return mState;
+ }
+
+ /**
+ * Set the current state of playback. One of the following:
+ * <ul>
+ * <li> {@link PlaybackState#PLAYSTATE_NONE}</li>
+ * <li> {@link PlaybackState#PLAYSTATE_STOPPED}</li>
+ * <li> {@link PlaybackState#PLAYSTATE_PLAYING}</li>
+ * <li> {@link PlaybackState#PLAYSTATE_PAUSED}</li>
+ * <li> {@link PlaybackState#PLAYSTATE_FAST_FORWARDING}</li>
+ * <li> {@link PlaybackState#PLAYSTATE_REWINDING}</li>
+ * <li> {@link PlaybackState#PLAYSTATE_BUFFERING}</li>
+ * <li> {@link PlaybackState#PLAYSTATE_ERROR}</li>
+ */
+ public void setState(int mState) {
+ this.mState = mState;
+ }
+
+ /**
+ * Get the current playback position in ms.
+ */
+ public long getPosition() {
+ return mPosition;
+ }
+
+ /**
+ * Set the current playback position in ms.
+ */
+ public void setPosition(long position) {
+ mPosition = position;
+ }
+
+ /**
+ * Get the current buffer position in ms. This is the farthest playback
+ * point that can be reached from the current position using only buffered
+ * content.
+ */
+ public long getBufferPosition() {
+ return mBufferPosition;
+ }
+
+ /**
+ * Set the current buffer position in ms. This is the farthest playback
+ * point that can be reached from the current position using only buffered
+ * content.
+ */
+ public void setBufferPosition(long bufferPosition) {
+ mBufferPosition = bufferPosition;
+ }
+
+ /**
+ * Get the current playback speed as a multiple of normal playback. This
+ * should be negative when rewinding. A value of 1 means normal playback and
+ * 0 means paused.
+ */
+ public float getSpeed() {
+ return mSpeed;
+ }
+
+ /**
+ * Set the current playback speed as a multiple of normal playback. This
+ * should be negative when rewinding. A value of 1 means normal playback and
+ * 0 means paused.
+ */
+ public void setSpeed(float speed) {
+ mSpeed = speed;
+ }
+
+ /**
+ * Get the current actions available on this session. This should use a
+ * bitmask of the available actions.
+ * <ul>
+ * <li> {@link PlaybackState#ACTION_PREVIOUS_ITEM}</li>
+ * <li> {@link PlaybackState#ACTION_REWIND}</li>
+ * <li> {@link PlaybackState#ACTION_PLAY}</li>
+ * <li> {@link PlaybackState#ACTION_PAUSE}</li>
+ * <li> {@link PlaybackState#ACTION_STOP}</li>
+ * <li> {@link PlaybackState#ACTION_FASTFORWARD}</li>
+ * <li> {@link PlaybackState#ACTION_NEXT_ITEM}</li>
+ * <li> {@link PlaybackState#ACTION_SEEK_TO}</li>
+ * <li> {@link PlaybackState#ACTION_RATING}</li>
+ * </ul>
+ */
+ public long getActions() {
+ return mCapabilities;
+ }
+
+ /**
+ * Set the current capabilities available on this session. This should use a
+ * bitmask of the available capabilities.
+ * <ul>
+ * <li> {@link PlaybackState#ACTION_PREVIOUS_ITEM}</li>
+ * <li> {@link PlaybackState#ACTION_REWIND}</li>
+ * <li> {@link PlaybackState#ACTION_PLAY}</li>
+ * <li> {@link PlaybackState#ACTION_PAUSE}</li>
+ * <li> {@link PlaybackState#ACTION_STOP}</li>
+ * <li> {@link PlaybackState#ACTION_FASTFORWARD}</li>
+ * <li> {@link PlaybackState#ACTION_NEXT_ITEM}</li>
+ * <li> {@link PlaybackState#ACTION_SEEK_TO}</li>
+ * <li> {@link PlaybackState#ACTION_RATING}</li>
+ * </ul>
+ */
+ public void setActions(long capabilities) {
+ mCapabilities = capabilities;
+ }
+
+ /**
+ * Get a user readable error message. This should be set when the state is
+ * {@link PlaybackState#PLAYSTATE_ERROR}.
+ */
+ public String getErrorMessage() {
+ return mErrorMessage;
+ }
+
+ /**
+ * Set a user readable error message. This should be set when the state is
+ * {@link PlaybackState#PLAYSTATE_ERROR}.
+ */
+ public void setErrorMessage(String errorMessage) {
+ mErrorMessage = errorMessage;
+ }
+
+ public static final Parcelable.Creator<PlaybackState> CREATOR
+ = new Parcelable.Creator<PlaybackState>() {
+ @Override
+ public PlaybackState createFromParcel(Parcel in) {
+ return new PlaybackState(in);
+ }
+
+ @Override
+ public PlaybackState[] newArray(int size) {
+ return new PlaybackState[size];
+ }
+ };
+}
diff --git a/media/java/android/media/session/RouteInterface.java b/media/java/android/media/session/RouteInterface.java
new file mode 100644
index 0000000..2391f27
--- /dev/null
+++ b/media/java/android/media/session/RouteInterface.java
@@ -0,0 +1,187 @@
+/*
+ * 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 android.media.session;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Parcelable;
+import android.os.ResultReceiver;
+
+/**
+ * Routes can support multiple interfaces for MediaSessions to interact with. To
+ * add a standard interface you should implement that interface's RouteInterface
+ * Stub and register it with the session. The set of supported commands is
+ * dependent on the specific interface's implementation.
+ * <p>
+ * A MediaInterface can be registered by calling TODO. Once added an interface
+ * will be used by Sessions to decide how they communicate with a session and
+ * cannot be removed, so all interfaces that you plan to support should be added
+ * when the route is created.
+ *
+ * @see RouteTransportControls
+ */
+public final class RouteInterface {
+ private static final String TAG = "MediaInterface";
+
+ private static final String KEY_RESULT = "result";
+
+ private final MediaController mController;
+ private final String mIface;
+
+ /**
+ * @hide
+ */
+ RouteInterface(MediaController controller, String iface) {
+ mController = controller;
+ mIface = iface;
+ }
+
+ public void sendCommand(String command, Bundle params, ResultReceiver cb) {
+ // TODO
+ }
+
+ public void addListener(EventListener listener) {
+ addListener(listener, null);
+ }
+
+ public void addListener(EventListener listener, Handler handler) {
+ // TODO See MediaController for add/remove pattern
+ }
+
+ public void removeListener(EventListener listener) {
+ // TODO
+ }
+
+ // TODO decide on list of supported types
+ private static Bundle writeResultToBundle(Object v) {
+ Bundle b = new Bundle();
+ if (v == null) {
+ // Don't send anything if null
+ } else if (v instanceof String) {
+ b.putString(KEY_RESULT, (String) v);
+ } else if (v instanceof Integer) {
+ b.putInt(KEY_RESULT, (Integer) v);
+ } else if (v instanceof Bundle) {
+ // Must be before Parcelable
+ b.putBundle(KEY_RESULT, (Bundle) v);
+ } else if (v instanceof Parcelable) {
+ b.putParcelable(KEY_RESULT, (Parcelable) v);
+ } else if (v instanceof Short) {
+ b.putShort(KEY_RESULT, (Short) v);
+ } else if (v instanceof Long) {
+ b.putLong(KEY_RESULT, (Long) v);
+ } else if (v instanceof Float) {
+ b.putFloat(KEY_RESULT, (Float) v);
+ } else if (v instanceof Double) {
+ b.putDouble(KEY_RESULT, (Double) v);
+ } else if (v instanceof Boolean) {
+ b.putBoolean(KEY_RESULT, (Boolean) v);
+ } else if (v instanceof CharSequence) {
+ // Must be after String
+ b.putCharSequence(KEY_RESULT, (CharSequence) v);
+ } else if (v instanceof boolean[]) {
+ b.putBooleanArray(KEY_RESULT, (boolean[]) v);
+ } else if (v instanceof byte[]) {
+ b.putByteArray(KEY_RESULT, (byte[]) v);
+ } else if (v instanceof String[]) {
+ b.putStringArray(KEY_RESULT, (String[]) v);
+ } else if (v instanceof CharSequence[]) {
+ // Must be after String[] and before Object[]
+ b.putCharSequenceArray(KEY_RESULT, (CharSequence[]) v);
+ } else if (v instanceof IBinder) {
+ b.putBinder(KEY_RESULT, (IBinder) v);
+ } else if (v instanceof Parcelable[]) {
+ b.putParcelableArray(KEY_RESULT, (Parcelable[]) v);
+ } else if (v instanceof int[]) {
+ b.putIntArray(KEY_RESULT, (int[]) v);
+ } else if (v instanceof long[]) {
+ b.putLongArray(KEY_RESULT, (long[]) v);
+ } else if (v instanceof Byte) {
+ b.putByte(KEY_RESULT, (Byte) v);
+ }
+ return b;
+ }
+
+ public abstract static class Stub {
+
+ /**
+ * The name of an interface should be a fully qualified name to prevent
+ * namespace collisions. Example: "com.myproject.MyPlaybackInterface"
+ *
+ * @return The name of this interface
+ */
+ public abstract String getName();
+
+ /**
+ * This is called when a command is received that matches the interface
+ * you registered. Commands can come from any app with a MediaController
+ * reference to the session.
+ *
+ * @see MediaController
+ * @see MediaSession
+ * @param command The command or method to invoke.
+ * @param args Any args that were included with the command. May be
+ * null.
+ * @param cb The callback provided to send a response on. May be null.
+ */
+ public abstract void onCommand(String command, Bundle args, ResultReceiver cb);
+
+ public final void sendEvent(MediaSession session, String event, Bundle extras) {
+ // TODO
+ }
+ }
+
+ /**
+ * An EventListener can be registered by an app with TODO to handle events
+ * sent by the session on a specific interface.
+ */
+ public static abstract class EventListener {
+ /**
+ * This is called when an event is received from the interface. Events
+ * are sent by the session owner and will be delivered to all
+ * controllers that are listening to the interface.
+ *
+ * @param event The event that occurred.
+ * @param args Any extras that were included with the event. May be
+ * null.
+ */
+ public abstract void onEvent(String event, Bundle args);
+ }
+
+ private static final class EventHandler extends Handler {
+
+ private final RouteInterface.EventListener mListener;
+
+ public EventHandler(Looper looper, RouteInterface.EventListener cb) {
+ super(looper, null, true);
+ mListener = cb;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ mListener.onEvent((String) msg.obj, msg.getData());
+ }
+
+ public void postEvent(String event, Bundle args) {
+ Message msg = obtainMessage(0, event);
+ msg.setData(args);
+ msg.sendToTarget();
+ }
+ }
+}
diff --git a/media/java/android/media/session/RouteTransportControls.java b/media/java/android/media/session/RouteTransportControls.java
new file mode 100644
index 0000000..665fd10
--- /dev/null
+++ b/media/java/android/media/session/RouteTransportControls.java
@@ -0,0 +1,230 @@
+/*
+ * 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 android.media.session;
+
+import android.media.RemoteControlClient;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.ResultReceiver;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * A standard media control interface for Routes. Routes can support multiple
+ * interfaces for MediaSessions to interact with. TODO rewrite for routes
+ */
+public final class RouteTransportControls {
+ private static final String TAG = "RouteTransportControls";
+ public static final String NAME = "android.media.session.RouteTransportControls";
+
+ private static final String KEY_VALUE1 = "value1";
+
+ private static final String METHOD_FAST_FORWARD = "fastForward";
+ private static final String METHOD_GET_CURRENT_POSITION = "getCurrentPosition";
+ private static final String METHOD_GET_CAPABILITIES = "getCapabilities";
+
+ private static final String EVENT_PLAYSTATE_CHANGE = "playstateChange";
+ private static final String EVENT_METADATA_CHANGE = "metadataChange";
+
+ private final MediaController mController;
+ private final RouteInterface mIface;
+
+ private RouteTransportControls(RouteInterface iface, MediaController controller) {
+ mIface = iface;
+ mController = controller;
+ }
+
+ public static RouteTransportControls from(MediaController controller) {
+// MediaInterface iface = controller.getInterface(NAME);
+// if (iface != null) {
+// return new RouteTransportControls(iface, controller);
+// }
+ return null;
+ }
+
+ /**
+ * Send a play command to the route. TODO rename resume() and use messaging
+ * protocol, not KeyEvent
+ */
+ public void play() {
+ // TODO
+ }
+
+ /**
+ * Send a pause command to the session.
+ */
+ public void pause() {
+ // TODO
+ }
+
+ /**
+ * Set the rate at which to fastforward. Valid values are in the range [0,1]
+ * with actual rates depending on the implementation.
+ *
+ * @param rate
+ */
+ public void fastForward(float rate) {
+ if (rate < 0 || rate > 1) {
+ throw new IllegalArgumentException("Rate must be between 0 and 1 inclusive");
+ }
+ Bundle b = new Bundle();
+ b.putFloat(KEY_VALUE1, rate);
+ mIface.sendCommand(METHOD_FAST_FORWARD, b, null);
+ }
+
+ public void getCurrentPosition(ResultReceiver cb) {
+ mIface.sendCommand(METHOD_GET_CURRENT_POSITION, null, cb);
+ }
+
+ public void getCapabilities(ResultReceiver cb) {
+ mIface.sendCommand(METHOD_GET_CAPABILITIES, null, cb);
+ }
+
+ public void addListener(Listener listener) {
+ mIface.addListener(listener.mListener);
+ }
+
+ public void addListener(Listener listener, Handler handler) {
+ mIface.addListener(listener.mListener, handler);
+ }
+
+ public void removeListener(Listener listener) {
+ mIface.removeListener(listener.mListener);
+ }
+
+ public static abstract class Stub extends RouteInterface.Stub {
+ private final MediaSession mSession;
+
+ public Stub(MediaSession session) {
+ mSession = session;
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public void onCommand(String method, Bundle extras, ResultReceiver cb) {
+ if (TextUtils.isEmpty(method)) {
+ return;
+ }
+ Bundle result;
+ if (METHOD_FAST_FORWARD.equals(method)) {
+ fastForward(extras.getFloat(KEY_VALUE1, -1));
+ } else if (METHOD_GET_CURRENT_POSITION.equals(method)) {
+ if (cb != null) {
+ result = new Bundle();
+ result.putLong(KEY_VALUE1, getCurrentPosition());
+ cb.send(0, result);
+ }
+ } else if (METHOD_GET_CAPABILITIES.equals(method)) {
+ if (cb != null) {
+ result = new Bundle();
+ result.putLong(KEY_VALUE1, getCapabilities());
+ cb.send(0, result);
+ }
+ }
+ }
+
+ /**
+ * Override to handle fast forwarding. Valid values are [0,1] inclusive.
+ * The interpretation of the rate is up to the implementation. If no
+ * rate was included with the command a rate of -1 will be used by
+ * default.
+ *
+ * @param rate The rate at which to fast forward as a multiplier
+ */
+ public void fastForward(float rate) {
+ Log.w(TAG, "fastForward is not supported.");
+ }
+
+ /**
+ * Override to handle getting the current position of playback in
+ * millis.
+ *
+ * @return The current position in millis or -1
+ */
+ public long getCurrentPosition() {
+ Log.w(TAG, "getCurrentPosition is not supported");
+ return -1;
+ }
+
+ /**
+ * Override to handle getting the set of capabilities currently
+ * available.
+ *
+ * @return A bit mask of the supported capabilities
+ */
+ public long getCapabilities() {
+ Log.w(TAG, "getCapabilities is not supported");
+ return 0;
+ }
+
+ /**
+ * Publish the current playback state to the system and any controllers.
+ * Valid values are defined in {@link RemoteControlClient}. TODO move
+ * play states somewhere else.
+ *
+ * @param state
+ */
+ public final void updatePlaybackState(int state) {
+ Bundle extras = new Bundle();
+ extras.putInt(KEY_VALUE1, state);
+ sendEvent(mSession, EVENT_PLAYSTATE_CHANGE, extras);
+ }
+ }
+
+ /**
+ * Register this event listener using TODO to receive
+ * TransportControlInterface events from a session.
+ *
+ * @see RouteInterface.EventListener
+ */
+ public static abstract class Listener {
+
+ private RouteInterface.EventListener mListener = new RouteInterface.EventListener() {
+ @Override
+ public final void onEvent(String event, Bundle args) {
+ if (EVENT_PLAYSTATE_CHANGE.equals(event)) {
+ onPlaybackStateChange(args.getInt(KEY_VALUE1));
+ } else if (EVENT_METADATA_CHANGE.equals(event)) {
+ onMetadataUpdate(args);
+ }
+ }
+ };
+
+ /**
+ * Override to handle updates to the playback state. Valid values are in
+ * {@link TransportPerformer}. TODO put playstate values somewhere more
+ * generic.
+ *
+ * @param state
+ */
+ public void onPlaybackStateChange(int state) {
+ }
+
+ /**
+ * Override to handle metadata changes for this session's media. The
+ * default supported fields are those in {@link MediaMetadata}.
+ *
+ * @param metadata
+ */
+ public void onMetadataUpdate(Bundle metadata) {
+ }
+ }
+
+}
diff --git a/media/java/android/media/session/TransportController.java b/media/java/android/media/session/TransportController.java
new file mode 100644
index 0000000..15b11f3
--- /dev/null
+++ b/media/java/android/media/session/TransportController.java
@@ -0,0 +1,342 @@
+/*
+ * 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 android.media.session;
+
+import android.media.Rating;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+/**
+ * Interface for controlling media playback on a session. This allows an app to
+ * request changes in playback, retrieve the current playback state and
+ * metadata, and listen for changes to the playback state and metadata.
+ */
+public final class TransportController {
+ private static final String TAG = "TransportController";
+
+ private final Object mLock = new Object();
+ private final ArrayList<MessageHandler> mListeners = new ArrayList<MessageHandler>();
+ private final IMediaController mBinder;
+
+ /**
+ * @hide
+ */
+ public TransportController(IMediaController binder) {
+ mBinder = binder;
+ }
+
+ /**
+ * Start listening to changes in playback state.
+ */
+ public void addStateListener(TransportStateListener listener) {
+ addStateListener(listener, null);
+ }
+
+ public void addStateListener(TransportStateListener listener, Handler handler) {
+ if (listener == null) {
+ throw new IllegalArgumentException("Listener cannot be null");
+ }
+ synchronized (mLock) {
+ if (getHandlerForListenerLocked(listener) != null) {
+ Log.w(TAG, "Listener is already added, ignoring");
+ return;
+ }
+ if (handler == null) {
+ handler = new Handler();
+ }
+
+ MessageHandler msgHandler = new MessageHandler(handler.getLooper(), listener);
+ mListeners.add(msgHandler);
+ }
+ }
+
+ /**
+ * Stop listening to changes in playback state.
+ */
+ public void removeStateListener(TransportStateListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("Listener cannot be null");
+ }
+ synchronized (mLock) {
+ removeStateListenerLocked(listener);
+ }
+ }
+
+ /**
+ * Request that the player start its playback at its current position.
+ */
+ public void play() {
+ try {
+ mBinder.play();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling play.", e);
+ }
+ }
+
+ /**
+ * Request that the player pause its playback and stay at its current
+ * position.
+ */
+ public void pause() {
+ try {
+ mBinder.pause();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling pause.", e);
+ }
+ }
+
+ /**
+ * Request that the player stop its playback; it may clear its state in
+ * whatever way is appropriate.
+ */
+ public void stop() {
+ try {
+ mBinder.stop();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling stop.", e);
+ }
+ }
+
+ /**
+ * Move to a new location in the media stream.
+ *
+ * @param pos Position to move to, in milliseconds.
+ */
+ public void seekTo(long pos) {
+ try {
+ mBinder.seekTo(pos);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling seekTo.", e);
+ }
+ }
+
+ /**
+ * Start fast forwarding. If playback is already fast forwarding this may
+ * increase the rate.
+ */
+ public void fastForward() {
+ try {
+ mBinder.fastForward();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling fastForward.", e);
+ }
+ }
+
+ /**
+ * Skip to the next item.
+ */
+ public void next() {
+ try {
+ mBinder.next();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling next.", e);
+ }
+ }
+
+ /**
+ * Start rewinding. If playback is already rewinding this may increase the
+ * rate.
+ */
+ public void rewind() {
+ try {
+ mBinder.rewind();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling rewind.", e);
+ }
+ }
+
+ /**
+ * Skip to the previous item.
+ */
+ public void previous() {
+ try {
+ mBinder.previous();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling previous.", e);
+ }
+ }
+
+ /**
+ * Rate the current content. This will cause the rating to be set for the
+ * current user. The Rating type must match the type returned by
+ * {@link #getRatingType()}.
+ *
+ * @param rating The rating to set for the current content
+ */
+ public void rate(Rating rating) {
+ try {
+ mBinder.rate(rating);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling rate.", e);
+ }
+ }
+
+ /**
+ * Get the rating type supported by the session. One of:
+ * <ul>
+ * <li>{@link Rating#RATING_NONE}</li>
+ * <li>{@link Rating#RATING_HEART}</li>
+ * <li>{@link Rating#RATING_THUMB_UP_DOWN}</li>
+ * <li>{@link Rating#RATING_3_STARS}</li>
+ * <li>{@link Rating#RATING_4_STARS}</li>
+ * <li>{@link Rating#RATING_5_STARS}</li>
+ * <li>{@link Rating#RATING_PERCENTAGE}</li>
+ * </ul>
+ *
+ * @return The supported rating type
+ */
+ public int getRatingType() {
+ try {
+ return mBinder.getRatingType();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling getRatingType.", e);
+ return Rating.RATING_NONE;
+ }
+ }
+
+ /**
+ * Get the current playback state for this session.
+ *
+ * @return The current PlaybackState or null
+ */
+ public PlaybackState getPlaybackState() {
+ try {
+ return mBinder.getPlaybackState();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling getPlaybackState.", e);
+ return null;
+ }
+ }
+
+ /**
+ * Get the current metadata for this session.
+ *
+ * @return The current MediaMetadata or null.
+ */
+ public MediaMetadata getMetadata() {
+ try {
+ return mBinder.getMetadata();
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Error calling getMetadata.", e);
+ return null;
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public final void postPlaybackStateChanged(PlaybackState state) {
+ synchronized (mLock) {
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ mListeners.get(i).post(MessageHandler.MSG_UPDATE_PLAYBACK_STATE, state);
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public final void postMetadataChanged(MediaMetadata metadata) {
+ synchronized (mLock) {
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ mListeners.get(i).post(MessageHandler.MSG_UPDATE_METADATA,
+ metadata);
+ }
+ }
+ }
+
+ private MessageHandler getHandlerForListenerLocked(TransportStateListener listener) {
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ MessageHandler handler = mListeners.get(i);
+ if (listener == handler.mListener) {
+ return handler;
+ }
+ }
+ return null;
+ }
+
+ private boolean removeStateListenerLocked(TransportStateListener listener) {
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ if (listener == mListeners.get(i).mListener) {
+ mListeners.remove(i);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Register using {@link #addStateListener} to receive updates when there
+ * are playback changes on the session.
+ */
+ public static abstract class TransportStateListener {
+ private MessageHandler mHandler;
+ /**
+ * Override to handle changes in playback state.
+ *
+ * @param state The new playback state of the session
+ */
+ public void onPlaybackStateChanged(PlaybackState state) {
+ }
+
+ /**
+ * Override to handle changes to the current metadata.
+ *
+ * @see MediaMetadata
+ * @param metadata The current metadata for the session or null
+ */
+ public void onMetadataChanged(MediaMetadata metadata) {
+ }
+
+ private void setHandler(Handler handler) {
+ mHandler = new MessageHandler(handler.getLooper(), this);
+ }
+ }
+
+ private static class MessageHandler extends Handler {
+ private static final int MSG_UPDATE_PLAYBACK_STATE = 1;
+ private static final int MSG_UPDATE_METADATA = 2;
+
+ private TransportStateListener mListener;
+
+ public MessageHandler(Looper looper, TransportStateListener cb) {
+ super(looper, null, true);
+ mListener = cb;
+ }
+
+ public void post(int msg, Object obj) {
+ obtainMessage(msg, obj).sendToTarget();
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_PLAYBACK_STATE:
+ mListener.onPlaybackStateChanged((PlaybackState) msg.obj);
+ break;
+ case MSG_UPDATE_METADATA:
+ mListener.onMetadataChanged((MediaMetadata) msg.obj);
+ break;
+ }
+ }
+ }
+
+}
diff --git a/media/java/android/media/session/TransportPerformer.java b/media/java/android/media/session/TransportPerformer.java
new file mode 100644
index 0000000..b96db20
--- /dev/null
+++ b/media/java/android/media/session/TransportPerformer.java
@@ -0,0 +1,357 @@
+/*
+ * 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 android.media.session;
+
+import android.media.AudioManager;
+import android.media.Rating;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+/**
+ * Allows broadcasting of playback changes.
+ */
+public final class TransportPerformer {
+ private static final String TAG = "TransportPerformer";
+ private final Object mLock = new Object();
+ private final ArrayList<MessageHandler> mListeners = new ArrayList<MessageHandler>();
+
+ private IMediaSession mBinder;
+
+ /**
+ * @hide
+ */
+ public TransportPerformer(IMediaSession binder) {
+ mBinder = binder;
+ }
+
+ /**
+ * Add a listener to receive updates on.
+ *
+ * @param listener The callback object
+ */
+ public void addListener(Listener listener) {
+ addListener(listener, null);
+ }
+
+ /**
+ * Add a listener to receive updates on. The updates will be posted to the
+ * specified handler. If no handler is provided they will be posted to the
+ * caller's thread.
+ *
+ * @param listener The listener to receive updates on
+ * @param handler The handler to post the updates on
+ */
+ public void addListener(Listener listener, Handler handler) {
+ if (listener == null) {
+ throw new IllegalArgumentException("Listener cannot be null");
+ }
+ synchronized (mLock) {
+ if (getHandlerForListenerLocked(listener) != null) {
+ Log.w(TAG, "Listener is already added, ignoring");
+ }
+ if (handler == null) {
+ handler = new Handler();
+ }
+ MessageHandler msgHandler = new MessageHandler(handler.getLooper(), listener);
+ mListeners.add(msgHandler);
+ }
+ }
+
+ /**
+ * Stop receiving updates on the specified handler. If an update has already
+ * been posted you may still receive it after this call returns.
+ *
+ * @param listener The listener to stop receiving updates on
+ */
+ public void removeListener(Listener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("Listener cannot be null");
+ }
+ synchronized (mLock) {
+ removeListenerLocked(listener);
+ }
+ }
+
+ /**
+ * Update the current playback state.
+ *
+ * @param state The current state of playback
+ */
+ public final void setPlaybackState(PlaybackState state) {
+ try {
+ mBinder.setPlaybackState(state);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Dead object in setPlaybackState.", e);
+ }
+ }
+
+ /**
+ * Update the current metadata. New metadata can be created using
+ * {@link MediaMetadata.Builder}.
+ *
+ * @param metadata The new metadata
+ */
+ public final void setMetadata(MediaMetadata metadata) {
+ try {
+ mBinder.setMetadata(metadata);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Dead object in setPlaybackState.", e);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public final void onPlay() {
+ post(MessageHandler.MESSAGE_PLAY);
+ }
+
+ /**
+ * @hide
+ */
+ public final void onPause() {
+ post(MessageHandler.MESSAGE_PAUSE);
+ }
+
+ /**
+ * @hide
+ */
+ public final void onStop() {
+ post(MessageHandler.MESSAGE_STOP);
+ }
+
+ /**
+ * @hide
+ */
+ public final void onNext() {
+ post(MessageHandler.MESSAGE_NEXT);
+ }
+
+ /**
+ * @hide
+ */
+ public final void onPrevious() {
+ post(MessageHandler.MESSAGE_PREVIOUS);
+ }
+
+ /**
+ * @hide
+ */
+ public final void onFastForward() {
+ post(MessageHandler.MESSAGE_FAST_FORWARD);
+ }
+
+ /**
+ * @hide
+ */
+ public final void onRewind() {
+ post(MessageHandler.MESSAGE_REWIND);
+ }
+
+ /**
+ * @hide
+ */
+ public final void onSeekTo(long pos) {
+ post(MessageHandler.MESSAGE_SEEK_TO, pos);
+ }
+
+ /**
+ * @hide
+ */
+ public final void onRate(Rating rating) {
+ post(MessageHandler.MESSAGE_RATE, rating);
+ }
+
+ private MessageHandler getHandlerForListenerLocked(Listener listener) {
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ MessageHandler handler = mListeners.get(i);
+ if (listener == handler.mListener) {
+ return handler;
+ }
+ }
+ return null;
+ }
+
+ private boolean removeListenerLocked(Listener listener) {
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ if (listener == mListeners.get(i).mListener) {
+ mListeners.remove(i);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void post(int what, Object obj) {
+ synchronized (mLock) {
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ mListeners.get(i).post(what, obj);
+ }
+ }
+ }
+
+ private void post(int what) {
+ post(what, null);
+ }
+
+ /**
+ * Extend Listener to handle transport controls. Listeners can be registered
+ * using {@link #addListener}.
+ */
+ public static abstract class Listener {
+
+ /**
+ * Override to handle requests to begin playback.
+ */
+ public void onPlay() {
+ }
+
+ /**
+ * Override to handle requests to pause playback.
+ */
+ public void onPause() {
+ }
+
+ /**
+ * Override to handle requests to skip to the next media item.
+ */
+ public void onNext() {
+ }
+
+ /**
+ * Override to handle requests to skip to the previous media item.
+ */
+ public void onPrevious() {
+ }
+
+ /**
+ * Override to handle requests to fast forward.
+ */
+ public void onFastForward() {
+ }
+
+ /**
+ * Override to handle requests to rewind.
+ */
+ public void onRewind() {
+ }
+
+ /**
+ * Override to handle requests to stop playback.
+ */
+ public void onStop() {
+ }
+
+ /**
+ * Override to handle requests to seek to a specific position in ms.
+ *
+ * @param pos New position to move to, in milliseconds.
+ */
+ public void onSeekTo(long pos) {
+ }
+
+ /**
+ * Override to handle the item being rated.
+ *
+ * @param rating
+ */
+ public void onRate(Rating rating) {
+ }
+
+ /**
+ * Report that audio focus has changed on the app. This only happens if
+ * you have indicated you have started playing with
+ * {@link #setPlaybackState}. TODO figure out route focus apis/handling.
+ *
+ * @param focusChange The type of focus change, TBD. The default
+ * implementation will deliver a call to {@link #onPause}
+ * when focus is lost.
+ */
+ public void onRouteFocusChange(int focusChange) {
+ switch (focusChange) {
+ case AudioManager.AUDIOFOCUS_LOSS:
+ onPause();
+ break;
+ }
+ }
+ }
+
+ private class MessageHandler extends Handler {
+ private static final int MESSAGE_PLAY = 1;
+ private static final int MESSAGE_PAUSE = 2;
+ private static final int MESSAGE_STOP = 3;
+ private static final int MESSAGE_NEXT = 4;
+ private static final int MESSAGE_PREVIOUS = 5;
+ private static final int MESSAGE_FAST_FORWARD = 6;
+ private static final int MESSAGE_REWIND = 7;
+ private static final int MESSAGE_SEEK_TO = 8;
+ private static final int MESSAGE_RATE = 9;
+
+ private Listener mListener;
+
+ public MessageHandler(Looper looper, Listener cb) {
+ super(looper);
+ mListener = cb;
+ }
+
+ public void post(int what, Object obj) {
+ obtainMessage(what, obj).sendToTarget();
+ }
+
+ public void post(int what) {
+ post(what, null);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_PLAY:
+ mListener.onPlay();
+ break;
+ case MESSAGE_PAUSE:
+ mListener.onPause();
+ break;
+ case MESSAGE_STOP:
+ mListener.onStop();
+ break;
+ case MESSAGE_NEXT:
+ mListener.onNext();
+ break;
+ case MESSAGE_PREVIOUS:
+ mListener.onPrevious();
+ break;
+ case MESSAGE_FAST_FORWARD:
+ mListener.onFastForward();
+ break;
+ case MESSAGE_REWIND:
+ mListener.onRewind();
+ break;
+ case MESSAGE_SEEK_TO:
+ mListener.onSeekTo((Long) msg.obj);
+ break;
+ case MESSAGE_RATE:
+ mListener.onRate((Rating) msg.obj);
+ break;
+ }
+ }
+ }
+}
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index cc464db..2ddbb7d 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -16,15 +16,19 @@
package android.mtp;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.ContentValues;
import android.content.IContentProvider;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.media.MediaScanner;
import android.net.Uri;
+import android.os.BatteryManager;
+import android.os.BatteryStats;
import android.os.RemoteException;
import android.provider.MediaStore;
import android.provider.MediaStore.Audio;
@@ -113,11 +117,35 @@
+ Files.FileColumns.PARENT + "=?";
private final MediaScanner mMediaScanner;
+ private MtpServer mServer;
+
+ // read from native code
+ private int mBatteryLevel;
+ private int mBatteryScale;
static {
System.loadLibrary("media_jni");
}
+ private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
+ mBatteryScale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
+ int newLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
+ if (newLevel != mBatteryLevel) {
+ mBatteryLevel = newLevel;
+ if (mServer != null) {
+ // send device property changed event
+ mServer.sendDevicePropertyChanged(
+ MtpConstants.DEVICE_PROPERTY_BATTERY_LEVEL);
+ }
+ }
+ }
+ }
+ };
+
public MtpDatabase(Context context, String volumeName, String storagePath,
String[] subDirectories) {
native_setup();
@@ -171,6 +199,18 @@
initDeviceProperties(context);
}
+ public void setServer(MtpServer server) {
+ mServer = server;
+
+ // register for battery notifications when we are connected
+ if (server != null) {
+ mContext.registerReceiver(mBatteryReceiver,
+ new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+ } else {
+ mContext.unregisterReceiver(mBatteryReceiver);
+ }
+ }
+
@Override
protected void finalize() throws Throwable {
try {
@@ -663,6 +703,7 @@
MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER,
MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME,
MtpConstants.DEVICE_PROPERTY_IMAGE_SIZE,
+ MtpConstants.DEVICE_PROPERTY_BATTERY_LEVEL,
};
}
@@ -819,6 +860,8 @@
outStringValue[imageSize.length()] = 0;
return MtpConstants.RESPONSE_OK;
+ // DEVICE_PROPERTY_BATTERY_LEVEL is implemented in the JNI code
+
default:
return MtpConstants.RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
}
diff --git a/media/java/android/mtp/MtpServer.java b/media/java/android/mtp/MtpServer.java
index 266f78e..3814630 100644
--- a/media/java/android/mtp/MtpServer.java
+++ b/media/java/android/mtp/MtpServer.java
@@ -30,6 +30,7 @@
public MtpServer(MtpDatabase database, boolean usePtp) {
native_setup(database, usePtp);
+ database.setServer(this);
}
public void start() {
@@ -51,6 +52,10 @@
native_send_object_removed(handle);
}
+ public void sendDevicePropertyChanged(int property) {
+ native_send_device_property_changed(property);
+ }
+
public void addStorage(MtpStorage storage) {
native_add_storage(storage);
}
@@ -64,6 +69,7 @@
private native final void native_cleanup();
private native final void native_send_object_added(int handle);
private native final void native_send_object_removed(int handle);
+ private native final void native_send_device_property_changed(int property);
private native final void native_add_storage(MtpStorage storage);
private native final void native_remove_storage(int storageId);
}
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 6b0bd0d..ea75a18 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -68,6 +68,8 @@
static jmethodID method_sessionEnded;
static jfieldID field_context;
+static jfieldID field_batteryLevel;
+static jfieldID field_batteryScale;
// MtpPropertyList fields
static jfieldID field_mCount;
@@ -527,68 +529,75 @@
MtpResponseCode MyMtpDatabase::getDevicePropertyValue(MtpDeviceProperty property,
MtpDataPacket& packet) {
- int type;
-
- if (!getDevicePropertyInfo(property, type))
- return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
-
JNIEnv* env = AndroidRuntime::getJNIEnv();
- jint result = env->CallIntMethod(mDatabase, method_getDeviceProperty,
- (jint)property, mLongBuffer, mStringBuffer);
- if (result != MTP_RESPONSE_OK) {
+
+ if (property == MTP_DEVICE_PROPERTY_BATTERY_LEVEL) {
+ // special case - implemented here instead of Java
+ packet.putUInt8((uint8_t)env->GetIntField(mDatabase, field_batteryLevel));
+ return MTP_RESPONSE_OK;
+ } else {
+ int type;
+
+ if (!getDevicePropertyInfo(property, type))
+ return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
+
+ jint result = env->CallIntMethod(mDatabase, method_getDeviceProperty,
+ (jint)property, mLongBuffer, mStringBuffer);
+ if (result != MTP_RESPONSE_OK) {
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ return result;
+ }
+
+ jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
+ jlong longValue = longValues[0];
+ env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
+
+ switch (type) {
+ case MTP_TYPE_INT8:
+ packet.putInt8(longValue);
+ break;
+ case MTP_TYPE_UINT8:
+ packet.putUInt8(longValue);
+ break;
+ case MTP_TYPE_INT16:
+ packet.putInt16(longValue);
+ break;
+ case MTP_TYPE_UINT16:
+ packet.putUInt16(longValue);
+ break;
+ case MTP_TYPE_INT32:
+ packet.putInt32(longValue);
+ break;
+ case MTP_TYPE_UINT32:
+ packet.putUInt32(longValue);
+ break;
+ case MTP_TYPE_INT64:
+ packet.putInt64(longValue);
+ break;
+ case MTP_TYPE_UINT64:
+ packet.putUInt64(longValue);
+ break;
+ case MTP_TYPE_INT128:
+ packet.putInt128(longValue);
+ break;
+ case MTP_TYPE_UINT128:
+ packet.putInt128(longValue);
+ break;
+ case MTP_TYPE_STR:
+ {
+ jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
+ packet.putString(str);
+ env->ReleaseCharArrayElements(mStringBuffer, str, 0);
+ break;
+ }
+ default:
+ ALOGE("unsupported type in getDevicePropertyValue\n");
+ return MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT;
+ }
+
checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return result;
+ return MTP_RESPONSE_OK;
}
-
- jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
- jlong longValue = longValues[0];
- env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
-
- switch (type) {
- case MTP_TYPE_INT8:
- packet.putInt8(longValue);
- break;
- case MTP_TYPE_UINT8:
- packet.putUInt8(longValue);
- break;
- case MTP_TYPE_INT16:
- packet.putInt16(longValue);
- break;
- case MTP_TYPE_UINT16:
- packet.putUInt16(longValue);
- break;
- case MTP_TYPE_INT32:
- packet.putInt32(longValue);
- break;
- case MTP_TYPE_UINT32:
- packet.putUInt32(longValue);
- break;
- case MTP_TYPE_INT64:
- packet.putInt64(longValue);
- break;
- case MTP_TYPE_UINT64:
- packet.putUInt64(longValue);
- break;
- case MTP_TYPE_INT128:
- packet.putInt128(longValue);
- break;
- case MTP_TYPE_UINT128:
- packet.putInt128(longValue);
- break;
- case MTP_TYPE_STR:
- {
- jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
- packet.putString(str);
- env->ReleaseCharArrayElements(mStringBuffer, str, 0);
- break;
- }
- default:
- ALOGE("unsupported type in getDevicePropertyValue\n");
- return MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT;
- }
-
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- return MTP_RESPONSE_OK;
}
MtpResponseCode MyMtpDatabase::setDevicePropertyValue(MtpDeviceProperty property,
@@ -923,6 +932,7 @@
{ MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER, MTP_TYPE_STR },
{ MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME, MTP_TYPE_STR },
{ MTP_DEVICE_PROPERTY_IMAGE_SIZE, MTP_TYPE_STR },
+ { MTP_DEVICE_PROPERTY_BATTERY_LEVEL, MTP_TYPE_UINT8 },
};
bool MyMtpDatabase::getObjectPropertyInfo(MtpObjectProperty property, int& type) {
@@ -1046,7 +1056,7 @@
case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
writable = true;
// fall through
- case MTP_DEVICE_PROPERTY_IMAGE_SIZE:
+ case MTP_DEVICE_PROPERTY_IMAGE_SIZE: {
result = new MtpProperty(property, MTP_TYPE_STR, writable);
// get current value
@@ -1063,6 +1073,12 @@
ALOGE("unable to read device property, response: %04X", ret);
}
break;
+ }
+ case MTP_DEVICE_PROPERTY_BATTERY_LEVEL:
+ result = new MtpProperty(property, MTP_TYPE_UINT8);
+ result->setFormRange(0, env->GetIntField(mDatabase, field_batteryScale), 1);
+ result->mCurrentValue.u.u8 = (uint8_t)env->GetIntField(mDatabase, field_batteryLevel);
+ break;
}
checkAndClearExceptionFromCallback(env, __FUNCTION__);
@@ -1234,6 +1250,16 @@
ALOGE("Can't find MtpDatabase.mNativeContext");
return -1;
}
+ field_batteryLevel = env->GetFieldID(clazz, "mBatteryLevel", "I");
+ if (field_batteryLevel == NULL) {
+ ALOGE("Can't find MtpDatabase.mBatteryLevel");
+ return -1;
+ }
+ field_batteryScale = env->GetFieldID(clazz, "mBatteryScale", "I");
+ if (field_batteryScale == NULL) {
+ ALOGE("Can't find MtpDatabase.mBatteryScale");
+ return -1;
+ }
// now set up fields for MtpPropertyList class
clazz = env->FindClass("android/mtp/MtpPropertyList");
diff --git a/media/jni/android_mtp_MtpServer.cpp b/media/jni/android_mtp_MtpServer.cpp
index 9d7f1c2..2f90dfe 100644
--- a/media/jni/android_mtp_MtpServer.cpp
+++ b/media/jni/android_mtp_MtpServer.cpp
@@ -118,6 +118,18 @@
}
static void
+android_mtp_MtpServer_send_device_property_changed(JNIEnv *env, jobject thiz, jint property)
+{
+ Mutex::Autolock autoLock(sMutex);
+
+ MtpServer* server = getMtpServer(env, thiz);
+ if (server)
+ server->sendDevicePropertyChanged(property);
+ else
+ ALOGE("server is null in send_object_removed");
+}
+
+static void
android_mtp_MtpServer_add_storage(JNIEnv *env, jobject thiz, jobject jstorage)
{
Mutex::Autolock autoLock(sMutex);
@@ -174,6 +186,8 @@
{"native_cleanup", "()V", (void *)android_mtp_MtpServer_cleanup},
{"native_send_object_added", "(I)V", (void *)android_mtp_MtpServer_send_object_added},
{"native_send_object_removed", "(I)V", (void *)android_mtp_MtpServer_send_object_removed},
+ {"native_send_device_property_changed", "(I)V",
+ (void *)android_mtp_MtpServer_send_device_property_changed},
{"native_add_storage", "(Landroid/mtp/MtpStorage;)V",
(void *)android_mtp_MtpServer_add_storage},
{"native_remove_storage", "(I)V", (void *)android_mtp_MtpServer_remove_storage},
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index eb07d88..a89921f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -931,6 +931,7 @@
}
if (contentViewLocal != null) {
+ contentViewLocal.setIsRootNamespace(true);
SizeAdaptiveLayout.LayoutParams params =
new SizeAdaptiveLayout.LayoutParams(contentViewLocal.getLayoutParams());
params.minHeight = minHeight;
@@ -938,6 +939,7 @@
expanded.addView(contentViewLocal, params);
}
if (bigContentViewLocal != null) {
+ bigContentViewLocal.setIsRootNamespace(true);
SizeAdaptiveLayout.LayoutParams params =
new SizeAdaptiveLayout.LayoutParams(bigContentViewLocal.getLayoutParams());
params.minHeight = minHeight+1;
@@ -955,6 +957,7 @@
expandedPublic, mOnClickHandler);
if (publicViewLocal != null) {
+ publicViewLocal.setIsRootNamespace(true);
SizeAdaptiveLayout.LayoutParams params =
new SizeAdaptiveLayout.LayoutParams(publicViewLocal.getLayoutParams());
params.minHeight = minHeight;
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 89acec9..1ff925c 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -21,14 +21,22 @@
import android.media.session.IMediaControllerCallback;
import android.media.session.IMediaSession;
import android.media.session.IMediaSessionCallback;
-import android.media.RemoteControlClient;
+import android.media.session.MediaMetadata;
+import android.media.session.PlaybackState;
+import android.media.Rating;
import android.os.Bundle;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.util.Log;
+import android.util.Slog;
import android.view.KeyEvent;
import java.util.ArrayList;
+import java.util.List;
/**
* This is the system implementation of a Session. Apps will interact with the
@@ -37,6 +45,8 @@
public class MediaSessionRecord implements IBinder.DeathRecipient {
private static final String TAG = "MediaSessionImpl";
+ private final MessageHandler mHandler;
+
private final int mPid;
private final String mPackageName;
private final String mTag;
@@ -45,13 +55,25 @@
private final SessionCb mSessionCb;
private final MediaSessionService mService;
- private final ArrayList<IMediaControllerCallback> mSessionCallbacks =
+ private final Object mControllerLock = new Object();
+ private final ArrayList<IMediaControllerCallback> mControllerCallbacks =
new ArrayList<IMediaControllerCallback>();
+ private final ArrayList<String> mInterfaces = new ArrayList<String>();
- private int mPlaybackState = RemoteControlClient.PLAYSTATE_NONE;
+ private boolean mTransportPerformerEnabled = false;
+ private Bundle mRoute;
+
+ // TransportPerformer fields
+
+ private MediaMetadata mMetadata;
+ private PlaybackState mPlaybackState;
+ private int mRatingType;
+ // End TransportPerformer fields
+
+ private boolean mIsPublished = false;
public MediaSessionRecord(int pid, String packageName, IMediaSessionCallback cb, String tag,
- MediaSessionService service) {
+ MediaSessionService service, Handler handler) {
mPid = pid;
mPackageName = packageName;
mTag = tag;
@@ -59,6 +81,7 @@
mSession = new SessionStub();
mSessionCb = new SessionCb(cb);
mService = service;
+ mHandler = new MessageHandler(handler.getLooper());
}
public IMediaSession getSessionBinder() {
@@ -69,61 +92,132 @@
return mController;
}
- public void setPlaybackStateInternal(int state) {
- mPlaybackState = state;
- for (int i = mSessionCallbacks.size() - 1; i >= 0; i--) {
- IMediaControllerCallback cb = mSessionCallbacks.get(i);
- try {
- cb.onPlaybackUpdate(state);
- } catch (RemoteException e) {
- Log.d(TAG, "SessionCallback object dead in setPlaybackState.", e);
- mSessionCallbacks.remove(i);
- }
- }
- }
-
@Override
public void binderDied() {
mService.sessionDied(this);
}
+ public boolean isPublished() {
+ return mIsPublished;
+ }
+
private void onDestroy() {
mService.destroySession(this);
}
+ private void pushPlaybackStateUpdate() {
+ synchronized (mControllerLock) {
+ for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
+ IMediaControllerCallback cb = mControllerCallbacks.get(i);
+ try {
+ cb.onPlaybackStateChanged(mPlaybackState);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Removing dead callback in pushPlaybackStateUpdate.", e);
+ mControllerCallbacks.remove(i);
+ }
+ }
+ }
+ }
+
+ private void pushMetadataUpdate() {
+ synchronized (mControllerLock) {
+ for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
+ IMediaControllerCallback cb = mControllerCallbacks.get(i);
+ try {
+ cb.onMetadataChanged(mMetadata);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Removing dead callback in pushMetadataUpdate.", e);
+ mControllerCallbacks.remove(i);
+ }
+ }
+ }
+ }
+
+ private void pushRouteUpdate() {
+ synchronized (mControllerLock) {
+ for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
+ IMediaControllerCallback cb = mControllerCallbacks.get(i);
+ try {
+ cb.onRouteChanged(mRoute);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Removing dead callback in pushRouteUpdate.", e);
+ mControllerCallbacks.remove(i);
+ }
+ }
+ }
+ }
+
+ private void pushEvent(String event, Bundle data) {
+ synchronized (mControllerLock) {
+ for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
+ IMediaControllerCallback cb = mControllerCallbacks.get(i);
+ try {
+ cb.onEvent(event, data);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Removing dead callback in pushRouteUpdate.", e);
+ mControllerCallbacks.remove(i);
+ }
+ }
+ }
+ }
+
private final class SessionStub extends IMediaSession.Stub {
@Override
- public void setPlaybackState(int state) throws RemoteException {
- setPlaybackStateInternal(state);
- }
-
- @Override
- public void destroy() throws RemoteException {
+ public void destroy() {
onDestroy();
}
@Override
- public void sendEvent(Bundle data) throws RemoteException {
+ public void sendEvent(String event, Bundle data) {
+ mHandler.post(MessageHandler.MSG_SEND_EVENT, event, data);
}
@Override
- public IMediaController getMediaSessionToken() throws RemoteException {
+ public IMediaController getMediaController() {
return mController;
}
@Override
- public void setMetadata(Bundle metadata) throws RemoteException {
+ public void setRouteState(Bundle routeState) {
}
@Override
- public void setRouteState(Bundle routeState) throws RemoteException {
+ public void setRoute(Bundle mediaRouteDescriptor) {
+ mRoute = mediaRouteDescriptor;
+ mHandler.post(MessageHandler.MSG_UPDATE_ROUTE);
}
@Override
- public void setRoute(Bundle medaiRouteDescriptor) throws RemoteException {
+ public void publish() {
+ mIsPublished = true; // TODO push update to service
+ }
+ @Override
+ public void setTransportPerformerEnabled() {
+ mTransportPerformerEnabled = true;
}
+ @Override
+ public List<String> getSupportedInterfaces() {
+ return mInterfaces;
+ }
+
+ @Override
+ public void setMetadata(MediaMetadata metadata) {
+ mMetadata = metadata;
+ mHandler.post(MessageHandler.MSG_UPDATE_METADATA);
+ }
+
+ @Override
+ public void setPlaybackState(PlaybackState state) {
+ mPlaybackState = state;
+ mHandler.post(MessageHandler.MSG_UPDATE_PLAYBACK_STATE);
+ }
+
+ @Override
+ public void setRatingType(int type) {
+ mRatingType = type;
+ }
}
class SessionCb {
@@ -139,32 +233,96 @@
try {
mCb.onMediaButton(mediaButtonIntent);
} catch (RemoteException e) {
- Log.d(TAG, "Controller object dead in sendMediaRequest.", e);
- onDestroy();
+ Slog.e(TAG, "Remote failure in sendMediaRequest.", e);
}
}
- public void sendCommand(String command, Bundle extras) {
+ public void sendCommand(String command, Bundle extras, ResultReceiver cb) {
try {
- mCb.onCommand(command, extras);
+ mCb.onCommand(command, extras, cb);
} catch (RemoteException e) {
- Log.d(TAG, "Controller object dead in sendCommand.", e);
- onDestroy();
+ Slog.e(TAG, "Remote failure in sendCommand.", e);
}
}
- public void registerCallbackListener(IMediaSessionCallback cb) {
-
+ public void play() {
+ try {
+ mCb.onPlay();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in play.", e);
+ }
}
+ public void pause() {
+ try {
+ mCb.onPause();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in pause.", e);
+ }
+ }
+
+ public void stop() {
+ try {
+ mCb.onStop();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in stop.", e);
+ }
+ }
+
+ public void next() {
+ try {
+ mCb.onNext();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in next.", e);
+ }
+ }
+
+ public void previous() {
+ try {
+ mCb.onPrevious();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in previous.", e);
+ }
+ }
+
+ public void fastForward() {
+ try {
+ mCb.onFastForward();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in fastForward.", e);
+ }
+ }
+
+ public void rewind() {
+ try {
+ mCb.onRewind();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in rewind.", e);
+ }
+ }
+
+ public void seekTo(long pos) {
+ try {
+ mCb.onSeekTo(pos);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in seekTo.", e);
+ }
+ }
+
+ public void rate(Rating rating) {
+ try {
+ mCb.onRate(rating);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in rate.", e);
+ }
+ }
}
class ControllerStub extends IMediaController.Stub {
- /*
- */
@Override
- public void sendCommand(String command, Bundle extras) throws RemoteException {
- mSessionCb.sendCommand(command, extras);
+ public void sendCommand(String command, Bundle extras, ResultReceiver cb)
+ throws RemoteException {
+ mSessionCb.sendCommand(command, extras, cb);
}
@Override
@@ -172,29 +330,130 @@
mSessionCb.sendMediaButton(mediaButtonIntent);
}
- /*
- */
@Override
- public void registerCallbackListener(IMediaControllerCallback cb) throws RemoteException {
- if (!mSessionCallbacks.contains(cb)) {
- mSessionCallbacks.add(cb);
+ public void registerCallbackListener(IMediaControllerCallback cb) {
+ synchronized (mControllerLock) {
+ if (!mControllerCallbacks.contains(cb)) {
+ mControllerCallbacks.add(cb);
+ }
}
}
- /*
- */
@Override
public void unregisterCallbackListener(IMediaControllerCallback cb)
throws RemoteException {
- mSessionCallbacks.remove(cb);
+ synchronized (mControllerLock) {
+ mControllerCallbacks.remove(cb);
+ }
}
- /*
- */
@Override
- public int getPlaybackState() throws RemoteException {
+ public void play() throws RemoteException {
+ mSessionCb.play();
+ }
+
+ @Override
+ public void pause() throws RemoteException {
+ mSessionCb.pause();
+ }
+
+ @Override
+ public void stop() throws RemoteException {
+ mSessionCb.stop();
+ }
+
+ @Override
+ public void next() throws RemoteException {
+ mSessionCb.next();
+ }
+
+ @Override
+ public void previous() throws RemoteException {
+ mSessionCb.previous();
+ }
+
+ @Override
+ public void fastForward() throws RemoteException {
+ mSessionCb.fastForward();
+ }
+
+ @Override
+ public void rewind() throws RemoteException {
+ mSessionCb.rewind();
+ }
+
+ @Override
+ public void seekTo(long pos) throws RemoteException {
+ mSessionCb.seekTo(pos);
+ }
+
+ @Override
+ public void rate(Rating rating) throws RemoteException {
+ mSessionCb.rate(rating);
+ }
+
+
+ @Override
+ public MediaMetadata getMetadata() {
+ return mMetadata;
+ }
+
+ @Override
+ public PlaybackState getPlaybackState() {
return mPlaybackState;
}
+
+ @Override
+ public int getRatingType() {
+ return mRatingType;
+ }
+
+ @Override
+ public boolean isTransportControlEnabled() throws RemoteException {
+ return mTransportPerformerEnabled;
+ }
+ }
+
+ private class MessageHandler extends Handler {
+ private static final int MSG_UPDATE_METADATA = 1;
+ private static final int MSG_UPDATE_PLAYBACK_STATE = 2;
+ private static final int MSG_UPDATE_ROUTE = 3;
+ private static final int MSG_SEND_EVENT = 4;
+
+ public MessageHandler(Looper looper) {
+ super(looper);
+ }
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_METADATA:
+ pushMetadataUpdate();
+ break;
+ case MSG_UPDATE_PLAYBACK_STATE:
+ pushPlaybackStateUpdate();
+ break;
+ case MSG_UPDATE_ROUTE:
+ pushRouteUpdate();
+ break;
+ case MSG_SEND_EVENT:
+ pushEvent((String) msg.obj, msg.getData());
+ break;
+ }
+ }
+
+ public void post(int what) {
+ post(what, null);
+ }
+
+ public void post(int what, Object obj) {
+ obtainMessage(what, obj).sendToTarget();
+ }
+
+ public void post(int what, Object obj, Bundle data) {
+ Message msg = obtainMessage(what, obj);
+ msg.setData(data);
+ msg.sendToTarget();
+ }
}
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index a7ff926..8fe6055 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -21,6 +21,7 @@
import android.media.session.IMediaSessionCallback;
import android.media.session.IMediaSessionManager;
import android.os.Binder;
+import android.os.Handler;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
@@ -41,6 +42,8 @@
private final ArrayList<MediaSessionRecord> mSessions
= new ArrayList<MediaSessionRecord>();
private final Object mLock = new Object();
+ // TODO do we want a separate thread for handling mediasession messages?
+ private final Handler mHandler = new Handler();
public MediaSessionService(Context context) {
super(context);
@@ -91,7 +94,8 @@
private MediaSessionRecord createSessionLocked(int pid, String packageName,
IMediaSessionCallback cb, String tag) {
- final MediaSessionRecord session = new MediaSessionRecord(pid, packageName, cb, tag, this);
+ final MediaSessionRecord session = new MediaSessionRecord(pid, packageName, cb, tag, this,
+ mHandler);
try {
cb.asBinder().linkToDeath(session, 0);
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/power/DisplayPowerController.java b/services/core/java/com/android/server/power/DisplayPowerController.java
index 12d51aa..6d3702a 100644
--- a/services/core/java/com/android/server/power/DisplayPowerController.java
+++ b/services/core/java/com/android/server/power/DisplayPowerController.java
@@ -479,7 +479,6 @@
&& mProximity == PROXIMITY_POSITIVE) {
mScreenOffBecauseOfProximity = true;
sendOnProximityPositiveWithWakelock();
- setScreenOn(false);
}
} else if (mWaitingForNegativeProximity
&& mScreenOffBecauseOfProximity
@@ -544,59 +543,62 @@
mUsingScreenAutoBrightness = false;
}
- // Animate the screen on or off.
- if (!mScreenOffBecauseOfProximity) {
- if (mPowerRequest.wantScreenOnAny()) {
- // Want screen on.
- // Wait for previous off animation to complete beforehand.
- // It is relatively short but if we cancel it and switch to the
- // on animation immediately then the results are pretty ugly.
- if (!mElectronBeamOffAnimator.isStarted()) {
- // Turn the screen on. The contents of the screen may not yet
- // be visible if the electron beam has not been dismissed because
- // its last frame of animation is solid black.
- setScreenOn(true);
+ // Animate the screen on or off unless blocked.
+ if (mScreenOffBecauseOfProximity) {
+ // Screen off due to proximity.
+ setScreenOn(false);
+ unblockScreenOn();
+ } else if (mPowerRequest.wantScreenOnAny()) {
+ // Want screen on.
+ // Wait for previous off animation to complete beforehand.
+ // It is relatively short but if we cancel it and switch to the
+ // on animation immediately then the results are pretty ugly.
+ if (!mElectronBeamOffAnimator.isStarted()) {
+ // Turn the screen on. The contents of the screen may not yet
+ // be visible if the electron beam has not been dismissed because
+ // its last frame of animation is solid black.
+ setScreenOn(true);
- if (mPowerRequest.blockScreenOn
- && mPowerState.getElectronBeamLevel() == 0.0f) {
- blockScreenOn();
- } else {
- unblockScreenOn();
- if (USE_ELECTRON_BEAM_ON_ANIMATION) {
- if (!mElectronBeamOnAnimator.isStarted()) {
- if (mPowerState.getElectronBeamLevel() == 1.0f) {
- mPowerState.dismissElectronBeam();
- } else if (mPowerState.prepareElectronBeam(
- mElectronBeamFadesConfig ?
- ElectronBeam.MODE_FADE :
- ElectronBeam.MODE_WARM_UP)) {
- mElectronBeamOnAnimator.start();
- } else {
- mElectronBeamOnAnimator.end();
- }
+ if (mPowerRequest.blockScreenOn
+ && mPowerState.getElectronBeamLevel() == 0.0f) {
+ blockScreenOn();
+ } else {
+ unblockScreenOn();
+ if (USE_ELECTRON_BEAM_ON_ANIMATION) {
+ if (!mElectronBeamOnAnimator.isStarted()) {
+ if (mPowerState.getElectronBeamLevel() == 1.0f) {
+ mPowerState.dismissElectronBeam();
+ } else if (mPowerState.prepareElectronBeam(
+ mElectronBeamFadesConfig ?
+ ElectronBeam.MODE_FADE :
+ ElectronBeam.MODE_WARM_UP)) {
+ mElectronBeamOnAnimator.start();
+ } else {
+ mElectronBeamOnAnimator.end();
}
- } else {
- mPowerState.setElectronBeamLevel(1.0f);
- mPowerState.dismissElectronBeam();
}
+ } else {
+ mPowerState.setElectronBeamLevel(1.0f);
+ mPowerState.dismissElectronBeam();
}
}
- } else {
- // Want screen off.
- // Wait for previous on animation to complete beforehand.
- if (!mElectronBeamOnAnimator.isStarted()) {
- if (!mElectronBeamOffAnimator.isStarted()) {
- if (mPowerState.getElectronBeamLevel() == 0.0f) {
- setScreenOn(false);
- } else if (mPowerState.prepareElectronBeam(
- mElectronBeamFadesConfig ?
- ElectronBeam.MODE_FADE :
- ElectronBeam.MODE_COOL_DOWN)
- && mPowerState.isScreenOn()) {
- mElectronBeamOffAnimator.start();
- } else {
- mElectronBeamOffAnimator.end();
- }
+ }
+ } else {
+ // Want screen off.
+ // Wait for previous on animation to complete beforehand.
+ unblockScreenOn();
+ if (!mElectronBeamOnAnimator.isStarted()) {
+ if (!mElectronBeamOffAnimator.isStarted()) {
+ if (mPowerState.getElectronBeamLevel() == 0.0f) {
+ setScreenOn(false);
+ } else if (mPowerState.prepareElectronBeam(
+ mElectronBeamFadesConfig ?
+ ElectronBeam.MODE_FADE :
+ ElectronBeam.MODE_COOL_DOWN)
+ && mPowerState.isScreenOn()) {
+ mElectronBeamOffAnimator.start();
+ } else {
+ mElectronBeamOffAnimator.end();
}
}
}
@@ -641,15 +643,15 @@
private void unblockScreenOn() {
if (mScreenOnWasBlocked) {
mScreenOnWasBlocked = false;
- if (DEBUG) {
- Slog.d(TAG, "Unblocked screen on after " +
- (SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime) + " ms");
+ long delay = SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime;
+ if (delay > 1000 || DEBUG) {
+ Slog.d(TAG, "Unblocked screen on after " + delay + " ms");
}
}
}
private void setScreenOn(boolean on) {
- if (!mPowerState.isScreenOn() == on) {
+ if (mPowerState.isScreenOn() != on) {
mPowerState.setScreenOn(on);
if (on) {
mNotifier.onScreenOn();
diff --git a/services/core/java/com/android/server/power/DisplayPowerState.java b/services/core/java/com/android/server/power/DisplayPowerState.java
index 42af4b4..8e331ad 100644
--- a/services/core/java/com/android/server/power/DisplayPowerState.java
+++ b/services/core/java/com/android/server/power/DisplayPowerState.java
@@ -304,8 +304,15 @@
int brightness = mScreenOn && mElectronBeamLevel > 0f ? mScreenBrightness : 0;
if (mPhotonicModulator.setState(mScreenOn, brightness)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Screen ready");
+ }
mScreenReady = true;
invokeCleanListenerIfNeeded();
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Screen not ready");
+ }
}
}
};
@@ -355,7 +362,7 @@
AsyncTask.THREAD_POOL_EXECUTOR.execute(mTask);
}
}
- return mChangeInProgress;
+ return !mChangeInProgress;
}
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 40ebe8d..fab972f 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -1047,6 +1047,9 @@
if (!mSystemReady || mDirty == 0) {
return;
}
+ if (!Thread.holdsLock(mLock)) {
+ Slog.wtf(TAG, "Power manager lock was not held when calling updatePowerStateLocked");
+ }
// Phase 0: Basic state updates.
updateIsPoweredLocked(mDirty);
@@ -1813,7 +1816,12 @@
private boolean isScreenOnInternal() {
synchronized (mLock) {
- return isScreenOnLocked();
+ // XXX This is a temporary hack to let certain parts of the system pretend the
+ // screen is still on even when dozing and we would normally want to report
+ // screen off. Will be removed when the window manager is modified to use
+ // the true blanking state of the display.
+ return isScreenOnLocked()
+ || mWakefulness == WAKEFULNESS_DOZING;
}
}
diff --git a/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java b/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
index 7ff81e4..3114ca9 100644
--- a/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
+++ b/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
@@ -1,7 +1,24 @@
+/*
+ * 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.onemedia;
import android.app.Activity;
+import android.media.session.MediaMetadata;
+import android.media.session.PlaybackState;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
@@ -79,10 +96,10 @@
switch (v.getId()) {
case R.id.play_button:
Log.d(TAG, "Play button pressed, in state " + mPlaybackState);
- if (mPlaybackState == Renderer.STATE_PAUSED
- || mPlaybackState == Renderer.STATE_ENDED) {
+ if (mPlaybackState == PlaybackState.PLAYSTATE_PAUSED
+ || mPlaybackState == PlaybackState.PLAYSTATE_STOPPED) {
mPlayer.play();
- } else if (mPlaybackState == Renderer.STATE_PLAYING) {
+ } else if (mPlaybackState == PlaybackState.PLAYSTATE_PLAYING) {
mPlayer.pause();
}
break;
@@ -97,48 +114,55 @@
private PlayerController.Listener mListener = new PlayerController.Listener() {
@Override
- public void onSessionStateChange(int state) {
- mPlaybackState = state;
+ public void onPlaybackStateChange(PlaybackState state) {
+ mPlaybackState = state.getState();
boolean enablePlay = false;
+ StringBuilder statusBuilder = new StringBuilder();
switch (mPlaybackState) {
- case Renderer.STATE_PLAYING:
- mStatusView.setText("playing");
+ case PlaybackState.PLAYSTATE_PLAYING:
+ statusBuilder.append("playing");
mPlayButton.setText("Pause");
enablePlay = true;
break;
- case Renderer.STATE_PAUSED:
- mStatusView.setText("paused");
+ case PlaybackState.PLAYSTATE_PAUSED:
+ statusBuilder.append("paused");
mPlayButton.setText("Play");
enablePlay = true;
break;
- case Renderer.STATE_ENDED:
- mStatusView.setText("ended");
+ case PlaybackState.PLAYSTATE_STOPPED:
+ statusBuilder.append("ended");
mPlayButton.setText("Play");
enablePlay = true;
break;
- case Renderer.STATE_ERROR:
- mStatusView.setText("error");
+ case PlaybackState.PLAYSTATE_ERROR:
+ statusBuilder.append("error: ").append(state.getErrorMessage());
break;
- case Renderer.STATE_PREPARING:
- mStatusView.setText("preparing");
+ case PlaybackState.PLAYSTATE_BUFFERING:
+ statusBuilder.append("buffering");
break;
- case Renderer.STATE_READY:
- mStatusView.setText("ready");
+ case PlaybackState.PLAYSTATE_NONE:
+ statusBuilder.append("none");
break;
- case Renderer.STATE_STOPPED:
- mStatusView.setText("stopped");
- break;
+ default:
+ statusBuilder.append(mPlaybackState);
}
+ statusBuilder.append(" -- At position: ").append(state.getPosition());
+ mStatusView.setText(statusBuilder.toString());
mPlayButton.setEnabled(enablePlay);
}
@Override
- public void onPlayerStateChange(int state) {
+ public void onConnectionStateChange(int state) {
if (state == PlayerController.STATE_DISCONNECTED) {
setControlsEnabled(false);
} else if (state == PlayerController.STATE_CONNECTED) {
setControlsEnabled(true);
}
}
+
+ @Override
+ public void onMetadataChange(MediaMetadata metadata) {
+ Log.d(TAG, "Metadata update! Title: " + metadata);
+ }
};
}
diff --git a/tests/OneMedia/src/com/android/onemedia/OnePlayerService.java b/tests/OneMedia/src/com/android/onemedia/OnePlayerService.java
index 01610cd..573f7ff 100644
--- a/tests/OneMedia/src/com/android/onemedia/OnePlayerService.java
+++ b/tests/OneMedia/src/com/android/onemedia/OnePlayerService.java
@@ -1,3 +1,18 @@
+/*
+ * 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.onemedia;
import android.content.Context;
@@ -5,9 +20,6 @@
import java.util.ArrayList;
-/**
- * TODO: Insert description here. (generated by epastern)
- */
public class OnePlayerService extends PlayerService {
private static final String TAG = "OnePlayerService";
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerController.java b/tests/OneMedia/src/com/android/onemedia/PlayerController.java
index 3f15db5..e831ec6 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerController.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerController.java
@@ -1,8 +1,27 @@
+/*
+ * 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.onemedia;
import android.media.session.MediaController;
+import android.media.session.MediaMetadata;
import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
+import android.media.session.TransportController;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
@@ -11,22 +30,23 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.util.Log;
-import android.view.KeyEvent;
import com.android.onemedia.playback.RequestUtils;
public class PlayerController {
- private static final String TAG = "PlayerSession";
+ private static final String TAG = "PlayerController";
public static final int STATE_DISCONNECTED = 0;
public static final int STATE_CONNECTED = 1;
protected MediaController mController;
protected IPlayerService mBinder;
+ protected TransportController mTransportControls;
private final Intent mServiceIntent;
private Context mContext;
private Listener mListener;
+ private TransportListener mTransportListener = new TransportListener();
private SessionCallback mControllerCb;
private MediaSessionManager mManager;
private Handler mHandler = new Handler();
@@ -52,7 +72,7 @@
Log.d(TAG, "Listener set to " + listener + " session is " + mController);
if (mListener != null) {
mHandler = new Handler();
- mListener.onPlayerStateChange(
+ mListener.onConnectionStateChange(
mController == null ? STATE_DISCONNECTED : STATE_CONNECTED);
}
}
@@ -70,11 +90,15 @@
}
public void play() {
- mController.sendMediaButton(KeyEvent.KEYCODE_MEDIA_PLAY);
+ if (mTransportControls != null) {
+ mTransportControls.play();
+ }
}
public void pause() {
- mController.sendMediaButton(KeyEvent.KEYCODE_MEDIA_PAUSE);
+ if (mTransportControls != null) {
+ mTransportControls.pause();
+ }
}
public void setContent(String source) {
@@ -113,10 +137,11 @@
}
mBinder = null;
mController = null;
+ mTransportControls = null;
Log.d(TAG, "Disconnected from PlayerService");
if (mListener != null) {
- mListener.onPlayerStateChange(STATE_DISCONNECTED);
+ mListener.onConnectionStateChange(STATE_DISCONNECTED);
}
}
@@ -125,33 +150,60 @@
mBinder = IPlayerService.Stub.asInterface(service);
Log.d(TAG, "service is " + service + " binder is " + mBinder);
try {
- mController = new MediaController(mBinder.getSessionToken());
+ mController = MediaController.fromToken(mBinder.getSessionToken());
} catch (RemoteException e) {
Log.e(TAG, "Error getting session", e);
return;
}
mController.addCallback(mControllerCb, mHandler);
+ mTransportControls = mController.getTransportController();
+ if (mTransportControls != null) {
+ mTransportControls.addStateListener(mTransportListener);
+ }
Log.d(TAG, "Ready to use PlayerService");
if (mListener != null) {
- mListener.onPlayerStateChange(STATE_CONNECTED);
+ mListener.onConnectionStateChange(STATE_CONNECTED);
+ if (mTransportControls != null) {
+ mListener.onPlaybackStateChange(mTransportControls.getPlaybackState());
+ }
}
}
};
private class SessionCallback extends MediaController.Callback {
@Override
- public void onPlaybackStateChange(int state) {
- if (mListener != null) {
- mListener.onSessionStateChange(state);
+ public void onRouteChanged(Bundle route) {
+ // TODO
+ }
+ }
+
+ private class TransportListener extends TransportController.TransportStateListener {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState state) {
+ if (state == null) {
+ return;
}
+ Log.d(TAG, "Received playback state change to state " + state.getState());
+ if (mListener != null) {
+ mListener.onPlaybackStateChange(state);
+ }
+ }
+
+ @Override
+ public void onMetadataChanged(MediaMetadata metadata) {
+ if (metadata == null) {
+ return;
+ }
+ Log.d(TAG, "Received metadata change, title is "
+ + metadata.getString(MediaMetadata.METADATA_KEY_TITLE));
}
}
public interface Listener {
- public void onSessionStateChange(int state);
-
- public void onPlayerStateChange(int state);
+ public void onPlaybackStateChange(PlaybackState state);
+ public void onMetadataChange(MediaMetadata metadata);
+ public void onConnectionStateChange(int state);
}
}
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerService.java b/tests/OneMedia/src/com/android/onemedia/PlayerService.java
index 0b2ba8f..0ad6dd1 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerService.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerService.java
@@ -1,11 +1,28 @@
+/*
+ * 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.onemedia;
import android.app.Service;
import android.content.Intent;
import android.media.session.MediaSessionToken;
+import android.media.session.PlaybackState;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.Log;
import com.android.onemedia.playback.IRequestCallback;
import com.android.onemedia.playback.RequestUtils;
@@ -18,14 +35,19 @@
private PlayerBinder mBinder;
private PlayerSession mSession;
private Intent mIntent;
+ private boolean mStarted = false;
private ArrayList<IPlayerCallback> mCbs = new ArrayList<IPlayerCallback>();
@Override
public void onCreate() {
+ Log.d(TAG, "onCreate");
mIntent = onCreateServiceIntent();
- mSession = onCreatePlayerController();
- mSession.createSession();
+ if (mSession == null) {
+ mSession = onCreatePlayerController();
+ mSession.createSession();
+ mSession.setListener(mPlayerListener);
+ }
}
@Override
@@ -38,12 +60,31 @@
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.d(TAG, "onStartCommand");
return START_STICKY;
}
@Override
public void onDestroy() {
+ Log.d(TAG, "onDestroy");
mSession.onDestroy();
+ mSession = null;
+ }
+
+ public void onPlaybackStarted() {
+ if (!mStarted) {
+ Log.d(TAG, "Starting self");
+ startService(onCreateServiceIntent());
+ mStarted = true;
+ }
+ }
+
+ public void onPlaybackEnded() {
+ if (mStarted) {
+ Log.d(TAG, "Stopping self");
+ stopSelf();
+ mStarted = false;
+ }
}
protected Intent onCreateServiceIntent() {
@@ -58,6 +99,21 @@
return null;
}
+ private final PlayerSession.Listener mPlayerListener = new PlayerSession.Listener() {
+ @Override
+ public void onPlayStateChanged(PlaybackState state) {
+ switch (state.getState()) {
+ case PlaybackState.PLAYSTATE_PLAYING:
+ onPlaybackStarted();
+ break;
+ case PlaybackState.PLAYSTATE_STOPPED:
+ case PlaybackState.PLAYSTATE_ERROR:
+ onPlaybackEnded();
+ break;
+ }
+ }
+ };
+
public class PlayerBinder extends IPlayerService.Stub {
@Override
public void sendRequest(String action, Bundle params, IRequestCallback cb) {
@@ -94,7 +150,6 @@
@Override
public MediaSessionToken getSessionToken() throws RemoteException {
- // TODO(epastern): Auto-generated method stub
return mSession.getSessionToken();
}
}
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
index e5fb0d0..a2d7897e 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
@@ -1,3 +1,18 @@
+/*
+ * 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.onemedia;
import android.content.Context;
@@ -5,6 +20,8 @@
import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
import android.media.session.MediaSessionToken;
+import android.media.session.PlaybackState;
+import android.media.session.TransportPerformer;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
@@ -14,14 +31,18 @@
import com.android.onemedia.playback.RendererFactory;
public class PlayerSession {
- private static final String TAG = "PlayerController";
+ private static final String TAG = "PlayerSession";
protected MediaSession mSession;
protected Context mContext;
protected RendererFactory mRendererFactory;
protected LocalRenderer mRenderer;
- protected ControllerCb mCallback;
- protected RenderListener mRenderListener;
+ protected MediaSession.Callback mCallback;
+ protected Renderer.Listener mRenderListener;
+ protected TransportPerformer mPerformer;
+
+ protected PlaybackState mPlaybackState;
+ protected Listener mListener;
public PlayerSession(Context context) {
mContext = context;
@@ -29,6 +50,9 @@
mRenderer = new LocalRenderer(context, null);
mCallback = new ControllerCb();
mRenderListener = new RenderListener();
+ mPlaybackState = new PlaybackState();
+ mPlaybackState.setActions(PlaybackState.ACTION_PAUSE
+ | PlaybackState.ACTION_PLAY);
mRenderer.registerListener(mRenderListener);
}
@@ -42,6 +66,10 @@
Log.d(TAG, "Creating session for package " + mContext.getBasePackageName());
mSession = man.createSession("OneMedia");
mSession.addCallback(mCallback);
+ mPerformer = mSession.setTransportPerformerEnabled();
+ mPerformer.addListener(new TransportListener());
+ mPerformer.setPlaybackState(mPlaybackState);
+ mSession.publish();
}
public void onDestroy() {
@@ -54,6 +82,10 @@
}
}
+ public void setListener(Listener listener) {
+ mListener = listener;
+ }
+
public MediaSessionToken getSessionToken() {
return mSession.getSessionToken();
}
@@ -66,16 +98,58 @@
mRenderer.setNextContent(request);
}
- protected class RenderListener implements Renderer.Listener {
+ public interface Listener {
+ public void onPlayStateChanged(PlaybackState state);
+ }
+
+ private class RenderListener implements Renderer.Listener {
@Override
public void onError(int type, int extra, Bundle extras, Throwable error) {
- mSession.setPlaybackState(Renderer.STATE_ERROR);
+ Log.d(TAG, "Sending onError with type " + type + " and extra " + extra);
+ mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR);
+ if (error != null) {
+ mPlaybackState.setErrorMessage(error.getLocalizedMessage());
+ }
+ mPerformer.setPlaybackState(mPlaybackState);
+ if (mListener != null) {
+ mListener.onPlayStateChanged(mPlaybackState);
+ }
}
@Override
public void onStateChanged(int newState) {
- mSession.setPlaybackState(newState);
+ if (newState != Renderer.STATE_ERROR) {
+ mPlaybackState.setErrorMessage(null);
+ }
+ switch (newState) {
+ case Renderer.STATE_ENDED:
+ case Renderer.STATE_STOPPED:
+ mPlaybackState.setState(PlaybackState.PLAYSTATE_STOPPED);
+ break;
+ case Renderer.STATE_INIT:
+ case Renderer.STATE_PREPARING:
+ mPlaybackState.setState(PlaybackState.PLAYSTATE_BUFFERING);
+ break;
+ case Renderer.STATE_ERROR:
+ mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR);
+ break;
+ case Renderer.STATE_PAUSED:
+ mPlaybackState.setState(PlaybackState.PLAYSTATE_PAUSED);
+ break;
+ case Renderer.STATE_PLAYING:
+ mPlaybackState.setState(PlaybackState.PLAYSTATE_PLAYING);
+ break;
+ default:
+ mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR);
+ mPlaybackState.setErrorMessage("unkown state");
+ break;
+ }
+ mPlaybackState.setPosition(mRenderer.getSeekPosition());
+ mPerformer.setPlaybackState(mPlaybackState);
+ if (mListener != null) {
+ mListener.onPlayStateChanged(mPlaybackState);
+ }
}
@Override
@@ -84,7 +158,13 @@
@Override
public void onFocusLost() {
- mSession.setPlaybackState(Renderer.STATE_PAUSED);
+ Log.d(TAG, "Focus lost, changing state to " + Renderer.STATE_PAUSED);
+ mPlaybackState.setState(PlaybackState.PLAYSTATE_PAUSED);
+ mPlaybackState.setPosition(mRenderer.getSeekPosition());
+ mPerformer.setPlaybackState(mPlaybackState);
+ if (mListener != null) {
+ mListener.onPlayStateChanged(mPlaybackState);
+ }
}
@Override
@@ -93,7 +173,7 @@
}
- protected class ControllerCb extends MediaSession.Callback {
+ private class ControllerCb extends MediaSession.Callback {
@Override
public void onMediaButton(Intent mediaRequestIntent) {
@@ -114,4 +194,16 @@
}
}
+ private class TransportListener extends TransportPerformer.Listener {
+ @Override
+ public void onPlay() {
+ mRenderer.onPlay();
+ }
+
+ @Override
+ public void onPause() {
+ mRenderer.onPause();
+ }
+ }
+
}
diff --git a/tests/OneMedia/src/com/android/onemedia/playback/LocalRenderer.java b/tests/OneMedia/src/com/android/onemedia/playback/LocalRenderer.java
index 7493366..7f62f66 100644
--- a/tests/OneMedia/src/com/android/onemedia/playback/LocalRenderer.java
+++ b/tests/OneMedia/src/com/android/onemedia/playback/LocalRenderer.java
@@ -499,11 +499,12 @@
@Override
public boolean onPause() {
MediaPlayer player = mPlayer;
+ // If the user paused us make sure we won't start playing again until
+ // asked to
+ mPlayOnReady = false;
if (player != null && (mState & CAN_PAUSE) != 0) {
player.pause();
setState(STATE_PAUSED);
- } else if ((mState & CAN_READY_PLAY) != 0) {
- mPlayOnReady = false;
} else if (!isPaused()) {
return false;
}