Merge "Remove dependency on audio_* location"
diff --git a/api/current.txt b/api/current.txt
index 6bac58a..93d9d44 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -26377,6 +26377,7 @@
ctor public EdgeEffect(android.content.Context);
method public boolean draw(android.graphics.Canvas);
method public void finish();
+ method public android.graphics.Rect getBounds();
method public boolean isFinished();
method public void onAbsorb(int);
method public void onPull(float);
diff --git a/cmds/system_server/library/Android.mk b/cmds/system_server/library/Android.mk
index 7d08a8c..9f92330 100644
--- a/cmds/system_server/library/Android.mk
+++ b/cmds/system_server/library/Android.mk
@@ -8,10 +8,7 @@
native = $(LOCAL_PATH)/../../../../native
LOCAL_C_INCLUDES := \
- $(base)/services/camera/libcameraservice \
- $(base)/services/audioflinger \
$(base)/services/sensorservice \
- $(base)/media/libmediaplayerservice \
$(native)/services/surfaceflinger \
$(JNI_H_INCLUDE)
@@ -19,9 +16,6 @@
libandroid_runtime \
libsensorservice \
libsurfaceflinger \
- libaudioflinger \
- libcameraservice \
- libmediaplayerservice \
libinput \
libutils \
libbinder \
diff --git a/cmds/system_server/library/system_init.cpp b/cmds/system_server/library/system_init.cpp
index bfbc138..745c34a 100644
--- a/cmds/system_server/library/system_init.cpp
+++ b/cmds/system_server/library/system_init.cpp
@@ -15,10 +15,6 @@
#include <utils/Log.h>
#include <SurfaceFlinger.h>
-#include <AudioFlinger.h>
-#include <CameraService.h>
-#include <AudioPolicyService.h>
-#include <MediaPlayerService.h>
#include <SensorService.h>
#include <android_runtime/AndroidRuntime.h>
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index eb83dbc..4b31552 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -17,6 +17,7 @@
package android.content;
import android.content.res.AssetFileDescriptor;
+import android.database.BulkCursorDescriptor;
import android.database.BulkCursorNative;
import android.database.BulkCursorToCursorAdaptor;
import android.database.Cursor;
@@ -113,20 +114,14 @@
if (cursor != null) {
CursorToBulkCursorAdaptor adaptor = new CursorToBulkCursorAdaptor(
cursor, observer, getProviderName());
- final IBinder binder = adaptor.asBinder();
- final int count = adaptor.count();
- final int index = BulkCursorToCursorAdaptor.findRowIdColumnIndex(
- adaptor.getColumnNames());
- final boolean wantsAllOnMoveCalls = adaptor.getWantsAllOnMoveCalls();
+ BulkCursorDescriptor d = adaptor.getBulkCursorDescriptor();
reply.writeNoException();
- reply.writeStrongBinder(binder);
- reply.writeInt(count);
- reply.writeInt(index);
- reply.writeInt(wantsAllOnMoveCalls ? 1 : 0);
+ reply.writeInt(1);
+ d.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeNoException();
- reply.writeStrongBinder(null);
+ reply.writeInt(0);
}
return true;
@@ -369,12 +364,9 @@
DatabaseUtils.readExceptionFromParcel(reply);
- IBulkCursor bulkCursor = BulkCursorNative.asInterface(reply.readStrongBinder());
- if (bulkCursor != null) {
- int rowCount = reply.readInt();
- int idColumnPosition = reply.readInt();
- boolean wantsAllOnMoveCalls = reply.readInt() != 0;
- adaptor.initialize(bulkCursor, rowCount, idColumnPosition, wantsAllOnMoveCalls);
+ if (reply.readInt() != 0) {
+ BulkCursorDescriptor d = BulkCursorDescriptor.CREATOR.createFromParcel(reply);
+ adaptor.initialize(d);
} else {
adaptor.close();
adaptor = null;
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index b28ed8d..dd6692c 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -33,10 +33,39 @@
public abstract class AbstractCursor implements CrossProcessCursor {
private static final String TAG = "Cursor";
- DataSetObservable mDataSetObservable = new DataSetObservable();
- ContentObservable mContentObservable = new ContentObservable();
+ /**
+ * @deprecated This is never updated by this class and should not be used
+ */
+ @Deprecated
+ protected HashMap<Long, Map<String, Object>> mUpdatedRows;
- Bundle mExtras = Bundle.EMPTY;
+ protected int mPos;
+
+ /**
+ * This must be set to the index of the row ID column by any
+ * subclass that wishes to support updates.
+ */
+ protected int mRowIdColumnIndex;
+
+ /**
+ * If {@link #mRowIdColumnIndex} is not -1 this contains contains the value of
+ * the column at {@link #mRowIdColumnIndex} for the current row this cursor is
+ * pointing at.
+ */
+ protected Long mCurrentRowID;
+
+ protected boolean mClosed;
+ protected ContentResolver mContentResolver;
+ private Uri mNotifyUri;
+
+ private final Object mSelfObserverLock = new Object();
+ private ContentObserver mSelfObserver;
+ private boolean mSelfObserverRegistered;
+
+ private DataSetObservable mDataSetObservable = new DataSetObservable();
+ private ContentObservable mContentObservable = new ContentObservable();
+
+ private Bundle mExtras = Bundle.EMPTY;
/* -------------------------------------------------------- */
/* These need to be implemented by subclasses */
@@ -415,30 +444,4 @@
}
}
}
-
- /**
- * @deprecated This is never updated by this class and should not be used
- */
- @Deprecated
- protected HashMap<Long, Map<String, Object>> mUpdatedRows;
-
- /**
- * This must be set to the index of the row ID column by any
- * subclass that wishes to support updates.
- */
- protected int mRowIdColumnIndex;
-
- protected int mPos;
- /**
- * If {@link #mRowIdColumnIndex} is not -1 this contains contains the value of
- * the column at {@link #mRowIdColumnIndex} for the current row this cursor is
- * pointing at.
- */
- protected Long mCurrentRowID;
- protected ContentResolver mContentResolver;
- protected boolean mClosed = false;
- private Uri mNotifyUri;
- private ContentObserver mSelfObserver;
- final private Object mSelfObserverLock = new Object();
- private boolean mSelfObserverRegistered;
}
diff --git a/core/java/android/database/BulkCursorDescriptor.java b/core/java/android/database/BulkCursorDescriptor.java
new file mode 100644
index 0000000..c1e5e63
--- /dev/null
+++ b/core/java/android/database/BulkCursorDescriptor.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2012 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.database;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Describes the properties of a {@link CursorToBulkCursorAdaptor} that are
+ * needed to initialize its {@link BulkCursorToCursorAdaptor} counterpart on the client's end.
+ *
+ * {@hide}
+ */
+public final class BulkCursorDescriptor implements Parcelable {
+ public static final Parcelable.Creator<BulkCursorDescriptor> CREATOR =
+ new Parcelable.Creator<BulkCursorDescriptor>() {
+ @Override
+ public BulkCursorDescriptor createFromParcel(Parcel in) {
+ BulkCursorDescriptor d = new BulkCursorDescriptor();
+ d.readFromParcel(in);
+ return d;
+ }
+
+ @Override
+ public BulkCursorDescriptor[] newArray(int size) {
+ return new BulkCursorDescriptor[size];
+ }
+ };
+
+ public IBulkCursor cursor;
+ public String[] columnNames;
+ public boolean wantsAllOnMoveCalls;
+ public int count;
+ public CursorWindow window;
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeStrongBinder(cursor.asBinder());
+ out.writeStringArray(columnNames);
+ out.writeInt(wantsAllOnMoveCalls ? 1 : 0);
+ out.writeInt(count);
+ if (window != null) {
+ out.writeInt(1);
+ window.writeToParcel(out, flags);
+ } else {
+ out.writeInt(0);
+ }
+ }
+
+ public void readFromParcel(Parcel in) {
+ cursor = BulkCursorNative.asInterface(in.readStrongBinder());
+ columnNames = in.readStringArray();
+ wantsAllOnMoveCalls = in.readInt() != 0;
+ count = in.readInt();
+ if (in.readInt() != 0) {
+ window = CursorWindow.CREATOR.createFromParcel(in);
+ }
+ }
+}
diff --git a/core/java/android/database/BulkCursorNative.java b/core/java/android/database/BulkCursorNative.java
index 67cf0f8..d3c11e78 100644
--- a/core/java/android/database/BulkCursorNative.java
+++ b/core/java/android/database/BulkCursorNative.java
@@ -72,26 +72,6 @@
return true;
}
- case COUNT_TRANSACTION: {
- data.enforceInterface(IBulkCursor.descriptor);
- int count = count();
- reply.writeNoException();
- reply.writeInt(count);
- return true;
- }
-
- case GET_COLUMN_NAMES_TRANSACTION: {
- data.enforceInterface(IBulkCursor.descriptor);
- String[] columnNames = getColumnNames();
- reply.writeNoException();
- reply.writeInt(columnNames.length);
- int length = columnNames.length;
- for (int i = 0; i < length; i++) {
- reply.writeString(columnNames[i]);
- }
- return true;
- }
-
case DEACTIVATE_TRANSACTION: {
data.enforceInterface(IBulkCursor.descriptor);
deactivate();
@@ -125,14 +105,6 @@
return true;
}
- case WANTS_ON_MOVE_TRANSACTION: {
- data.enforceInterface(IBulkCursor.descriptor);
- boolean result = getWantsAllOnMoveCalls();
- reply.writeNoException();
- reply.writeInt(result ? 1 : 0);
- return true;
- }
-
case GET_EXTRAS_TRANSACTION: {
data.enforceInterface(IBulkCursor.descriptor);
Bundle extras = getExtras();
@@ -217,52 +189,6 @@
}
}
- public int count() throws RemoteException
- {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- try {
- data.writeInterfaceToken(IBulkCursor.descriptor);
-
- boolean result = mRemote.transact(COUNT_TRANSACTION, data, reply, 0);
- DatabaseUtils.readExceptionFromParcel(reply);
-
- int count;
- if (result == false) {
- count = -1;
- } else {
- count = reply.readInt();
- }
- return count;
- } finally {
- data.recycle();
- reply.recycle();
- }
- }
-
- public String[] getColumnNames() throws RemoteException
- {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- try {
- data.writeInterfaceToken(IBulkCursor.descriptor);
-
- mRemote.transact(GET_COLUMN_NAMES_TRANSACTION, data, reply, 0);
- DatabaseUtils.readExceptionFromParcel(reply);
-
- String[] columnNames = null;
- int numColumns = reply.readInt();
- columnNames = new String[numColumns];
- for (int i = 0; i < numColumns; i++) {
- columnNames[i] = reply.readString();
- }
- return columnNames;
- } finally {
- data.recycle();
- reply.recycle();
- }
- }
-
public void deactivate() throws RemoteException
{
Parcel data = Parcel.obtain();
@@ -317,23 +243,6 @@
}
}
- public boolean getWantsAllOnMoveCalls() throws RemoteException {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- try {
- data.writeInterfaceToken(IBulkCursor.descriptor);
-
- mRemote.transact(WANTS_ON_MOVE_TRANSACTION, data, reply, 0);
- DatabaseUtils.readExceptionFromParcel(reply);
-
- int result = reply.readInt();
- return result != 0;
- } finally {
- data.recycle();
- reply.recycle();
- }
- }
-
public Bundle getExtras() throws RemoteException {
if (mExtras == null) {
Parcel data = Parcel.obtain();
diff --git a/core/java/android/database/BulkCursorToCursorAdaptor.java b/core/java/android/database/BulkCursorToCursorAdaptor.java
index 885046b..98c7043 100644
--- a/core/java/android/database/BulkCursorToCursorAdaptor.java
+++ b/core/java/android/database/BulkCursorToCursorAdaptor.java
@@ -30,34 +30,23 @@
private SelfContentObserver mObserverBridge = new SelfContentObserver(this);
private IBulkCursor mBulkCursor;
- private int mCount;
private String[] mColumns;
private boolean mWantsAllOnMoveCalls;
+ private int mCount;
/**
* Initializes the adaptor.
* Must be called before first use.
*/
- public void initialize(IBulkCursor bulkCursor, int count, int idIndex,
- boolean wantsAllOnMoveCalls) {
- mBulkCursor = bulkCursor;
- mColumns = null; // lazily retrieved
- mCount = count;
- mRowIdColumnIndex = idIndex;
- mWantsAllOnMoveCalls = wantsAllOnMoveCalls;
- }
-
- /**
- * Returns column index of "_id" column, or -1 if not found.
- */
- public static int findRowIdColumnIndex(String[] columnNames) {
- int length = columnNames.length;
- for (int i = 0; i < length; i++) {
- if (columnNames[i].equals("_id")) {
- return i;
- }
+ public void initialize(BulkCursorDescriptor d) {
+ mBulkCursor = d.cursor;
+ mColumns = d.columnNames;
+ mRowIdColumnIndex = DatabaseUtils.findRowIdColumnIndex(mColumns);
+ mWantsAllOnMoveCalls = d.wantsAllOnMoveCalls;
+ mCount = d.count;
+ if (d.window != null) {
+ setWindow(d.window);
}
- return -1;
}
/**
@@ -169,14 +158,6 @@
public String[] getColumnNames() {
throwIfCursorIsClosed();
- if (mColumns == null) {
- try {
- mColumns = mBulkCursor.getColumnNames();
- } catch (RemoteException ex) {
- Log.e(TAG, "Unable to fetch column names because the remote process is dead");
- return null;
- }
- }
return mColumns;
}
diff --git a/core/java/android/database/CursorToBulkCursorAdaptor.java b/core/java/android/database/CursorToBulkCursorAdaptor.java
index 167278a..525be96 100644
--- a/core/java/android/database/CursorToBulkCursorAdaptor.java
+++ b/core/java/android/database/CursorToBulkCursorAdaptor.java
@@ -132,6 +132,25 @@
}
}
+ public BulkCursorDescriptor getBulkCursorDescriptor() {
+ synchronized (mLock) {
+ throwIfCursorIsClosed();
+
+ BulkCursorDescriptor d = new BulkCursorDescriptor();
+ d.cursor = this;
+ d.columnNames = mCursor.getColumnNames();
+ d.wantsAllOnMoveCalls = mCursor.getWantsAllOnMoveCalls();
+ d.count = mCursor.getCount();
+ d.window = mCursor.getWindow();
+ if (d.window != null) {
+ // Acquire a reference to the window because its reference count will be
+ // decremented when it is returned as part of the binder call reply parcel.
+ d.window.acquireReference();
+ }
+ return d;
+ }
+ }
+
@Override
public CursorWindow getWindow(int position) {
synchronized (mLock) {
@@ -157,10 +176,9 @@
mCursor.fillWindow(position, window);
}
- // Acquire a reference before returning from this RPC.
- // The Binder proxy will decrement the reference count again as part of writing
- // the CursorWindow to the reply parcel as a return value.
if (window != null) {
+ // Acquire a reference to the window because its reference count will be
+ // decremented when it is returned as part of the binder call reply parcel.
window.acquireReference();
}
return window;
@@ -177,24 +195,6 @@
}
@Override
- public int count() {
- synchronized (mLock) {
- throwIfCursorIsClosed();
-
- return mCursor.getCount();
- }
- }
-
- @Override
- public String[] getColumnNames() {
- synchronized (mLock) {
- throwIfCursorIsClosed();
-
- return mCursor.getColumnNames();
- }
- }
-
- @Override
public void deactivate() {
synchronized (mLock) {
if (mCursor != null) {
@@ -237,15 +237,6 @@
}
}
- @Override
- public boolean getWantsAllOnMoveCalls() {
- synchronized (mLock) {
- throwIfCursorIsClosed();
-
- return mCursor.getWantsAllOnMoveCalls();
- }
- }
-
/**
* Create a ContentObserver from the observer and register it as an observer on the
* underlying cursor.
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 99d260e..40a54cf 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -1386,4 +1386,18 @@
System.arraycopy(newValues, 0, result, originalValues.length, newValues.length);
return result;
}
+
+ /**
+ * Returns column index of "_id" column, or -1 if not found.
+ * @hide
+ */
+ public static int findRowIdColumnIndex(String[] columnNames) {
+ int length = columnNames.length;
+ for (int i = 0; i < length; i++) {
+ if (columnNames[i].equals("_id")) {
+ return i;
+ }
+ }
+ return -1;
+ }
}
diff --git a/core/java/android/database/IBulkCursor.java b/core/java/android/database/IBulkCursor.java
index 0f4500a..b551116 100644
--- a/core/java/android/database/IBulkCursor.java
+++ b/core/java/android/database/IBulkCursor.java
@@ -43,29 +43,12 @@
*/
public void onMove(int position) throws RemoteException;
- /**
- * Returns the number of rows in the cursor.
- *
- * @return the number of rows in the cursor.
- */
- public int count() throws RemoteException;
-
- /**
- * Returns a string array holding the names of all of the columns in the
- * cursor in the order in which they were listed in the result.
- *
- * @return the names of the columns returned in this query.
- */
- public String[] getColumnNames() throws RemoteException;
-
public void deactivate() throws RemoteException;
public void close() throws RemoteException;
public int requery(IContentObserver observer) throws RemoteException;
- boolean getWantsAllOnMoveCalls() throws RemoteException;
-
Bundle getExtras() throws RemoteException;
Bundle respond(Bundle extras) throws RemoteException;
@@ -74,13 +57,10 @@
static final String descriptor = "android.content.IBulkCursor";
static final int GET_CURSOR_WINDOW_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
- static final int COUNT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 1;
- static final int GET_COLUMN_NAMES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 2;
- static final int DEACTIVATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 5;
- static final int REQUERY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 6;
- static final int ON_MOVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 7;
- static final int WANTS_ON_MOVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 8;
- static final int GET_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 9;
- static final int RESPOND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 10;
- static final int CLOSE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 11;
+ static final int DEACTIVATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 1;
+ static final int REQUERY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 2;
+ static final int ON_MOVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 3;
+ static final int GET_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 4;
+ static final int RESPOND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 5;
+ static final int CLOSE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 6;
}
diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java
index 82bb23e..b29897e 100644
--- a/core/java/android/database/sqlite/SQLiteCursor.java
+++ b/core/java/android/database/sqlite/SQLiteCursor.java
@@ -105,12 +105,7 @@
mQuery = query;
mColumns = query.getColumnNames();
- for (int i = 0; i < mColumns.length; i++) {
- // Make note of the row ID column index for quick access to it
- if ("_id".equals(mColumns[i])) {
- mRowIdColumnIndex = i;
- }
- }
+ mRowIdColumnIndex = DatabaseUtils.findRowIdColumnIndex(mColumns);
}
/**
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index f09e29d..dbcea71 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -742,7 +742,8 @@
url = url.replaceFirst(ANDROID_ASSET, "");
try {
AssetManager assets = mContext.getAssets();
- return assets.open(url, AssetManager.ACCESS_STREAMING);
+ Uri uri = Uri.parse(url);
+ return assets.open(uri.getPath(), AssetManager.ACCESS_STREAMING);
} catch (IOException e) {
return null;
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 5774440..9e07151 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -88,6 +88,7 @@
ViewTreeObserver.OnTouchModeChangeListener,
RemoteViewsAdapter.RemoteAdapterConnectionCallback {
+ @SuppressWarnings("UnusedDeclaration")
private static final String TAG = "AbsListView";
/**
@@ -2429,7 +2430,7 @@
final ViewTreeObserver treeObserver = getViewTreeObserver();
treeObserver.removeOnTouchModeChangeListener(this);
if (mTextFilterEnabled && mPopup != null) {
- treeObserver.removeGlobalOnLayoutListener(this);
+ treeObserver.removeOnGlobalLayoutListener(this);
mGlobalLayoutListenerAddedFilter = false;
}
@@ -2943,11 +2944,23 @@
mDirection = 0; // Reset when entering overscroll.
mTouchMode = TOUCH_MODE_OVERSCROLL;
if (rawDeltaY > 0) {
+ if (!mEdgeGlowTop.isIdle()) {
+ invalidate(mEdgeGlowTop.getBounds());
+ } else {
+ invalidate();
+ }
+
mEdgeGlowTop.onPull((float) overscroll / getHeight());
if (!mEdgeGlowBottom.isFinished()) {
mEdgeGlowBottom.onRelease();
}
} else if (rawDeltaY < 0) {
+ if (!mEdgeGlowBottom.isIdle()) {
+ invalidate(mEdgeGlowBottom.getBounds());
+ } else {
+ invalidate();
+ }
+
mEdgeGlowBottom.onPull((float) overscroll / getHeight());
if (!mEdgeGlowTop.isFinished()) {
mEdgeGlowTop.onRelease();
@@ -2956,7 +2969,6 @@
}
}
mMotionY = y;
- invalidate();
}
mLastY = y;
}
@@ -2990,26 +3002,26 @@
if (!mEdgeGlowBottom.isFinished()) {
mEdgeGlowBottom.onRelease();
}
+ invalidate(mEdgeGlowTop.getBounds());
} else if (rawDeltaY < 0) {
mEdgeGlowBottom.onPull((float) overScrollDistance / getHeight());
if (!mEdgeGlowTop.isFinished()) {
mEdgeGlowTop.onRelease();
}
+ invalidate(mEdgeGlowBottom.getBounds());
}
- invalidate();
}
}
if (incrementalDeltaY != 0) {
// Coming back to 'real' list scrolling
- mScrollY = 0;
- invalidateParentIfNeeded();
-
- // No need to do all this work if we're not going to move anyway
- if (incrementalDeltaY != 0) {
- trackMotionScroll(incrementalDeltaY, incrementalDeltaY);
+ if (mScrollY != 0) {
+ mScrollY = 0;
+ invalidateParentIfNeeded();
}
+ trackMotionScroll(incrementalDeltaY, incrementalDeltaY);
+
mTouchMode = TOUCH_MODE_SCROLL;
// We did not scroll the full amount. Treat this essentially like the
@@ -3468,11 +3480,12 @@
final int rightPadding = mListPadding.right + mGlowPaddingRight;
final int width = getWidth() - leftPadding - rightPadding;
- canvas.translate(leftPadding,
- Math.min(0, scrollY + mFirstPositionDistanceGuess));
+ int edgeY = Math.min(0, scrollY + mFirstPositionDistanceGuess);
+ canvas.translate(leftPadding, edgeY);
mEdgeGlowTop.setSize(width, getHeight());
if (mEdgeGlowTop.draw(canvas)) {
- invalidate();
+ mEdgeGlowTop.setPosition(leftPadding, edgeY);
+ invalidate(mEdgeGlowTop.getBounds());
}
canvas.restoreToCount(restoreCount);
}
@@ -3483,12 +3496,15 @@
final int width = getWidth() - leftPadding - rightPadding;
final int height = getHeight();
- canvas.translate(-width + leftPadding,
- Math.max(height, scrollY + mLastPositionDistanceGuess));
+ int edgeX = -width + leftPadding;
+ int edgeY = Math.max(height, scrollY + mLastPositionDistanceGuess);
+ canvas.translate(edgeX, edgeY);
canvas.rotate(180, width, 0);
mEdgeGlowBottom.setSize(width, height);
if (mEdgeGlowBottom.draw(canvas)) {
- invalidate();
+ // Account for the rotation
+ mEdgeGlowBottom.setPosition(edgeX + width, edgeY - mEdgeGlowBottom.getHeight());
+ invalidate(mEdgeGlowBottom.getBounds());
}
canvas.restoreToCount(restoreCount);
}
@@ -3874,7 +3890,8 @@
}
// Don't stop just because delta is zero (it could have been rounded)
- final boolean atEnd = trackMotionScroll(delta, delta) && (delta != 0);
+ final boolean atEdge = trackMotionScroll(delta, delta);
+ final boolean atEnd = atEdge && (delta != 0);
if (atEnd) {
if (motionView != null) {
// Tweak the scroll for how far we overshot
@@ -3889,7 +3906,7 @@
}
if (more && !atEnd) {
- invalidate();
+ if (atEdge) invalidate();
mLastFlingY = y;
post(this);
} else {
@@ -4431,7 +4448,7 @@
}
private void createScrollingCache() {
- if (mScrollingCacheEnabled && !mCachingStarted) {
+ if (mScrollingCacheEnabled && !mCachingStarted && !isHardwareAccelerated()) {
setChildrenDrawnWithCacheEnabled(true);
setChildrenDrawingCacheEnabled(true);
mCachingStarted = mCachingActive = true;
@@ -4439,23 +4456,25 @@
}
private void clearScrollingCache() {
- if (mClearScrollingCache == null) {
- mClearScrollingCache = new Runnable() {
- public void run() {
- if (mCachingStarted) {
- mCachingStarted = mCachingActive = false;
- setChildrenDrawnWithCacheEnabled(false);
- if ((mPersistentDrawingCache & PERSISTENT_SCROLLING_CACHE) == 0) {
- setChildrenDrawingCacheEnabled(false);
- }
- if (!isAlwaysDrawnWithCacheEnabled()) {
- invalidate();
+ if (!isHardwareAccelerated()) {
+ if (mClearScrollingCache == null) {
+ mClearScrollingCache = new Runnable() {
+ public void run() {
+ if (mCachingStarted) {
+ mCachingStarted = mCachingActive = false;
+ setChildrenDrawnWithCacheEnabled(false);
+ if ((mPersistentDrawingCache & PERSISTENT_SCROLLING_CACHE) == 0) {
+ setChildrenDrawingCacheEnabled(false);
+ }
+ if (!isAlwaysDrawnWithCacheEnabled()) {
+ invalidate();
+ }
}
}
- }
- };
+ };
+ }
+ post(mClearScrollingCache);
}
- post(mClearScrollingCache);
}
/**
@@ -4599,14 +4618,18 @@
mRecycler.removeSkippedScrap();
}
+ // invalidate before moving the children to avoid unnecessary invalidate
+ // calls to bubble up from the children all the way to the top
+ if (!awakenScrollBars()) {
+ invalidate();
+ }
+
offsetChildrenTopAndBottom(incrementalDeltaY);
if (down) {
mFirstPosition += count;
}
- invalidate();
-
final int absIncrementalDeltaY = Math.abs(incrementalDeltaY);
if (spaceAbove < absIncrementalDeltaY || spaceBelow < absIncrementalDeltaY) {
fillGap(down);
@@ -4629,7 +4652,6 @@
mBlockLayoutRequests = false;
invokeOnItemScrollListener();
- awakenScrollBars();
return false;
}
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index 83aa8ba..c1f31bb 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.graphics.Rect;
import com.android.internal.R;
import android.content.Context;
@@ -45,6 +46,7 @@
* {@link #draw(Canvas)} method.</p>
*/
public class EdgeEffect {
+ @SuppressWarnings("UnusedDeclaration")
private static final String TAG = "EdgeEffect";
// Time it will take the effect to fully recede in ms
@@ -57,10 +59,7 @@
private static final int PULL_DECAY_TIME = 1000;
private static final float MAX_ALPHA = 1.f;
- private static final float HELD_EDGE_ALPHA = 0.7f;
private static final float HELD_EDGE_SCALE_Y = 0.5f;
- private static final float HELD_GLOW_ALPHA = 0.5f;
- private static final float HELD_GLOW_SCALE_Y = 0.5f;
private static final float MAX_GLOW_HEIGHT = 4.f;
@@ -76,7 +75,9 @@
private final Drawable mGlow;
private int mWidth;
private int mHeight;
- private final int MIN_WIDTH = 300;
+ private int mX;
+ private int mY;
+ private static final int MIN_WIDTH = 300;
private final int mMinWidth;
private float mEdgeAlpha;
@@ -119,6 +120,8 @@
private int mState = STATE_IDLE;
private float mPullDistance;
+
+ private final Rect mBounds = new Rect();
/**
* Construct a new EdgeEffect with a theme appropriate for the provided context.
@@ -145,6 +148,29 @@
}
/**
+ * Set the position of this edge effect in pixels. This position is
+ * only used by {@link #getBounds()}.
+ *
+ * @param x The position of the edge effect on the X axis
+ * @param y The position of the edge effect on the Y axis
+ */
+ void setPosition(int x, int y) {
+ mX = x;
+ mY = y;
+ }
+
+ boolean isIdle() {
+ return mState == STATE_IDLE;
+ }
+
+ /**
+ * Returns the height of the effect itself.
+ */
+ int getHeight() {
+ return Math.max(mGlow.getBounds().height(), mEdge.getBounds().height());
+ }
+
+ /**
* Reports if this EdgeEffect's animation is finished. If this method returns false
* after a call to {@link #draw(Canvas)} the host widget should schedule another
* drawing pass to continue the animation.
@@ -301,7 +327,6 @@
update();
final int edgeHeight = mEdge.getIntrinsicHeight();
- final int edgeWidth = mEdge.getIntrinsicWidth();
final int glowHeight = mGlow.getIntrinsicHeight();
final int glowWidth = mGlow.getIntrinsicWidth();
@@ -334,9 +359,23 @@
}
mEdge.draw(canvas);
+ if (mState == STATE_RECEDE && glowBottom == 0 && edgeBottom == 0) {
+ mState = STATE_IDLE;
+ }
+
return mState != STATE_IDLE;
}
+ /**
+ * Returns the bounds of the edge effect.
+ */
+ public Rect getBounds() {
+ mBounds.set(mGlow.getBounds());
+ mBounds.union(mEdge.getBounds());
+ mBounds.offset(mX, mY);
+ return mBounds;
+ }
+
private void update() {
final long time = AnimationUtils.currentAnimationTimeMillis();
final float t = Math.min((time - mStartTime) / mDuration, 1.f);
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index fc08cc5..60dd55c 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -29,8 +29,8 @@
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
-
import com.android.internal.R;
+import android.widget.RemoteViews.RemoteView;
import java.lang.reflect.Array;
import java.util.ArrayList;
@@ -146,6 +146,7 @@
* @attr ref android.R.styleable#GridLayout_rowOrderPreserved
* @attr ref android.R.styleable#GridLayout_columnOrderPreserved
*/
+@RemoteView
public class GridLayout extends ViewGroup {
// Public constants
@@ -234,7 +235,6 @@
final Axis horizontalAxis = new Axis(true);
final Axis verticalAxis = new Axis(false);
- boolean layoutParamsValid = false;
int orientation = DEFAULT_ORIENTATION;
boolean useDefaultMargins = DEFAULT_USE_DEFAULT_MARGINS;
int alignmentMode = DEFAULT_ALIGNMENT_MODE;
@@ -713,12 +713,10 @@
minor = minor + minorSpan;
}
- lastLayoutParamsHashCode = computeLayoutParamsHashCode();
- invalidateStructure();
}
private void invalidateStructure() {
- layoutParamsValid = false;
+ lastLayoutParamsHashCode = UNINITIALIZED_HASH;
horizontalAxis.invalidateStructure();
verticalAxis.invalidateStructure();
// This can end up being done twice. Better twice than not at all.
@@ -742,10 +740,6 @@
}
final LayoutParams getLayoutParams(View c) {
- if (!layoutParamsValid) {
- validateLayoutParams();
- layoutParamsValid = true;
- }
return (LayoutParams) c.getLayoutParams();
}
@@ -874,20 +868,22 @@
return result;
}
- private void checkForLayoutParamsModification() {
- int layoutParamsHashCode = computeLayoutParamsHashCode();
- if (lastLayoutParamsHashCode != UNINITIALIZED_HASH &&
- lastLayoutParamsHashCode != layoutParamsHashCode) {
- invalidateStructure();
+ private void consistencyCheck() {
+ if (lastLayoutParamsHashCode == UNINITIALIZED_HASH) {
+ validateLayoutParams();
+ lastLayoutParamsHashCode = computeLayoutParamsHashCode();
+ } else if (lastLayoutParamsHashCode != computeLayoutParamsHashCode()) {
Log.w(TAG, "The fields of some layout parameters were modified in between layout " +
"operations. Check the javadoc for GridLayout.LayoutParams#rowSpec.");
+ invalidateStructure();
+ consistencyCheck();
}
}
// Measurement
private void measureChildWithMargins2(View child, int parentWidthSpec, int parentHeightSpec,
- int childWidth, int childHeight) {
+ int childWidth, int childHeight) {
int childWidthSpec = getChildMeasureSpec(parentWidthSpec,
mPaddingLeft + mPaddingRight + getTotalMargin(child, true), childWidth);
int childHeightSpec = getChildMeasureSpec(parentHeightSpec,
@@ -923,7 +919,7 @@
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
- checkForLayoutParamsModification();
+ consistencyCheck();
/** If we have been called by {@link View#measure(int, int)}, one of width or height
* is likely to have changed. We must invalidate if so. */
@@ -993,7 +989,7 @@
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- checkForLayoutParamsModification();
+ consistencyCheck();
int targetWidth = right - left;
int targetHeight = bottom - top;
@@ -1250,7 +1246,7 @@
}
private void include(List<Arc> arcs, Interval key, MutableInt size,
- boolean ignoreIfAlreadyPresent) {
+ boolean ignoreIfAlreadyPresent) {
/*
Remove self referential links.
These appear:
@@ -1429,8 +1425,8 @@
int dst = arc.span.max;
int value = arc.value.value;
result.append((src < dst) ?
- var + dst + " - " + var + src + " > " + value :
- var + src + " - " + var + dst + " < " + -value);
+ var + dst + "-" + var + src + ">=" + value :
+ var + src + "-" + var + dst + "<=" + -value);
}
return result.toString();
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index 662dd13af..a68ab4e 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -120,6 +120,9 @@
MEDIA_INFO_NOT_SEEKABLE = 801,
// New media metadata is available.
MEDIA_INFO_METADATA_UPDATE = 802,
+
+ //9xx
+ MEDIA_INFO_TIMED_TEXT_ERROR = 900,
};
@@ -140,9 +143,6 @@
// The same enum space is used for both set and get, in case there are future keys that
// can be both set and get. But as of now, all parameters are either set only or get only.
enum media_parameter_keys {
- KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX = 1000, // set only
- KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE = 1001, // set only
-
// Streaming/buffering parameters
KEY_PARAMETER_CACHE_STAT_COLLECT_FREQ_MS = 1100, // set only
@@ -155,6 +155,23 @@
KEY_PARAMETER_PLAYBACK_RATE_PERMILLE = 1300, // set only
};
+// Keep INVOKE_ID_* in sync with MediaPlayer.java.
+enum media_player_invoke_ids {
+ INVOKE_ID_GET_TRACK_INFO = 1,
+ INVOKE_ID_ADD_EXTERNAL_SOURCE = 2,
+ INVOKE_ID_ADD_EXTERNAL_SOURCE_FD = 3,
+ INVOKE_ID_SELECT_TRACK = 4,
+ INVOKE_ID_UNSELECT_TRACK = 5,
+};
+
+// Keep MEDIA_TRACK_TYPE_* in sync with MediaPlayer.java.
+enum media_track_type {
+ MEDIA_TRACK_TYPE_UNKNOWN = 0,
+ MEDIA_TRACK_TYPE_VIDEO = 1,
+ MEDIA_TRACK_TYPE_AUDIO = 2,
+ MEDIA_TRACK_TYPE_TIMEDTEXT = 3,
+};
+
// ----------------------------------------------------------------------------
// ref-counted object for callbacks
class MediaPlayerListener: virtual public RefBase
diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h
index 2eb259e..457d5d7 100644
--- a/include/media/stagefright/MediaDefs.h
+++ b/include/media/stagefright/MediaDefs.h
@@ -54,6 +54,7 @@
extern const char *MEDIA_MIMETYPE_CONTAINER_WVM;
extern const char *MEDIA_MIMETYPE_TEXT_3GPP;
+extern const char *MEDIA_MIMETYPE_TEXT_SUBRIP;
} // namespace android
diff --git a/include/media/stagefright/timedtext/TimedTextDriver.h b/include/media/stagefright/timedtext/TimedTextDriver.h
index efedb6e..b9752df 100644
--- a/include/media/stagefright/timedtext/TimedTextDriver.h
+++ b/include/media/stagefright/timedtext/TimedTextDriver.h
@@ -37,26 +37,26 @@
~TimedTextDriver();
- // TODO: pause-resume pair seems equivalent to stop-start pair.
- // Check if it is replaceable with stop-start.
status_t start();
- status_t stop();
status_t pause();
- status_t resume();
+ status_t selectTrack(int32_t index);
+ status_t unselectTrack(int32_t index);
status_t seekToAsync(int64_t timeUs);
status_t addInBandTextSource(const sp<MediaSource>& source);
- status_t addOutOfBandTextSource(const Parcel &request);
+ status_t addOutOfBandTextSource(const char *uri, const char *mimeType);
+ // Caller owns the file desriptor and caller is responsible for closing it.
+ status_t addOutOfBandTextSource(
+ int fd, off64_t offset, size_t length, const char *mimeType);
- status_t setTimedTextTrackIndex(int32_t index);
+ void getTrackInfo(Parcel *parcel);
private:
Mutex mLock;
enum State {
UNINITIALIZED,
- STOPPED,
PLAYING,
PAUSED,
};
@@ -67,11 +67,11 @@
// Variables to be guarded by mLock.
State mState;
- Vector<sp<TimedTextSource> > mTextInBandVector;
- Vector<sp<TimedTextSource> > mTextOutOfBandVector;
+ int32_t mCurrentTrackIndex;
+ Vector<sp<TimedTextSource> > mTextSourceVector;
// -- End of variables to be guarded by mLock
- status_t setTimedTextTrackIndex_l(int32_t index);
+ status_t selectTrack_l(int32_t index);
DISALLOW_EVIL_CONSTRUCTORS(TimedTextDriver);
};
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index f5fa877..d92180d 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -24,6 +24,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
+import android.os.Parcelable;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.util.Log;
@@ -455,6 +456,22 @@
* <td>Successful invoke of this method in a valid state transfers the
* object to the <em>Stopped</em> state. Calling this method in an
* invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
+ * <tr><td>getTrackInfo </p></td>
+ * <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
+ * <td>{Idle, Initialized, Error}</p></td>
+ * <td>Successful invoke of this method does not change the state.</p></td></tr>
+ * <tr><td>addExternalSource </p></td>
+ * <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
+ * <td>{Idle, Initialized, Error}</p></td>
+ * <td>Successful invoke of this method does not change the state.</p></td></tr>
+ * <tr><td>selectTrack </p></td>
+ * <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
+ * <td>{Idle, Initialized, Error}</p></td>
+ * <td>Successful invoke of this method does not change the state.</p></td></tr>
+ * <tr><td>disableTrack </p></td>
+ * <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
+ * <td>{Idle, Initialized, Error}</p></td>
+ * <td>Successful invoke of this method does not change the state.</p></td></tr>
*
* </table>
*
@@ -572,6 +589,15 @@
*/
private native void _setVideoSurface(Surface surface);
+ /* Do not change these values (starting with INVOKE_ID) without updating
+ * their counterparts in include/media/mediaplayer.h!
+ */
+ private static final int INVOKE_ID_GET_TRACK_INFO = 1;
+ private static final int INVOKE_ID_ADD_EXTERNAL_SOURCE = 2;
+ private static final int INVOKE_ID_ADD_EXTERNAL_SOURCE_FD = 3;
+ private static final int INVOKE_ID_SELECT_TRACK = 4;
+ private static final int INVOKE_ID_UNSELECT_TRACK = 5;
+
/**
* Create a request parcel which can be routed to the native media
* player using {@link #invoke(Parcel, Parcel)}. The Parcel
@@ -1312,23 +1338,6 @@
/* Do not change these values (starting with KEY_PARAMETER) without updating
* their counterparts in include/media/mediaplayer.h!
*/
- /*
- * Key used in setParameter method.
- * Indicates the index of the timed text track to be enabled/disabled.
- * The index includes both the in-band and out-of-band timed text.
- * The index should start from in-band text if any. Application can retrieve the number
- * of in-band text tracks by using MediaMetadataRetriever::extractMetadata().
- * Note it might take a few hundred ms to scan an out-of-band text file
- * before displaying it.
- */
- private static final int KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX = 1000;
- /*
- * Key used in setParameter method.
- * Used to add out-of-band timed text source path.
- * Application can add multiple text sources by calling setParameter() with
- * KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE multiple times.
- */
- private static final int KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE = 1001;
// There are currently no defined keys usable from Java with get*Parameter.
// But if any keys are defined, the order must be kept in sync with include/media/mediaplayer.h.
@@ -1373,7 +1382,7 @@
return ret;
}
- /**
+ /*
* Gets the value of the parameter indicated by key.
* @param key key indicates the parameter to get.
* @param reply value of the parameter to get.
@@ -1435,7 +1444,7 @@
*/
public native void setAuxEffectSendLevel(float level);
- /**
+ /*
* @param request Parcel destinated to the media player. The
* Interface token must be set to the IMediaPlayer
* one to be routed correctly through the system.
@@ -1445,7 +1454,7 @@
private native final int native_invoke(Parcel request, Parcel reply);
- /**
+ /*
* @param update_only If true fetch only the set of metadata that have
* changed since the last invocation of getMetadata.
* The set is built using the unfiltered
@@ -1462,7 +1471,7 @@
boolean apply_filter,
Parcel reply);
- /**
+ /*
* @param request Parcel with the 2 serialized lists of allowed
* metadata types followed by the one to be
* dropped. Each list starts with an integer
@@ -1476,33 +1485,289 @@
private native final void native_finalize();
/**
- * @param index The index of the text track to be turned on.
- * @return true if the text track is enabled successfully.
+ * Class for MediaPlayer to return each audio/video/subtitle track's metadata.
+ *
+ * {@see #getTrackInfo()}.
* {@hide}
*/
- public boolean enableTimedTextTrackIndex(int index) {
- if (index < 0) {
- return false;
+ static public class TrackInfo implements Parcelable {
+ /**
+ * Gets the track type.
+ * @return TrackType which indicates if the track is video, audio, timed text.
+ */
+ public int getTrackType() {
+ return mTrackType;
}
- return setParameter(KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX, index);
+
+ /**
+ * Gets the language code of the track.
+ * @return a language code in either way of ISO-639-1 or ISO-639-2.
+ * When the language is unknown or could not be determined,
+ * ISO-639-2 language code, "und", is returned.
+ */
+ public String getLanguage() {
+ return mLanguage;
+ }
+
+ public static final int MEDIA_TRACK_TYPE_UNKNOWN = 0;
+ public static final int MEDIA_TRACK_TYPE_VIDEO = 1;
+ public static final int MEDIA_TRACK_TYPE_AUDIO = 2;
+ public static final int MEDIA_TRACK_TYPE_TIMEDTEXT = 3;
+
+ final int mTrackType;
+ final String mLanguage;
+
+ TrackInfo(Parcel in) {
+ mTrackType = in.readInt();
+ mLanguage = in.readString();
+ }
+
+ /*
+ * No special parcel contents. Keep it as hide.
+ * {@hide}
+ */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /*
+ * {@hide}
+ */
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mTrackType);
+ dest.writeString(mLanguage);
+ }
+
+ /**
+ * Used to read a TrackInfo from a Parcel.
+ */
+ static final Parcelable.Creator<TrackInfo> CREATOR
+ = new Parcelable.Creator<TrackInfo>() {
+ @Override
+ public TrackInfo createFromParcel(Parcel in) {
+ return new TrackInfo(in);
+ }
+
+ @Override
+ public TrackInfo[] newArray(int size) {
+ return new TrackInfo[size];
+ }
+ };
+
+ };
+
+ /**
+ * Returns an array of track information.
+ *
+ * @return Array of track info. null if an error occured.
+ * {@hide}
+ */
+ // FIXME: It returns timed text tracks' information for now. Other types of tracks will be
+ // supported in future.
+ public TrackInfo[] getTrackInfo() {
+ Parcel request = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ request.writeInterfaceToken(IMEDIA_PLAYER);
+ request.writeInt(INVOKE_ID_GET_TRACK_INFO);
+ invoke(request, reply);
+ TrackInfo trackInfo[] = reply.createTypedArray(TrackInfo.CREATOR);
+ return trackInfo;
+ }
+
+ /*
+ * A helper function to check if the mime type is supported by media framework.
+ */
+ private boolean availableMimeTypeForExternalSource(String mimeType) {
+ if (mimeType == MEDIA_MIMETYPE_TEXT_SUBRIP) {
+ return true;
+ }
+ return false;
+ }
+
+ /* TODO: Limit the total number of external timed text source to a reasonable number.
+ */
+ /**
+ * Adds an external source file.
+ *
+ * Currently supported format is SubRip with the file extension .srt, case insensitive.
+ * Note that a single external source may contain multiple tracks in it.
+ * One can find the total number of available tracks using {@link #getTrackInfo()} to see what
+ * additional tracks become available after this method call.
+ *
+ * @param path The file path of external source file.
+ * {@hide}
+ */
+ // FIXME: define error codes and throws exceptions according to the error codes.
+ // (IllegalStateException, IOException).
+ public void addExternalSource(String path, String mimeType)
+ throws IllegalArgumentException {
+ if (!availableMimeTypeForExternalSource(mimeType)) {
+ throw new IllegalArgumentException("Illegal mimeType for external source: " + mimeType);
+ }
+
+ Parcel request = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ request.writeInterfaceToken(IMEDIA_PLAYER);
+ request.writeInt(INVOKE_ID_ADD_EXTERNAL_SOURCE);
+ request.writeString(path);
+ request.writeString(mimeType);
+ invoke(request, reply);
}
/**
- * Enables the first timed text track if any.
- * @return true if the text track is enabled successfully
+ * Adds an external source file (Uri).
+ *
+ * Currently supported format is SubRip with the file extension .srt, case insensitive.
+ * Note that a single external source may contain multiple tracks in it.
+ * One can find the total number of available tracks using {@link #getTrackInfo()} to see what
+ * additional tracks become available after this method call.
+ *
+ * @param context the Context to use when resolving the Uri
+ * @param uri the Content URI of the data you want to play
* {@hide}
*/
- public boolean enableTimedText() {
- return enableTimedTextTrackIndex(0);
+ // FIXME: define error codes and throws exceptions according to the error codes.
+ // (IllegalStateException, IOException).
+ public void addExternalSource(Context context, Uri uri, String mimeType)
+ throws IOException, IllegalArgumentException {
+ String scheme = uri.getScheme();
+ if(scheme == null || scheme.equals("file")) {
+ addExternalSource(uri.getPath(), mimeType);
+ return;
+ }
+
+ AssetFileDescriptor fd = null;
+ try {
+ ContentResolver resolver = context.getContentResolver();
+ fd = resolver.openAssetFileDescriptor(uri, "r");
+ if (fd == null) {
+ return;
+ }
+ addExternalSource(fd.getFileDescriptor(), mimeType);
+ return;
+ } catch (SecurityException ex) {
+ } catch (IOException ex) {
+ } finally {
+ if (fd != null) {
+ fd.close();
+ }
+ }
+
+ // TODO: try server side.
+ }
+
+ /* Do not change these values without updating their counterparts
+ * in include/media/stagefright/MediaDefs.h and media/libstagefright/MediaDefs.cpp!
+ */
+ /**
+ * MIME type for SubRip (SRT) container. Used in {@link #addExternalSource()} APIs.
+ * {@hide}
+ */
+ public static final String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
+
+ /**
+ * Adds an external source file (FileDescriptor).
+ * It is the caller's responsibility to close the file descriptor.
+ * It is safe to do so as soon as this call returns.
+ *
+ * Currently supported format is SubRip with the file extension .srt, case insensitive.
+ * Note that a single external source may contain multiple tracks in it.
+ * One can find the total number of available tracks using {@link #getTrackInfo()} to see what
+ * additional tracks become available after this method call.
+ *
+ * @param fd the FileDescriptor for the file you want to play
+ * @param mimeType A MIME type for the content. It can be null.
+ * <ul>
+ * <li>{@link #MEDIA_MIMETYPE_TEXT_SUBRIP}
+ * </ul>
+ * {@hide}
+ */
+ // FIXME: define error codes and throws exceptions according to the error codes.
+ // (IllegalStateException, IOException).
+ public void addExternalSource(FileDescriptor fd, String mimeType)
+ throws IllegalArgumentException {
+ // intentionally less than LONG_MAX
+ addExternalSource(fd, 0, 0x7ffffffffffffffL, mimeType);
}
/**
- * Disables timed text display.
- * @return true if the text track is disabled successfully.
+ * Adds an external timed text file (FileDescriptor).
+ * It is the caller's responsibility to close the file descriptor.
+ * It is safe to do so as soon as this call returns.
+ *
+ * Currently supported format is SubRip with the file extension .srt, case insensitive.
+ * Note that a single external source may contain multiple tracks in it.
+ * One can find the total number of available tracks using {@link #getTrackInfo()} to see what
+ * additional tracks become available after this method call.
+ *
+ * @param fd the FileDescriptor for the file you want to play
+ * @param offset the offset into the file where the data to be played starts, in bytes
+ * @param length the length in bytes of the data to be played
+ * @param mimeType A MIME type for the content. It can be null.
* {@hide}
*/
- public boolean disableTimedText() {
- return setParameter(KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX, -1);
+ // FIXME: define error codes and throws exceptions according to the error codes.
+ // (IllegalStateException, IOException).
+ public void addExternalSource(FileDescriptor fd, long offset, long length, String mimeType)
+ throws IllegalArgumentException {
+ if (!availableMimeTypeForExternalSource(mimeType)) {
+ throw new IllegalArgumentException("Illegal mimeType for external source: " + mimeType);
+ }
+ Parcel request = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ request.writeInterfaceToken(IMEDIA_PLAYER);
+ request.writeInt(INVOKE_ID_ADD_EXTERNAL_SOURCE_FD);
+ request.writeFileDescriptor(fd);
+ request.writeLong(offset);
+ request.writeLong(length);
+ request.writeString(mimeType);
+ invoke(request, reply);
+ }
+
+ /**
+ * Selects a track.
+ * <p>
+ * If a MediaPlayer is in invalid state, it throws exception.
+ * If a MediaPlayer is in Started state, the selected track will be presented immediately.
+ * If a MediaPlayer is not in Started state, it just marks the track to be played.
+ * </p>
+ * <p>
+ * In any valid state, if it is called multiple times on the same type of track (ie. Video,
+ * Audio, Timed Text), the most recent one will be chosen.
+ * </p>
+ * <p>
+ * The first audio and video tracks will be selected by default, even though this function is not
+ * called. However, no timed text track will be selected until this function is called.
+ * </p>
+ * {@hide}
+ */
+ // FIXME: define error codes and throws exceptions according to the error codes.
+ // (IllegalStateException, IOException, IllegalArgumentException).
+ public void selectTrack(int index) {
+ Parcel request = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ request.writeInterfaceToken(IMEDIA_PLAYER);
+ request.writeInt(INVOKE_ID_SELECT_TRACK);
+ request.writeInt(index);
+ invoke(request, reply);
+ }
+
+ /**
+ * Unselect a track.
+ * If the track identified by index has not been selected before, it throws an exception.
+ * {@hide}
+ */
+ // FIXME: define error codes and throws exceptions according to the error codes.
+ // (IllegalStateException, IOException, IllegalArgumentException).
+ public void unselectTrack(int index) {
+ Parcel request = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ request.writeInterfaceToken(IMEDIA_PLAYER);
+ request.writeInt(INVOKE_ID_UNSELECT_TRACK);
+ request.writeInt(index);
+ invoke(request, reply);
}
/**
@@ -1641,14 +1906,14 @@
// No real default action so far.
return;
case MEDIA_TIMED_TEXT:
- if (mOnTimedTextListener != null) {
- if (msg.obj == null) {
- mOnTimedTextListener.onTimedText(mMediaPlayer, null);
- } else {
- if (msg.obj instanceof byte[]) {
- TimedText text = new TimedText((byte[])(msg.obj));
- mOnTimedTextListener.onTimedText(mMediaPlayer, text);
- }
+ if (mOnTimedTextListener == null)
+ return;
+ if (msg.obj == null) {
+ mOnTimedTextListener.onTimedText(mMediaPlayer, null);
+ } else {
+ if (msg.obj instanceof byte[]) {
+ TimedText text = new TimedText((byte[])(msg.obj));
+ mOnTimedTextListener.onTimedText(mMediaPlayer, text);
}
}
return;
@@ -1663,7 +1928,7 @@
}
}
- /**
+ /*
* Called from native code when an interesting event happens. This method
* just uses the EventHandler system to post the event back to the main app thread.
* We use a weak reference to the original MediaPlayer object so that the native
@@ -1977,6 +2242,13 @@
*/
public static final int MEDIA_INFO_METADATA_UPDATE = 802;
+ /** Failed to handle timed text track properly.
+ * @see android.media.MediaPlayer.OnInfoListener
+ *
+ * {@hide}
+ */
+ public static final int MEDIA_INFO_TIMED_TEXT_ERROR = 900;
+
/**
* Interface definition of a callback to be invoked to communicate some
* info and/or warning about the media or its playback.
diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp
index 052ebf0..619c149 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.cpp
+++ b/media/libmediaplayerservice/StagefrightPlayer.cpp
@@ -166,7 +166,8 @@
}
status_t StagefrightPlayer::invoke(const Parcel &request, Parcel *reply) {
- return INVALID_OPERATION;
+ ALOGV("invoke()");
+ return mPlayer->invoke(request, reply);
}
void StagefrightPlayer::setAudioSink(const sp<AudioSink> &audioSink) {
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 9e00bb3..b4cb1ab 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -1114,7 +1114,7 @@
modifyFlags(AUDIO_RUNNING, CLEAR);
}
- if (mFlags & TEXTPLAYER_STARTED) {
+ if (mFlags & TEXTPLAYER_INITIALIZED) {
mTextDriver->pause();
modifyFlags(TEXT_RUNNING, CLEAR);
}
@@ -1268,32 +1268,6 @@
return OK;
}
-status_t AwesomePlayer::setTimedTextTrackIndex(int32_t index) {
- if (mTextDriver != NULL) {
- if (index >= 0) { // to turn on a text track
- status_t err = mTextDriver->setTimedTextTrackIndex(index);
- if (err != OK) {
- return err;
- }
-
- modifyFlags(TEXT_RUNNING, SET);
- modifyFlags(TEXTPLAYER_STARTED, SET);
- return OK;
- } else { // to turn off the text track display
- if (mFlags & TEXT_RUNNING) {
- modifyFlags(TEXT_RUNNING, CLEAR);
- }
- if (mFlags & TEXTPLAYER_STARTED) {
- modifyFlags(TEXTPLAYER_STARTED, CLEAR);
- }
-
- return mTextDriver->setTimedTextTrackIndex(index);
- }
- } else {
- return INVALID_OPERATION;
- }
-}
-
status_t AwesomePlayer::seekTo_l(int64_t timeUs) {
if (mFlags & CACHE_UNDERRUN) {
modifyFlags(CACHE_UNDERRUN, CLEAR);
@@ -1315,7 +1289,7 @@
seekAudioIfNecessary_l();
- if (mFlags & TEXTPLAYER_STARTED) {
+ if (mFlags & TEXTPLAYER_INITIALIZED) {
mTextDriver->seekToAsync(mSeekTimeUs);
}
@@ -1691,8 +1665,8 @@
}
}
- if ((mFlags & TEXTPLAYER_STARTED) && !(mFlags & (TEXT_RUNNING | SEEK_PREVIEW))) {
- mTextDriver->resume();
+ if ((mFlags & TEXTPLAYER_INITIALIZED) && !(mFlags & (TEXT_RUNNING | SEEK_PREVIEW))) {
+ mTextDriver->start();
modifyFlags(TEXT_RUNNING, SET);
}
@@ -2232,20 +2206,6 @@
status_t AwesomePlayer::setParameter(int key, const Parcel &request) {
switch (key) {
- case KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX:
- {
- Mutex::Autolock autoLock(mTimedTextLock);
- return setTimedTextTrackIndex(request.readInt32());
- }
- case KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE:
- {
- Mutex::Autolock autoLock(mTimedTextLock);
- if (mTextDriver == NULL) {
- mTextDriver = new TimedTextDriver(mListener);
- }
-
- return mTextDriver->addOutOfBandTextSource(request);
- }
case KEY_PARAMETER_CACHE_STAT_COLLECT_FREQ_MS:
{
return setCacheStatCollectFreq(request);
@@ -2294,6 +2254,103 @@
}
}
+status_t AwesomePlayer::invoke(const Parcel &request, Parcel *reply) {
+ if (NULL == reply) {
+ return android::BAD_VALUE;
+ }
+ int32_t methodId;
+ status_t ret = request.readInt32(&methodId);
+ if (ret != android::OK) {
+ return ret;
+ }
+ switch(methodId) {
+ case INVOKE_ID_GET_TRACK_INFO:
+ {
+ Mutex::Autolock autoLock(mTimedTextLock);
+ if (mTextDriver == NULL) {
+ return INVALID_OPERATION;
+ }
+ mTextDriver->getTrackInfo(reply);
+ return OK;
+ }
+ case INVOKE_ID_ADD_EXTERNAL_SOURCE:
+ {
+ Mutex::Autolock autoLock(mTimedTextLock);
+ if (mTextDriver == NULL) {
+ mTextDriver = new TimedTextDriver(mListener);
+ }
+ // String values written in Parcel are UTF-16 values.
+ String16 uri16 = request.readString16();
+ const char *uri = NULL;
+ if (uri16 != NULL) {
+ uri = String8(uri16).string();
+ }
+ String16 mimeType16 = request.readString16();
+ const char *mimeType = NULL;
+ if (mimeType16 != NULL) {
+ mimeType = String8(mimeType16).string();
+ }
+ return mTextDriver->addOutOfBandTextSource(uri, mimeType);
+ }
+ case INVOKE_ID_ADD_EXTERNAL_SOURCE_FD:
+ {
+ Mutex::Autolock autoLock(mTimedTextLock);
+ if (mTextDriver == NULL) {
+ mTextDriver = new TimedTextDriver(mListener);
+ }
+ int fd = request.readFileDescriptor();
+ off64_t offset = request.readInt64();
+ size_t length = request.readInt64();
+ String16 mimeType16 = request.readString16();
+ const char *mimeType = NULL;
+ if (mimeType16 != NULL) {
+ mimeType = String8(mimeType16).string();
+ }
+
+ return mTextDriver->addOutOfBandTextSource(
+ fd, offset, length, mimeType);
+ }
+ case INVOKE_ID_SELECT_TRACK:
+ {
+ Mutex::Autolock autoLock(mTimedTextLock);
+ if (mTextDriver == NULL) {
+ return INVALID_OPERATION;
+ }
+
+ status_t err = mTextDriver->selectTrack(
+ request.readInt32());
+ if (err == OK) {
+ modifyFlags(TEXTPLAYER_INITIALIZED, SET);
+ if (mFlags & PLAYING && !(mFlags & TEXT_RUNNING)) {
+ mTextDriver->start();
+ modifyFlags(TEXT_RUNNING, SET);
+ }
+ }
+ return err;
+ }
+ case INVOKE_ID_UNSELECT_TRACK:
+ {
+ Mutex::Autolock autoLock(mTimedTextLock);
+ if (mTextDriver == NULL) {
+ return INVALID_OPERATION;
+ }
+ status_t err = mTextDriver->unselectTrack(
+ request.readInt32());
+ if (err == OK) {
+ modifyFlags(TEXTPLAYER_INITIALIZED, CLEAR);
+ modifyFlags(TEXT_RUNNING, CLEAR);
+ }
+ return err;
+ }
+ default:
+ {
+ return ERROR_UNSUPPORTED;
+ }
+ }
+ // It will not reach here.
+ return OK;
+}
+
bool AwesomePlayer::isStreamingHTTP() const {
return mCachedSource != NULL || mWVMExtractor != NULL;
}
diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp
index 444e823..2549de6 100644
--- a/media/libstagefright/MediaDefs.cpp
+++ b/media/libstagefright/MediaDefs.cpp
@@ -52,5 +52,6 @@
const char *MEDIA_MIMETYPE_CONTAINER_WVM = "video/wvm";
const char *MEDIA_MIMETYPE_TEXT_3GPP = "text/3gpp-tt";
+const char *MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
} // namespace android
diff --git a/media/libstagefright/XINGSeeker.cpp b/media/libstagefright/XINGSeeker.cpp
index 2091381..e36d619 100644
--- a/media/libstagefright/XINGSeeker.cpp
+++ b/media/libstagefright/XINGSeeker.cpp
@@ -24,7 +24,7 @@
static bool parse_xing_header(
const sp<DataSource> &source, off64_t first_frame_pos,
int32_t *frame_number = NULL, int32_t *byte_number = NULL,
- unsigned char *table_of_contents = NULL,
+ unsigned char *table_of_contents = NULL, bool *toc_is_valid = NULL,
int32_t *quality_indicator = NULL, int64_t *duration = NULL);
// static
@@ -36,7 +36,7 @@
if (!parse_xing_header(
source, first_frame_pos,
- NULL, &seeker->mSizeBytes, seeker->mTableOfContents,
+ NULL, &seeker->mSizeBytes, seeker->mTOC, &seeker->mTOCValid,
NULL, &seeker->mDurationUs)) {
return NULL;
}
@@ -60,7 +60,7 @@
}
bool XINGSeeker::getOffsetForTime(int64_t *timeUs, off64_t *pos) {
- if (mSizeBytes == 0 || mTableOfContents[0] <= 0 || mDurationUs < 0) {
+ if (mSizeBytes == 0 || !mTOCValid || mDurationUs < 0) {
return false;
}
@@ -76,10 +76,10 @@
if ( a == 0 ) {
fa = 0.0f;
} else {
- fa = (float)mTableOfContents[a-1];
+ fa = (float)mTOC[a-1];
}
if ( a < 99 ) {
- fb = (float)mTableOfContents[a];
+ fb = (float)mTOC[a];
} else {
fb = 256.0f;
}
@@ -94,7 +94,8 @@
static bool parse_xing_header(
const sp<DataSource> &source, off64_t first_frame_pos,
int32_t *frame_number, int32_t *byte_number,
- unsigned char *table_of_contents, int32_t *quality_indicator,
+ unsigned char *table_of_contents, bool *toc_valid,
+ int32_t *quality_indicator,
int64_t *duration) {
if (frame_number) {
*frame_number = 0;
@@ -102,8 +103,8 @@
if (byte_number) {
*byte_number = 0;
}
- if (table_of_contents) {
- table_of_contents[0] = 0;
+ if (toc_valid) {
+ *toc_valid = false;
}
if (quality_indicator) {
*quality_indicator = 0;
@@ -199,10 +200,13 @@
offset += 4;
}
if (flags & 0x0004) { // TOC field is present
- if (table_of_contents) {
+ if (table_of_contents) {
if (source->readAt(offset + 1, table_of_contents, 99) < 99) {
return false;
}
+ if (toc_valid) {
+ *toc_valid = true;
+ }
}
offset += 100;
}
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 4c7bfa6..06e9468 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -90,6 +90,7 @@
status_t setParameter(int key, const Parcel &request);
status_t getParameter(int key, Parcel *reply);
+ status_t invoke(const Parcel &request, Parcel *reply);
status_t setCacheStatCollectFreq(const Parcel &request);
status_t seekTo(int64_t timeUs);
@@ -100,8 +101,6 @@
void postAudioEOS(int64_t delayUs = 0ll);
void postAudioSeekComplete();
- status_t setTimedTextTrackIndex(int32_t index);
-
status_t dump(int fd, const Vector<String16> &args) const;
private:
@@ -136,7 +135,7 @@
INCOGNITO = 0x8000,
TEXT_RUNNING = 0x10000,
- TEXTPLAYER_STARTED = 0x20000,
+ TEXTPLAYER_INITIALIZED = 0x20000,
SLOW_DECODER_HACK = 0x40000,
};
diff --git a/media/libstagefright/include/XINGSeeker.h b/media/libstagefright/include/XINGSeeker.h
index ec5bd9b..8510979 100644
--- a/media/libstagefright/include/XINGSeeker.h
+++ b/media/libstagefright/include/XINGSeeker.h
@@ -37,7 +37,8 @@
int32_t mSizeBytes;
// TOC entries in XING header. Skip the first one since it's always 0.
- unsigned char mTableOfContents[99];
+ unsigned char mTOC[99];
+ bool mTOCValid;
XINGSeeker();
diff --git a/media/libstagefright/timedtext/TimedText3GPPSource.cpp b/media/libstagefright/timedtext/TimedText3GPPSource.cpp
index 4a3bfd3..c423ef0 100644
--- a/media/libstagefright/timedtext/TimedText3GPPSource.cpp
+++ b/media/libstagefright/timedtext/TimedText3GPPSource.cpp
@@ -110,4 +110,8 @@
return OK;
}
+sp<MetaData> TimedText3GPPSource::getFormat() {
+ return mSource->getFormat();
+}
+
} // namespace android
diff --git a/media/libstagefright/timedtext/TimedText3GPPSource.h b/media/libstagefright/timedtext/TimedText3GPPSource.h
index dfc6418..4ec3d8a 100644
--- a/media/libstagefright/timedtext/TimedText3GPPSource.h
+++ b/media/libstagefright/timedtext/TimedText3GPPSource.h
@@ -37,6 +37,7 @@
Parcel *parcel,
const MediaSource::ReadOptions *options = NULL);
virtual status_t extractGlobalDescriptions(Parcel *parcel);
+ virtual sp<MetaData> getFormat();
protected:
virtual ~TimedText3GPPSource();
diff --git a/media/libstagefright/timedtext/TimedTextDriver.cpp b/media/libstagefright/timedtext/TimedTextDriver.cpp
index c70870e..ed83894 100644
--- a/media/libstagefright/timedtext/TimedTextDriver.cpp
+++ b/media/libstagefright/timedtext/TimedTextDriver.cpp
@@ -20,10 +20,13 @@
#include <binder/IPCThreadState.h>
+#include <media/mediaplayer.h>
#include <media/MediaPlayerInterface.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaSource.h>
-#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MetaData.h>
#include <media/stagefright/Utils.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALooper.h>
@@ -47,24 +50,22 @@
}
TimedTextDriver::~TimedTextDriver() {
- mTextInBandVector.clear();
- mTextOutOfBandVector.clear();
+ mTextSourceVector.clear();
mLooper->stop();
}
-status_t TimedTextDriver::setTimedTextTrackIndex_l(int32_t index) {
- if (index >=
- (int)(mTextInBandVector.size() + mTextOutOfBandVector.size())) {
+status_t TimedTextDriver::selectTrack_l(int32_t index) {
+ if (index >= (int)(mTextSourceVector.size())) {
return BAD_VALUE;
}
sp<TimedTextSource> source;
- if (index < mTextInBandVector.size()) {
- source = mTextInBandVector.itemAt(index);
- } else {
- source = mTextOutOfBandVector.itemAt(index - mTextInBandVector.size());
- }
+ source = mTextSourceVector.itemAt(index);
mPlayer->setDataSource(source);
+ if (mState == UNINITIALIZED) {
+ mState = PAUSED;
+ }
+ mCurrentTrackIndex = index;
return OK;
}
@@ -73,13 +74,10 @@
switch (mState) {
case UNINITIALIZED:
return INVALID_OPERATION;
- case STOPPED:
- mPlayer->start();
- break;
case PLAYING:
return OK;
case PAUSED:
- mPlayer->resume();
+ mPlayer->start();
break;
default:
TRESPASS();
@@ -88,10 +86,6 @@
return OK;
}
-status_t TimedTextDriver::stop() {
- return pause();
-}
-
// TODO: Test if pause() works properly.
// Scenario 1: start - pause - resume
// Scenario 2: start - seek
@@ -101,8 +95,6 @@
switch (mState) {
case UNINITIALIZED:
return INVALID_OPERATION;
- case STOPPED:
- return OK;
case PLAYING:
mPlayer->pause();
break;
@@ -115,45 +107,17 @@
return OK;
}
-status_t TimedTextDriver::resume() {
- return start();
-}
-
-status_t TimedTextDriver::seekToAsync(int64_t timeUs) {
- mPlayer->seekToAsync(timeUs);
- return OK;
-}
-
-status_t TimedTextDriver::setTimedTextTrackIndex(int32_t index) {
- // TODO: This is current implementation for MediaPlayer::disableTimedText().
- // Find better way for readability.
- if (index < 0) {
- mPlayer->pause();
- return OK;
- }
-
+status_t TimedTextDriver::selectTrack(int32_t index) {
status_t ret = OK;
Mutex::Autolock autoLock(mLock);
switch (mState) {
case UNINITIALIZED:
- ret = INVALID_OPERATION;
- break;
case PAUSED:
- ret = setTimedTextTrackIndex_l(index);
+ ret = selectTrack_l(index);
break;
case PLAYING:
mPlayer->pause();
- ret = setTimedTextTrackIndex_l(index);
- if (ret != OK) {
- break;
- }
- mPlayer->start();
- break;
- case STOPPED:
- // TODO: The only difference between STOPPED and PAUSED is this
- // part. Revise the flow from "MediaPlayer::enableTimedText()" and
- // remove one of the status, PAUSED and STOPPED, if possible.
- ret = setTimedTextTrackIndex_l(index);
+ ret = selectTrack_l(index);
if (ret != OK) {
break;
}
@@ -165,6 +129,24 @@
return ret;
}
+status_t TimedTextDriver::unselectTrack(int32_t index) {
+ if (mCurrentTrackIndex != index) {
+ return INVALID_OPERATION;
+ }
+ status_t err = pause();
+ if (err != OK) {
+ return err;
+ }
+ Mutex::Autolock autoLock(mLock);
+ mState = UNINITIALIZED;
+ return OK;
+}
+
+status_t TimedTextDriver::seekToAsync(int64_t timeUs) {
+ mPlayer->seekToAsync(timeUs);
+ return OK;
+}
+
status_t TimedTextDriver::addInBandTextSource(
const sp<MediaSource>& mediaSource) {
sp<TimedTextSource> source =
@@ -173,25 +155,17 @@
return ERROR_UNSUPPORTED;
}
Mutex::Autolock autoLock(mLock);
- mTextInBandVector.add(source);
- if (mState == UNINITIALIZED) {
- mState = STOPPED;
- }
+ mTextSourceVector.add(source);
return OK;
}
status_t TimedTextDriver::addOutOfBandTextSource(
- const Parcel &request) {
+ const char *uri, const char *mimeType) {
// TODO: Define "TimedTextSource::CreateFromURI(uri)"
// and move below lines there..?
- // String values written in Parcel are UTF-16 values.
- const String16 uri16 = request.readString16();
- String8 uri = String8(request.readString16());
-
- uri.toLower();
// To support local subtitle file only for now
- if (strncasecmp("file://", uri.string(), 7)) {
+ if (strncasecmp("file://", uri, 7)) {
return ERROR_UNSUPPORTED;
}
sp<DataSource> dataSource =
@@ -201,7 +175,7 @@
}
sp<TimedTextSource> source;
- if (uri.getPathExtension() == String8(".srt")) {
+ if (strcasecmp(mimeType, MEDIA_MIMETYPE_TEXT_SUBRIP)) {
source = TimedTextSource::CreateTimedTextSource(
dataSource, TimedTextSource::OUT_OF_BAND_FILE_SRT);
}
@@ -211,12 +185,38 @@
}
Mutex::Autolock autoLock(mLock);
-
- mTextOutOfBandVector.add(source);
- if (mState == UNINITIALIZED) {
- mState = STOPPED;
- }
+ mTextSourceVector.add(source);
return OK;
}
+status_t TimedTextDriver::addOutOfBandTextSource(
+ int fd, off64_t offset, size_t length, const char *mimeType) {
+ // Not supported yet. This requires DataSource::sniff to detect various text
+ // formats such as srt/smi/ttml.
+ return ERROR_UNSUPPORTED;
+}
+
+void TimedTextDriver::getTrackInfo(Parcel *parcel) {
+ Mutex::Autolock autoLock(mLock);
+ Vector<sp<TimedTextSource> >::const_iterator iter;
+ parcel->writeInt32(mTextSourceVector.size());
+ for (iter = mTextSourceVector.begin();
+ iter != mTextSourceVector.end(); ++iter) {
+ sp<MetaData> meta = (*iter)->getFormat();
+ if (meta != NULL) {
+ // There are two fields.
+ parcel->writeInt32(2);
+
+ // track type.
+ parcel->writeInt32(MEDIA_TRACK_TYPE_TIMEDTEXT);
+
+ const char *lang = "und";
+ meta->findCString(kKeyMediaLanguage, &lang);
+ parcel->writeString16(String16(lang));
+ } else {
+ parcel->writeInt32(0);
+ }
+ }
+}
+
} // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextPlayer.cpp b/media/libstagefright/timedtext/TimedTextPlayer.cpp
index bda7b46..8717914 100644
--- a/media/libstagefright/timedtext/TimedTextPlayer.cpp
+++ b/media/libstagefright/timedtext/TimedTextPlayer.cpp
@@ -56,10 +56,6 @@
(new AMessage(kWhatPause, id()))->post();
}
-void TimedTextPlayer::resume() {
- start();
-}
-
void TimedTextPlayer::seekToAsync(int64_t timeUs) {
sp<AMessage> msg = new AMessage(kWhatSeek, id());
msg->setInt64("seekTimeUs", timeUs);
@@ -104,9 +100,9 @@
if (obj != NULL) {
sp<ParcelEvent> parcelEvent;
parcelEvent = static_cast<ParcelEvent*>(obj.get());
- notifyListener(MEDIA_TIMED_TEXT, &(parcelEvent->parcel));
+ notifyListener(&(parcelEvent->parcel));
} else {
- notifyListener(MEDIA_TIMED_TEXT);
+ notifyListener();
}
doRead();
break;
@@ -119,14 +115,18 @@
mSource->stop();
}
mSource = static_cast<TimedTextSource*>(obj.get());
- mSource->start();
- Parcel parcel;
- if (mSource->extractGlobalDescriptions(&parcel) == OK &&
- parcel.dataSize() > 0) {
- notifyListener(MEDIA_TIMED_TEXT, &parcel);
- } else {
- notifyListener(MEDIA_TIMED_TEXT);
+ status_t err = mSource->start();
+ if (err != OK) {
+ notifyError(err);
+ break;
}
+ Parcel parcel;
+ err = mSource->extractGlobalDescriptions(&parcel);
+ if (err != OK) {
+ notifyError(err);
+ break;
+ }
+ notifyListener(&parcel);
break;
}
}
@@ -141,8 +141,12 @@
void TimedTextPlayer::doRead(MediaSource::ReadOptions* options) {
int64_t timeUs = 0;
sp<ParcelEvent> parcelEvent = new ParcelEvent();
- mSource->read(&timeUs, &(parcelEvent->parcel), options);
- postTextEvent(parcelEvent, timeUs);
+ status_t err = mSource->read(&timeUs, &(parcelEvent->parcel), options);
+ if (err != OK) {
+ notifyError(err);
+ } else {
+ postTextEvent(parcelEvent, timeUs);
+ }
}
void TimedTextPlayer::postTextEvent(const sp<ParcelEvent>& parcel, int64_t timeUs) {
@@ -151,7 +155,7 @@
int64_t positionUs, delayUs;
int32_t positionMs = 0;
listener->getCurrentPosition(&positionMs);
- positionUs = positionMs * 1000;
+ positionUs = positionMs * 1000ll;
if (timeUs <= positionUs + kAdjustmentProcessingTimeUs) {
delayUs = 0;
@@ -167,13 +171,20 @@
}
}
-void TimedTextPlayer::notifyListener(int msg, const Parcel *parcel) {
+void TimedTextPlayer::notifyError(int error) {
+ sp<MediaPlayerBase> listener = mListener.promote();
+ if (listener != NULL) {
+ listener->sendEvent(MEDIA_INFO, MEDIA_INFO_TIMED_TEXT_ERROR, error);
+ }
+}
+
+void TimedTextPlayer::notifyListener(const Parcel *parcel) {
sp<MediaPlayerBase> listener = mListener.promote();
if (listener != NULL) {
if (parcel != NULL && (parcel->dataSize() > 0)) {
- listener->sendEvent(msg, 0, 0, parcel);
+ listener->sendEvent(MEDIA_TIMED_TEXT, 0, 0, parcel);
} else { // send an empty timed text to clear the screen
- listener->sendEvent(msg);
+ listener->sendEvent(MEDIA_TIMED_TEXT);
}
}
}
diff --git a/media/libstagefright/timedtext/TimedTextPlayer.h b/media/libstagefright/timedtext/TimedTextPlayer.h
index 837beeb..b869f18 100644
--- a/media/libstagefright/timedtext/TimedTextPlayer.h
+++ b/media/libstagefright/timedtext/TimedTextPlayer.h
@@ -40,7 +40,6 @@
void start();
void pause();
- void resume();
void seekToAsync(int64_t timeUs);
void setDataSource(sp<TimedTextSource> source);
@@ -68,7 +67,8 @@
void doRead(MediaSource::ReadOptions* options = NULL);
void onTextEvent();
void postTextEvent(const sp<ParcelEvent>& parcel = NULL, int64_t timeUs = -1);
- void notifyListener(int msg, const Parcel *parcel = NULL);
+ void notifyError(int error = 0);
+ void notifyListener(const Parcel *parcel = NULL);
DISALLOW_EVIL_CONSTRUCTORS(TimedTextPlayer);
};
diff --git a/media/libstagefright/timedtext/TimedTextSRTSource.cpp b/media/libstagefright/timedtext/TimedTextSRTSource.cpp
index 3752d34..c44a99b 100644
--- a/media/libstagefright/timedtext/TimedTextSRTSource.cpp
+++ b/media/libstagefright/timedtext/TimedTextSRTSource.cpp
@@ -21,8 +21,10 @@
#include <binder/Parcel.h>
#include <media/stagefright/foundation/AString.h>
#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDefs.h> // for MEDIA_MIMETYPE_xxx
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
#include "TimedTextSRTSource.h"
#include "TextDescriptions.h"
@@ -31,6 +33,7 @@
TimedTextSRTSource::TimedTextSRTSource(const sp<DataSource>& dataSource)
: mSource(dataSource),
+ mMetaData(new MetaData),
mIndex(0) {
}
@@ -42,10 +45,14 @@
if (err != OK) {
reset();
}
+ // TODO: Need to detect the language, because SRT doesn't give language
+ // information explicitly.
+ mMetaData->setCString(kKeyMediaLanguage, "");
return err;
}
void TimedTextSRTSource::reset() {
+ mMetaData->clear();
mTextVector.clear();
mIndex = 0;
}
@@ -272,4 +279,8 @@
return OK;
}
+sp<MetaData> TimedTextSRTSource::getFormat() {
+ return mMetaData;
+}
+
} // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextSRTSource.h b/media/libstagefright/timedtext/TimedTextSRTSource.h
index acc01f9..62710a0 100644
--- a/media/libstagefright/timedtext/TimedTextSRTSource.h
+++ b/media/libstagefright/timedtext/TimedTextSRTSource.h
@@ -39,12 +39,14 @@
int64_t *timeUs,
Parcel *parcel,
const MediaSource::ReadOptions *options = NULL);
+ virtual sp<MetaData> getFormat();
protected:
virtual ~TimedTextSRTSource();
private:
sp<DataSource> mSource;
+ sp<MetaData> mMetaData;
struct TextInfo {
int64_t endTimeUs;
diff --git a/media/libstagefright/timedtext/TimedTextSource.cpp b/media/libstagefright/timedtext/TimedTextSource.cpp
index ffbe1c3..953f7b5 100644
--- a/media/libstagefright/timedtext/TimedTextSource.cpp
+++ b/media/libstagefright/timedtext/TimedTextSource.cpp
@@ -59,4 +59,8 @@
return NULL;
}
+sp<MetaData> TimedTextSource::getFormat() {
+ return NULL;
+}
+
} // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextSource.h b/media/libstagefright/timedtext/TimedTextSource.h
index 06bae71..9349342 100644
--- a/media/libstagefright/timedtext/TimedTextSource.h
+++ b/media/libstagefright/timedtext/TimedTextSource.h
@@ -25,6 +25,7 @@
namespace android {
class DataSource;
+class MetaData;
class Parcel;
class TimedTextSource : public RefBase {
@@ -48,6 +49,7 @@
virtual status_t extractGlobalDescriptions(Parcel *parcel) {
return INVALID_OPERATION;
}
+ virtual sp<MetaData> getFormat();
protected:
virtual ~TimedTextSource() { }
diff --git a/services/java/com/android/server/wm/AppWindowToken.java b/services/java/com/android/server/wm/AppWindowToken.java
index b84fbdb..5ca09e7 100644
--- a/services/java/com/android/server/wm/AppWindowToken.java
+++ b/services/java/com/android/server/wm/AppWindowToken.java
@@ -27,6 +27,7 @@
import android.view.IApplicationToken;
import android.view.View;
import android.view.WindowManager;
+import android.view.WindowManagerPolicy;
import android.view.animation.Animation;
import android.view.animation.Transformation;
@@ -37,7 +38,7 @@
* Version of WindowToken that is specifically for a particular application (or
* really activity) that is displaying windows.
*/
-class AppWindowToken extends WindowToken implements WindowManagerService.StepAnimator {
+class AppWindowToken extends WindowToken {
// Non-null only for application tokens.
final IApplicationToken appToken;
@@ -195,8 +196,8 @@
}
}
- @Override
- public boolean stepAnimation(long currentTime) {
+
+ private boolean stepAnimation(long currentTime) {
if (animation == null) {
return false;
}
@@ -216,7 +217,7 @@
}
// This must be called while inside a transaction.
- boolean startAndFinishAnimationLocked(long currentTime, int dw, int dh) {
+ boolean stepAnimationLocked(long currentTime, int dw, int dh) {
if (!service.mDisplayFrozen && service.mPolicy.isScreenOnFully()) {
// We will run animations as long as the display isn't frozen.
@@ -240,7 +241,7 @@
animating = true;
}
// we're done!
- return true;
+ return stepAnimation(currentTime);
}
} else if (animation != null) {
// If the display is frozen, and there is a pending animation,
@@ -255,6 +256,7 @@
return false;
}
+ service.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
clearAnimation();
animating = false;
if (animLayerAdjustment != 0) {
diff --git a/services/java/com/android/server/wm/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java
index 7b5bf08..58187b6 100644
--- a/services/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -29,7 +29,7 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Transformation;
-class ScreenRotationAnimation implements WindowManagerService.StepAnimator {
+class ScreenRotationAnimation {
static final String TAG = "ScreenRotationAnimation";
static final boolean DEBUG_STATE = false;
static final boolean DEBUG_TRANSFORMS = false;
@@ -540,8 +540,7 @@
|| mRotateFrameAnimation != null;
}
- @Override
- public boolean stepAnimation(long now) {
+ private boolean stepAnimation(long now) {
if (mFinishAnimReady && mFinishAnimStartTime < 0) {
if (DEBUG_STATE) Slog.v(TAG, "Step: finish anim now ready");
@@ -725,7 +724,7 @@
setSnapshotTransform(mSnapshotFinalMatrix, mExitTransformation.getAlpha());
}
- public boolean startAndFinishAnimationLocked(long now) {
+ public boolean stepAnimationLocked(long now) {
if (!isAnimating()) {
if (DEBUG_STATE) Slog.v(TAG, "Step: no animations running");
mFinishAnimReady = false;
@@ -763,8 +762,8 @@
}
mAnimRunning = true;
}
-
- return true;
+
+ return stepAnimation(now);
}
public Transformation getEnterTransformation() {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 22949f3..4f55217 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -461,6 +461,7 @@
= new ArrayList<IRotationWatcher>();
int mDeferredRotationPauseCount;
+ int mPendingLayoutChanges = 0;
boolean mLayoutNeeded = true;
boolean mTraversalScheduled = false;
boolean mDisplayFrozen = false;
@@ -617,18 +618,6 @@
final AnimationRunnable mAnimationRunnable = new AnimationRunnable();
boolean mAnimationScheduled;
- interface StepAnimator {
- /**
- * Continue the stepping of an ongoing animation. When the animation completes this method
- * must disable the animation on the StepAnimator.
- * @param currentTime Animation time in milliseconds. Use SystemClock.uptimeMillis().
- * @return True if the animation is still going on, false if the animation has completed
- * and stepAnimation has cleared the animation locally.
- */
- boolean stepAnimation(long currentTime);
- }
- final ArrayList<StepAnimator> mStepAnimators = new ArrayList<StepAnimator>();
-
final class DragInputEventReceiver extends InputEventReceiver {
public DragInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
@@ -2995,15 +2984,27 @@
}
void applyEnterAnimationLocked(WindowState win) {
- int transit = WindowManagerPolicy.TRANSIT_SHOW;
+ final int transit;
if (win.mEnterAnimationPending) {
win.mEnterAnimationPending = false;
transit = WindowManagerPolicy.TRANSIT_ENTER;
+ } else {
+ transit = WindowManagerPolicy.TRANSIT_SHOW;
}
applyAnimationLocked(win, transit, true);
}
+ /**
+ * Choose the correct animation and set it to the passed WindowState.
+ * @param win The window to add the animation to.
+ * @param transit If WindowManagerPolicy.TRANSIT_PREVIEW_DONE and the app window has been drawn
+ * then the animation will be app_starting_exit. Any other value loads the animation from
+ * the switch statement below.
+ * @param isEntrance The animation type the last time this was called. Used to keep from
+ * loading the same animation twice.
+ * @return true if an animation has been loaded.
+ */
boolean applyAnimationLocked(WindowState win,
int transit, boolean isEntrance) {
if (win.mLocalAnimating && win.mAnimationIsEntrance == isEntrance) {
@@ -7643,20 +7644,6 @@
}
/**
- * Run through each of the animating objects saved in mStepAnimators.
- */
- private void stepAnimations() {
- final long currentTime = SystemClock.uptimeMillis();
- for (final StepAnimator stepAnimator : mStepAnimators) {
- final boolean more = stepAnimator.stepAnimation(currentTime);
- if (DEBUG_ANIM) {
- Slog.v(TAG, "stepAnimations: " + currentTime + ": Stepped " + stepAnimator
- + (more ? " more" : " done"));
- }
- }
- }
-
- /**
* Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
* Update animations of all applications, including those associated with exiting/removed apps.
*
@@ -7670,16 +7657,14 @@
final int NAT = mAppTokens.size();
for (i=0; i<NAT; i++) {
final AppWindowToken appToken = mAppTokens.get(i);
- if (appToken.startAndFinishAnimationLocked(currentTime, innerDw, innerDh)) {
- mStepAnimators.add(appToken);
+ if (appToken.stepAnimationLocked(currentTime, innerDw, innerDh)) {
mInnerFields.mAnimating = true;
}
}
final int NEAT = mExitingAppTokens.size();
for (i=0; i<NEAT; i++) {
final AppWindowToken appToken = mExitingAppTokens.get(i);
- if (appToken.startAndFinishAnimationLocked(currentTime, innerDw, innerDh)) {
- mStepAnimators.add(appToken);
+ if (appToken.stepAnimationLocked(currentTime, innerDw, innerDh)) {
mInnerFields.mAnimating = true;
}
}
@@ -7687,10 +7672,9 @@
if (mScreenRotationAnimation != null) {
if (mScreenRotationAnimation.isAnimating() ||
mScreenRotationAnimation.mFinishAnimReady) {
- if (mScreenRotationAnimation.startAndFinishAnimationLocked(currentTime)) {
+ if (mScreenRotationAnimation.stepAnimationLocked(currentTime)) {
mInnerFields.mUpdateRotation = false;
mInnerFields.mAnimating = true;
- mStepAnimators.add(mScreenRotationAnimation);
} else {
mInnerFields.mUpdateRotation = true;
mScreenRotationAnimation.kill();
@@ -7711,9 +7695,7 @@
*/
private int updateWindowsAndWallpaperLocked(final long currentTime, final int dw, final int dh,
final int innerDw, final int innerDh) {
-
- mPolicy.beginAnimationLw(dw, dh);
-
+ int changes = 0;
for (int i = mWindows.size() - 1; i >= 0; i--) {
WindowState w = mWindows.get(i);
@@ -7727,6 +7709,7 @@
if (DEBUG_WALLPAPER) Slog.v(TAG,
"First draw done in potential wallpaper target " + w);
mInnerFields.mWallpaperMayChange = true;
+ changes |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
}
}
@@ -7749,13 +7732,7 @@
}
final boolean wasAnimating = w.mWasAnimating;
-
-
- final boolean nowAnimating = w.startAndFinishAnimationLocked(currentTime);
- if (nowAnimating) {
- mStepAnimators.add(w);
- mInnerFields.mAnimating = true;
- }
+ final boolean nowAnimating = w.stepAnimationLocked(currentTime);
if (DEBUG_WALLPAPER) {
Slog.v(TAG, w + ": wasAnimating=" + wasAnimating +
@@ -7806,6 +7783,7 @@
if (wasAnimating && !w.mAnimating && mWallpaperTarget == w) {
mInnerFields.mWallpaperMayChange = true;
+ changes |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
}
if (mPolicy.doesForceHide(w, attrs)) {
@@ -7814,6 +7792,7 @@
"Animation started that could impact force hide: "
+ w);
mInnerFields.mWallpaperForceHidingChanged = true;
+ changes |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
mFocusMayChange = true;
} else if (w.isReadyForDisplay() && w.mAnimation == null) {
mInnerFields.mForceHiding = true;
@@ -7852,10 +7831,9 @@
if (changed && (attrs.flags
& WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
mInnerFields.mWallpaperMayChange = true;
+ changes |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
}
}
-
- mPolicy.animatingWindowLw(w, attrs);
}
final AppWindowToken atoken = w.mAppToken;
@@ -7900,10 +7878,11 @@
}
} else if (w.mReadyToShow) {
w.performShowLocked();
+ changes |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
}
} // end forall windows
- return mPolicy.finishAnimationLw();
+ return changes;
}
/**
@@ -8322,6 +8301,72 @@
return changes;
}
+ private void updateResizingWindows(final WindowState w) {
+ if (!w.mAppFreezing && w.mLayoutSeq == mLayoutSeq) {
+ w.mContentInsetsChanged |=
+ !w.mLastContentInsets.equals(w.mContentInsets);
+ w.mVisibleInsetsChanged |=
+ !w.mLastVisibleInsets.equals(w.mVisibleInsets);
+ boolean configChanged =
+ w.mConfiguration != mCurConfiguration
+ && (w.mConfiguration == null
+ || mCurConfiguration.diff(w.mConfiguration) != 0);
+ if (DEBUG_CONFIGURATION && configChanged) {
+ Slog.v(TAG, "Win " + w + " config changed: "
+ + mCurConfiguration);
+ }
+ if (localLOGV) Slog.v(TAG, "Resizing " + w
+ + ": configChanged=" + configChanged
+ + " last=" + w.mLastFrame + " frame=" + w.mFrame);
+ w.mLastFrame.set(w.mFrame);
+ if (w.mContentInsetsChanged
+ || w.mVisibleInsetsChanged
+ || w.mSurfaceResized
+ || configChanged) {
+ if (DEBUG_RESIZE || DEBUG_ORIENTATION) {
+ Slog.v(TAG, "Resize reasons: "
+ + " contentInsetsChanged=" + w.mContentInsetsChanged
+ + " visibleInsetsChanged=" + w.mVisibleInsetsChanged
+ + " surfaceResized=" + w.mSurfaceResized
+ + " configChanged=" + configChanged);
+ }
+
+ w.mLastContentInsets.set(w.mContentInsets);
+ w.mLastVisibleInsets.set(w.mVisibleInsets);
+ makeWindowFreezingScreenIfNeededLocked(w);
+ // If the orientation is changing, then we need to
+ // hold off on unfreezing the display until this
+ // window has been redrawn; to do that, we need
+ // to go through the process of getting informed
+ // by the application when it has finished drawing.
+ if (w.mOrientationChanging) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG,
+ "Orientation start waiting for draw in "
+ + w + ", surface " + w.mSurface);
+ w.mDrawPending = true;
+ w.mCommitDrawPending = false;
+ w.mReadyToShow = false;
+ if (w.mAppToken != null) {
+ w.mAppToken.allDrawn = false;
+ }
+ }
+ if (!mResizingWindows.contains(w)) {
+ if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG,
+ "Resizing window " + w + " to " + w.mSurfaceW
+ + "x" + w.mSurfaceH);
+ mResizingWindows.add(w);
+ }
+ } else if (w.mOrientationChanging) {
+ if (!w.mDrawPending && !w.mCommitDrawPending) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG,
+ "Orientation not waiting for draw in "
+ + w + ", surface " + w.mSurface);
+ w.mOrientationChanging = false;
+ }
+ }
+ }
+ }
+
/**
* Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method.
*
@@ -8407,69 +8452,7 @@
}
}
- if (!w.mAppFreezing && w.mLayoutSeq == mLayoutSeq) {
- w.mContentInsetsChanged |=
- !w.mLastContentInsets.equals(w.mContentInsets);
- w.mVisibleInsetsChanged |=
- !w.mLastVisibleInsets.equals(w.mVisibleInsets);
- boolean configChanged =
- w.mConfiguration != mCurConfiguration
- && (w.mConfiguration == null
- || mCurConfiguration.diff(w.mConfiguration) != 0);
- if (DEBUG_CONFIGURATION && configChanged) {
- Slog.v(TAG, "Win " + w + " config changed: "
- + mCurConfiguration);
- }
- if (localLOGV) Slog.v(TAG, "Resizing " + w
- + ": configChanged=" + configChanged
- + " last=" + w.mLastFrame + " frame=" + w.mFrame);
- w.mLastFrame.set(w.mFrame);
- if (w.mContentInsetsChanged
- || w.mVisibleInsetsChanged
- || w.mSurfaceResized
- || configChanged) {
- if (DEBUG_RESIZE || DEBUG_ORIENTATION) {
- Slog.v(TAG, "Resize reasons: "
- + " contentInsetsChanged=" + w.mContentInsetsChanged
- + " visibleInsetsChanged=" + w.mVisibleInsetsChanged
- + " surfaceResized=" + w.mSurfaceResized
- + " configChanged=" + configChanged);
- }
-
- w.mLastContentInsets.set(w.mContentInsets);
- w.mLastVisibleInsets.set(w.mVisibleInsets);
- makeWindowFreezingScreenIfNeededLocked(w);
- // If the orientation is changing, then we need to
- // hold off on unfreezing the display until this
- // window has been redrawn; to do that, we need
- // to go through the process of getting informed
- // by the application when it has finished drawing.
- if (w.mOrientationChanging) {
- if (DEBUG_ORIENTATION) Slog.v(TAG,
- "Orientation start waiting for draw in "
- + w + ", surface " + w.mSurface);
- w.mDrawPending = true;
- w.mCommitDrawPending = false;
- w.mReadyToShow = false;
- if (w.mAppToken != null) {
- w.mAppToken.allDrawn = false;
- }
- }
- if (!mResizingWindows.contains(w)) {
- if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG,
- "Resizing window " + w + " to " + w.mSurfaceW
- + "x" + w.mSurfaceH);
- mResizingWindows.add(w);
- }
- } else if (w.mOrientationChanging) {
- if (!w.mDrawPending && !w.mCommitDrawPending) {
- if (DEBUG_ORIENTATION) Slog.v(TAG,
- "Orientation not waiting for draw in "
- + w + ", surface " + w.mSurface);
- w.mOrientationChanging = false;
- }
- }
- }
+ updateResizingWindows(w);
if (w.mAttachedHidden || !w.isReadyForDisplay()) {
if (!w.mLastHidden) {
@@ -8678,6 +8661,67 @@
}
}
+ private final int performAnimationsLocked(long currentTime, int dw, int dh,
+ int innerDw, int innerDh) {
+ ++mTransactionSequence;
+
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: seq="
+ + mTransactionSequence + " mAnimating="
+ + mInnerFields.mAnimating);
+
+ mInnerFields.mTokenMayBeDrawn = false;
+ mInnerFields.mWallpaperMayChange = false;
+ mInnerFields.mForceHiding = false;
+ mInnerFields.mDetachedWallpaper = null;
+ mInnerFields.mWindowAnimationBackground = null;
+ mInnerFields.mWindowAnimationBackgroundColor = 0;
+
+ int changes = updateWindowsAndWallpaperLocked(currentTime, dw, dh, innerDw, innerDh);
+
+ if (mInnerFields.mTokenMayBeDrawn) {
+ changes |= testTokenMayBeDrawnLocked();
+ }
+
+ // If we are ready to perform an app transition, check through
+ // all of the app tokens to be shown and see if they are ready
+ // to go.
+ if (mAppTransitionReady) {
+ changes |= handleAppTransitionReadyLocked();
+ }
+
+ mInnerFields.mAdjResult = 0;
+
+ if (!mInnerFields.mAnimating && mAppTransitionRunning) {
+ // We have finished the animation of an app transition. To do
+ // this, we have delayed a lot of operations like showing and
+ // hiding apps, moving apps in Z-order, etc. The app token list
+ // reflects the correct Z-order, but the window list may now
+ // be out of sync with it. So here we will just rebuild the
+ // entire app window list. Fun!
+ changes |= handleAnimatingStoppedAndTransitionLocked();
+ }
+
+ if (mInnerFields.mWallpaperForceHidingChanged && changes == 0 && !mAppTransitionReady) {
+ // At this point, there was a window with a wallpaper that
+ // was force hiding other windows behind it, but now it
+ // is going away. This may be simple -- just animate
+ // away the wallpaper and its window -- or it may be
+ // hard -- the wallpaper now needs to be shown behind
+ // something that was hidden.
+ changes |= animateAwayWallpaperLocked();
+ }
+
+ changes |= testWallpaperAndBackgroundLocked();
+
+ if (mLayoutNeeded) {
+ changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
+ }
+
+ if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: changes=0x"
+ + Integer.toHexString(changes));
+ return changes;
+ }
+
// "Something has changed! Let's make it correct now."
private final void performLayoutAndPlaceSurfacesLockedInner(
boolean recoveringMemory) {
@@ -8741,7 +8785,6 @@
try {
mInnerFields.mWallpaperForceHidingChanged = false;
int repeats = 0;
- int changes = 0;
do {
repeats++;
@@ -8751,20 +8794,20 @@
break;
}
- if ((changes & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
+ if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
if ((adjustWallpaperWindowsLocked()&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
assignLayersLocked();
mLayoutNeeded = true;
}
}
- if ((changes & WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) {
+ if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) {
if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
if (updateOrientationFromAppTokensLocked(true)) {
mLayoutNeeded = true;
mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
}
}
- if ((changes & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
+ if ((mPendingLayoutChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
mLayoutNeeded = true;
}
@@ -8775,71 +8818,26 @@
Slog.w(TAG, "Layout repeat skipped after too many iterations");
}
- ++mTransactionSequence;
-
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: seq="
- + mTransactionSequence + " mAnimating="
- + mInnerFields.mAnimating);
-
- mInnerFields.mTokenMayBeDrawn = false;
- mInnerFields.mWallpaperMayChange = false;
- mInnerFields.mForceHiding = false;
- mInnerFields.mDetachedWallpaper = null;
- mInnerFields.mWindowAnimationBackground = null;
- mInnerFields.mWindowAnimationBackgroundColor = 0;
-
- mStepAnimators.clear();
- changes = updateWindowsAndWallpaperLocked(currentTime, dw, dh, innerDw, innerDh);
-
- if (mInnerFields.mTokenMayBeDrawn) {
- changes |= testTokenMayBeDrawnLocked();
+ // FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think
+ // it is animating.
+ mPendingLayoutChanges = 0;
+ mPolicy.beginAnimationLw(dw, dh);
+ for (i = mWindows.size() - 1; i >= 0; i--) {
+ WindowState w = mWindows.get(i);
+ if (w.mSurface != null) {
+ mPolicy.animatingWindowLw(w, w.mAttrs);
+ }
}
-
- // If we are ready to perform an app transition, check through
- // all of the app tokens to be shown and see if they are ready
- // to go.
- if (mAppTransitionReady) {
- changes |= handleAppTransitionReadyLocked();
- }
-
- mInnerFields.mAdjResult = 0;
-
- if (!mInnerFields.mAnimating && mAppTransitionRunning) {
- // We have finished the animation of an app transition. To do
- // this, we have delayed a lot of operations like showing and
- // hiding apps, moving apps in Z-order, etc. The app token list
- // reflects the correct Z-order, but the window list may now
- // be out of sync with it. So here we will just rebuild the
- // entire app window list. Fun!
- changes |= handleAnimatingStoppedAndTransitionLocked();
- }
-
- if (mInnerFields.mWallpaperForceHidingChanged && changes == 0 && !mAppTransitionReady) {
- // At this point, there was a window with a wallpaper that
- // was force hiding other windows behind it, but now it
- // is going away. This may be simple -- just animate
- // away the wallpaper and its window -- or it may be
- // hard -- the wallpaper now needs to be shown behind
- // something that was hidden.
- changes |= animateAwayWallpaperLocked();
- }
-
- changes |= testWallpaperAndBackgroundLocked();
-
- if (mLayoutNeeded) {
- changes |= PhoneWindowManager.FINISH_LAYOUT_REDO_LAYOUT;
- }
-
- if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: changes=0x"
- + Integer.toHexString(changes));
- } while (changes != 0);
+ mPendingLayoutChanges |= mPolicy.finishAnimationLw();
+
+ } while (mPendingLayoutChanges != 0);
// Update animations of all applications, including those
// associated with exiting/removed apps
+ mPendingLayoutChanges = performAnimationsLocked(currentTime, dw, dh,
+ innerDw, innerDh);
updateWindowsAppsAndRotationAnimationsLocked(currentTime, innerDw, innerDh);
-
- stepAnimations();
// THIRD LOOP: Update the surfaces of all windows.
@@ -9049,6 +9047,13 @@
if (wallpaperDestroyed) {
needRelayout = adjustWallpaperWindowsLocked() != 0;
}
+ if ((mPendingLayoutChanges & (
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER |
+ ADJUST_WALLPAPER_LAYERS_CHANGED |
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG |
+ WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT)) != 0) {
+ needRelayout = true;
+ }
if (needRelayout) {
requestTraversalLocked();
} else if (mInnerFields.mAnimating) {
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index e11c87a..48788e7 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -54,8 +54,7 @@
/**
* A window in the window manager.
*/
-final class WindowState implements WindowManagerPolicy.WindowState,
- WindowManagerService.StepAnimator {
+final class WindowState implements WindowManagerPolicy.WindowState {
static final boolean DEBUG_VISIBILITY = WindowManagerService.DEBUG_VISIBILITY;
static final boolean SHOW_TRANSACTIONS = WindowManagerService.SHOW_TRANSACTIONS;
static final boolean SHOW_LIGHT_TRANSACTIONS = WindowManagerService.SHOW_LIGHT_TRANSACTIONS;
@@ -995,8 +994,7 @@
return true;
}
- @Override
- public boolean stepAnimation(long currentTime) {
+ private boolean stepAnimation(long currentTime) {
if ((mAnimation == null) || !mLocalAnimating || (mAnimState != ANIM_STATE_RUNNING)) {
return false;
}
@@ -1013,7 +1011,7 @@
// This must be called while inside a transaction. Returns true if
// there is more animation to run.
- boolean startAndFinishAnimationLocked(long currentTime) {
+ boolean stepAnimationLocked(long currentTime) {
// Save the animation state as it was before this step so WindowManagerService can tell if
// we just started or just stopped animating by comparing mWasAnimating with isAnimating().
mWasAnimating = mAnimating;
@@ -1038,7 +1036,7 @@
}
if ((mAnimation != null) && mLocalAnimating &&
(mAnimState != ANIM_STATE_STOPPING)) {
- return true;
+ return stepAnimation(currentTime);
}
if (WindowManagerService.DEBUG_ANIM) Slog.v(
WindowManagerService.TAG, "Finished animation in " + this +
@@ -1133,6 +1131,7 @@
}
finishExit();
+ mService.mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
if (mAppToken != null) {
mAppToken.updateReportedVisibilityLocked();
@@ -1608,6 +1607,7 @@
boolean showLw(boolean doAnimation, boolean requestAnim) {
if (mPolicyVisibility && mPolicyVisibilityAfterAnim) {
+ // Already showing.
return false;
}
if (DEBUG_VISIBILITY) Slog.v(WindowManagerService.TAG, "Policy visibility true: " + this);
@@ -1647,6 +1647,7 @@
boolean current = doAnimation ? mPolicyVisibilityAfterAnim
: mPolicyVisibility;
if (!current) {
+ // Already hiding.
return false;
}
if (doAnimation) {