diff options
104 files changed, 2749 insertions, 1305 deletions
diff --git a/api/current.txt b/api/current.txt index 2a7e2f70491d..bf71f67f20c7 100644 --- a/api/current.txt +++ b/api/current.txt @@ -9083,14 +9083,17 @@ package android.graphics.drawable { ctor public GradientDrawable(android.graphics.drawable.GradientDrawable.Orientation, int[]); method public void draw(android.graphics.Canvas); method public int getOpacity(); + method public android.graphics.drawable.GradientDrawable.Orientation getOrientation(); method public void setAlpha(int); method public void setColor(int); method public void setColorFilter(android.graphics.ColorFilter); + method public void setColors(int[]); method public void setCornerRadii(float[]); method public void setCornerRadius(float); method public void setGradientCenter(float, float); method public void setGradientRadius(float); method public void setGradientType(int); + method public void setOrientation(android.graphics.drawable.GradientDrawable.Orientation); method public void setShape(int); method public void setSize(int, int); method public void setStroke(int, int); @@ -23828,6 +23831,9 @@ package android.view { method public android.view.ViewPropertyAnimator translationXBy(float); method public android.view.ViewPropertyAnimator translationY(float); method public android.view.ViewPropertyAnimator translationYBy(float); + method public android.view.ViewPropertyAnimator withEndAction(java.lang.Runnable); + method public android.view.ViewPropertyAnimator withLayer(); + method public android.view.ViewPropertyAnimator withStartAction(java.lang.Runnable); method public android.view.ViewPropertyAnimator x(float); method public android.view.ViewPropertyAnimator xBy(float); method public android.view.ViewPropertyAnimator y(float); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 9807b89a2b10..3c5f53a6efd8 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -4413,7 +4413,7 @@ public final class ActivityThread { }); } - public static final ActivityThread systemMain() { + public static ActivityThread systemMain() { HardwareRenderer.disable(true); ActivityThread thread = new ActivityThread(); thread.attach(true); @@ -4454,6 +4454,8 @@ public final class ActivityThread { ActivityThread thread = new ActivityThread(); thread.attach(false); + AsyncTask.init(); + if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java index 5e9abb7ba612..fd6bed7ee761 100644 --- a/core/java/android/os/AsyncTask.java +++ b/core/java/android/os/AsyncTask.java @@ -135,6 +135,8 @@ import java.util.concurrent.atomic.AtomicInteger; * <p>There are a few threading rules that must be followed for this class to * work properly:</p> * <ul> + * <li>The AsyncTask class must be loaded on the UI thread. This is done + * automatically as of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}.</li> * <li>The task instance must be created on the UI thread.</li> * <li>{@link #execute} must be invoked on the UI thread.</li> * <li>Do not call {@link #onPreExecute()}, {@link #onPostExecute}, diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index c08a402412c5..fa4dd25a6f38 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -189,7 +189,7 @@ class GLES20Canvas extends HardwareCanvas { } private static native int nGetMaximumTextureWidth(); - private static native int nGetMaximumTextureHeight(); + private static native int nGetMaximumTextureHeight(); /////////////////////////////////////////////////////////////////////////// // Setup @@ -268,6 +268,24 @@ class GLES20Canvas extends HardwareCanvas { private static native void nFinish(int renderer); + /** + * Returns the size of the stencil buffer required by the underlying + * implementation. + * + * @return The minimum number of bits the stencil buffer must. Always >= 0. + * + * @hide + */ + public static int getStencilSize() { + return nGetStencilSize(); + } + + private static native int nGetStencilSize(); + + /////////////////////////////////////////////////////////////////////////// + // Functor + /////////////////////////////////////////////////////////////////////////// + @Override public boolean callDrawGLFunction(int drawGLFunction) { return nCallDrawGLFunction(mRenderer, drawGLFunction); @@ -275,7 +293,6 @@ class GLES20Canvas extends HardwareCanvas { private static native boolean nCallDrawGLFunction(int renderer, int drawGLFunction); - /////////////////////////////////////////////////////////////////////////// // Memory /////////////////////////////////////////////////////////////////////////// @@ -361,6 +378,12 @@ class GLES20Canvas extends HardwareCanvas { private static native int nGetDisplayListSize(int displayList); + static void setDisplayListName(int displayList, String name) { + nSetDisplayListName(displayList, name); + } + + private static native void nSetDisplayListName(int displayList, String name); + @Override public boolean drawDisplayList(DisplayList displayList, int width, int height, Rect dirty) { return nDrawDisplayList(mRenderer, diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java index 0cb944957130..969c9ab0710f 100644 --- a/core/java/android/view/GLES20DisplayList.java +++ b/core/java/android/view/GLES20DisplayList.java @@ -31,10 +31,17 @@ class GLES20DisplayList extends DisplayList { private GLES20RecordingCanvas mCanvas; private boolean mValid; + // Used for debugging + private final String mName; + // The native display list will be destroyed when this object dies. // DO NOT overwrite this reference once it is set. private DisplayListFinalizer mFinalizer; + GLES20DisplayList(String name) { + mName = name; + } + int getNativeDisplayList() { if (!mValid || mFinalizer == null) { throw new IllegalStateException("The display list is not valid."); @@ -75,6 +82,7 @@ class GLES20DisplayList extends DisplayList { mCanvas.end(mFinalizer.mNativeDisplayList); } else { mFinalizer = new DisplayListFinalizer(mCanvas.end(0)); + GLES20Canvas.setDisplayListName(mFinalizer.mNativeDisplayList, mName); } mCanvas.recycle(); mCanvas = null; diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 1c9cbbfea53b..9e8a228edfa1 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -283,9 +283,12 @@ public abstract class HardwareRenderer { * Creates a new display list that can be used to record batches of * drawing operations. * + * @param name The name of the display list, used for debugging purpose. + * May be null + * * @return A new display list. */ - public abstract DisplayList createDisplayList(); + public abstract DisplayList createDisplayList(String name); /** * Creates a new hardware layer. A hardware layer built by calling this @@ -1047,7 +1050,7 @@ public abstract class HardwareRenderer { EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 0, - EGL_STENCIL_SIZE, 0, + EGL_STENCIL_SIZE, GLES20Canvas.getStencilSize(), EGL_SURFACE_TYPE, EGL_WINDOW_BIT | (dirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0), EGL_NONE @@ -1094,8 +1097,8 @@ public abstract class HardwareRenderer { } @Override - public DisplayList createDisplayList() { - return new GLES20DisplayList(); + public DisplayList createDisplayList(String name) { + return new GLES20DisplayList(name); } @Override diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 39f603d12e19..8597017a6498 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -3770,6 +3770,14 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } if ((mPrivateFlags & FOCUSED) != 0) { + // If this is the first focusable do not clear focus since the we + // try to give it focus every time a view clears its focus. Hence, + // the view that would gain focus already has it. + View firstFocusable = getFirstFocusable(); + if (firstFocusable == this) { + return; + } + mPrivateFlags &= ~FOCUSED; if (mParent != null) { @@ -3778,9 +3786,24 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal onFocusChanged(false, 0, null); refreshDrawableState(); + + // The view cleared focus and invoked the callbacks, so now is the + // time to give focus to the the first focusable to ensure that the + // gain focus is announced after clear focus. + if (firstFocusable != null) { + firstFocusable.requestFocus(FOCUS_FORWARD); + } } } + private View getFirstFocusable() { + ViewRootImpl viewRoot = getViewRootImpl(); + if (viewRoot != null) { + return viewRoot.focusSearch(null, FOCUS_FORWARD); + } + return null; + } + /** * Called to clear the focus of a view that is about to be removed. * Doesn't call clearChildFocus, which prevents this view from taking @@ -10374,7 +10397,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal // we copy in child display lists into ours in drawChild() mRecreateDisplayList = true; if (mDisplayList == null) { - mDisplayList = mAttachInfo.mHardwareRenderer.createDisplayList(); + final String name = getClass().getSimpleName(); + mDisplayList = mAttachInfo.mHardwareRenderer.createDisplayList(name); // If we're creating a new display list, make sure our parent gets invalidated // since they will need to recreate their display list to account for this // new child display list. diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index d906a16fbd2e..75598620a362 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -675,11 +675,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ @Override public void clearFocus() { - super.clearFocus(); - - // clear any child focus if it exists - if (mFocused != null) { + if (DBG) { + System.out.println(this + " clearFocus()"); + } + if (mFocused == null) { + super.clearFocus(); + } else { mFocused.clearFocus(); + mFocused = null; } } @@ -691,12 +694,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (DBG) { System.out.println(this + " unFocus()"); } - - super.unFocus(); - if (mFocused != null) { + if (mFocused == null) { + super.unFocus(); + } else { mFocused.unFocus(); + mFocused = null; } - mFocused = null; } /** diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java index 89a1ef26918d..0fdcd0f2d879 100644 --- a/core/java/android/view/ViewPropertyAnimator.java +++ b/core/java/android/view/ViewPropertyAnimator.java @@ -113,6 +113,10 @@ public class ViewPropertyAnimator { * on that list are added to the list of properties associated with that animator. */ ArrayList<NameValuesHolder> mPendingAnimations = new ArrayList<NameValuesHolder>(); + private Runnable mPendingSetupAction; + private Runnable mPendingCleanupAction; + private Runnable mPendingOnStartAction; + private Runnable mPendingOnEndAction; /** * Constants used to associate a property being requested and the mechanism used to set @@ -199,6 +203,10 @@ public class ViewPropertyAnimator { */ private HashMap<Animator, PropertyBundle> mAnimatorMap = new HashMap<Animator, PropertyBundle>(); + private HashMap<Animator, Runnable> mAnimatorSetupMap; + private HashMap<Animator, Runnable> mAnimatorCleanupMap; + private HashMap<Animator, Runnable> mAnimatorOnStartMap; + private HashMap<Animator, Runnable> mAnimatorOnEndMap; /** * This is the information we need to set each property during the animation. @@ -614,6 +622,93 @@ public class ViewPropertyAnimator { } /** + * The View associated with this ViewPropertyAnimator will have its + * {@link View#setLayerType(int, android.graphics.Paint) layer type} set to + * {@link View#LAYER_TYPE_HARDWARE} for the duration of the next animation. This state + * is not persistent, either on the View or on this ViewPropertyAnimator: the layer type + * of the View will be restored when the animation ends to what it was when this method was + * called, and this setting on ViewPropertyAnimator is only valid for the next animation. + * Note that calling this method and then independently setting the layer type of the View + * (by a direct call to {@link View#setLayerType(int, android.graphics.Paint)}) will result + * in some inconsistency, including having the layer type restored to its pre-withLayer() + * value when the animation ends. + * + * @see View#setLayerType(int, android.graphics.Paint) + * @return This object, allowing calls to methods in this class to be chained. + */ + public ViewPropertyAnimator withLayer() { + mPendingSetupAction= new Runnable() { + @Override + public void run() { + mView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + } + }; + final int currentLayerType = mView.getLayerType(); + mPendingCleanupAction = new Runnable() { + @Override + public void run() { + mView.setLayerType(currentLayerType, null); + } + }; + if (mAnimatorSetupMap == null) { + mAnimatorSetupMap = new HashMap<Animator, Runnable>(); + } + if (mAnimatorCleanupMap == null) { + mAnimatorCleanupMap = new HashMap<Animator, Runnable>(); + } + + return this; + } + + /** + * Specifies an action to take place when the next animation runs. If there is a + * {@link #setStartDelay(long) startDelay} set on this ViewPropertyAnimator, then the + * action will run after that startDelay expires, when the actual animation begins. + * This method, along with {@link #withEndAction(Runnable)}, is intended to help facilitate + * choreographing ViewPropertyAnimator animations with other animations or actions + * in the application. + * + * @param runnable The action to run when the next animation starts. + * @return This object, allowing calls to methods in this class to be chained. + */ + public ViewPropertyAnimator withStartAction(Runnable runnable) { + mPendingOnStartAction = runnable; + if (runnable != null && mAnimatorOnStartMap == null) { + mAnimatorOnStartMap = new HashMap<Animator, Runnable>(); + } + return this; + } + + /** + * Specifies an action to take place when the next animation ends. The action is only + * run if the animation ends normally; if the ViewPropertyAnimator is canceled during + * that animation, the runnable will not run. + * This method, along with {@link #withStartAction(Runnable)}, is intended to help facilitate + * choreographing ViewPropertyAnimator animations with other animations or actions + * in the application. + * + * <p>For example, the following code animates a view to x=200 and then back to 0:</p> + * <pre> + * Runnable endAction = new Runnable() { + * public void run() { + * view.animate().x(0); + * } + * }; + * view.animate().x(200).onEnd(endAction); + * </pre> + * + * @param runnable The action to run when the next animation ends. + * @return This object, allowing calls to methods in this class to be chained. + */ + public ViewPropertyAnimator withEndAction(Runnable runnable) { + mPendingOnEndAction = runnable; + if (runnable != null && mAnimatorOnEndMap == null) { + mAnimatorOnEndMap = new HashMap<Animator, Runnable>(); + } + return this; + } + + /** * Starts the underlying Animator for a set of properties. We use a single animator that * simply runs from 0 to 1, and then use that fractional value to set each property * value accordingly. @@ -630,6 +725,22 @@ public class ViewPropertyAnimator { propertyMask |= nameValuesHolder.mNameConstant; } mAnimatorMap.put(animator, new PropertyBundle(propertyMask, nameValueList)); + if (mPendingSetupAction != null) { + mAnimatorSetupMap.put(animator, mPendingSetupAction); + mPendingSetupAction = null; + } + if (mPendingCleanupAction != null) { + mAnimatorCleanupMap.put(animator, mPendingCleanupAction); + mPendingCleanupAction = null; + } + if (mPendingOnStartAction != null) { + mAnimatorOnStartMap.put(animator, mPendingOnStartAction); + mPendingOnStartAction = null; + } + if (mPendingOnEndAction != null) { + mAnimatorOnEndMap.put(animator, mPendingOnEndAction); + mPendingOnEndAction = null; + } animator.addUpdateListener(mAnimatorEventListener); animator.addListener(mAnimatorEventListener); if (mStartDelaySet) { @@ -800,6 +911,20 @@ public class ViewPropertyAnimator { implements Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener { @Override public void onAnimationStart(Animator animation) { + if (mAnimatorSetupMap != null) { + Runnable r = mAnimatorSetupMap.get(animation); + if (r != null) { + r.run(); + } + mAnimatorSetupMap.remove(animation); + } + if (mAnimatorOnStartMap != null) { + Runnable r = mAnimatorOnStartMap.get(animation); + if (r != null) { + r.run(); + } + mAnimatorOnStartMap.remove(animation); + } if (mListener != null) { mListener.onAnimationStart(animation); } @@ -810,6 +935,9 @@ public class ViewPropertyAnimator { if (mListener != null) { mListener.onAnimationCancel(animation); } + if (mAnimatorOnEndMap != null) { + mAnimatorOnEndMap.remove(animation); + } } @Override @@ -824,6 +952,20 @@ public class ViewPropertyAnimator { if (mListener != null) { mListener.onAnimationEnd(animation); } + if (mAnimatorOnEndMap != null) { + Runnable r = mAnimatorOnEndMap.get(animation); + if (r != null) { + r.run(); + } + mAnimatorOnEndMap.remove(animation); + } + if (mAnimatorCleanupMap != null) { + Runnable r = mAnimatorCleanupMap.get(animation); + if (r != null) { + r.run(); + } + mAnimatorCleanupMap.remove(animation); + } mAnimatorMap.remove(animation); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 1a4bdf4bc373..2ef843ba5e17 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -168,6 +168,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, View mView; View mFocusedView; View mRealFocusedView; // this is not set to null in touch mode + View mOldFocusedView; int mViewVisibility; boolean mAppVisible = true; int mOrigWindowType = -1; @@ -2226,32 +2227,33 @@ public final class ViewRootImpl extends Handler implements ViewParent, public void requestChildFocus(View child, View focused) { checkThread(); - if (mFocusedView != focused) { - mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mFocusedView, focused); - scheduleTraversals(); + + if (DEBUG_INPUT_RESIZE) { + Log.v(TAG, "Request child focus: focus now " + focused); } + + mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mOldFocusedView, focused); + scheduleTraversals(); + mFocusedView = mRealFocusedView = focused; - if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Request child focus: focus now " - + mFocusedView); } public void clearChildFocus(View child) { checkThread(); - View oldFocus = mFocusedView; + if (DEBUG_INPUT_RESIZE) { + Log.v(TAG, "Clearing child focus"); + } + + mOldFocusedView = mFocusedView; - if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Clearing child focus"); - mFocusedView = mRealFocusedView = null; - if (mView != null && !mView.hasFocus()) { - // If a view gets the focus, the listener will be invoked from requestChildFocus() - if (!mView.requestFocus(View.FOCUS_FORWARD)) { - mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null); - } - } else if (oldFocus != null) { - mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null); + // Invoke the listener only if there is no view to take focus + if (focusSearch(null, View.FOCUS_FORWARD) == null) { + mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mOldFocusedView, null); } - } + mFocusedView = mRealFocusedView = null; + } public void focusableViewAvailable(View v) { checkThread(); @@ -2724,6 +2726,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, mView.unFocus(); mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null); mFocusedView = null; + mOldFocusedView = null; return true; } } diff --git a/core/java/android/webkit/WebCoreThreadWatchdog.java b/core/java/android/webkit/WebCoreThreadWatchdog.java index d100260c0616..0541d5db4157 100644 --- a/core/java/android/webkit/WebCoreThreadWatchdog.java +++ b/core/java/android/webkit/WebCoreThreadWatchdog.java @@ -40,9 +40,6 @@ class WebCoreThreadWatchdog implements Runnable { // WebCore thread unresponsive. private static final int TIMED_OUT = 101; - // Message to tell the Watchdog thread to terminate. - private static final int QUIT = 102; - // Wait 10s after hearing back from the WebCore thread before checking it's still alive. private static final int HEARTBEAT_PERIOD = 10 * 1000; @@ -57,7 +54,6 @@ class WebCoreThreadWatchdog implements Runnable { private Handler mWebCoreThreadHandler; private Handler mHandler; private boolean mPaused; - private boolean mPendingQuit; private static WebCoreThreadWatchdog sInstance; @@ -88,12 +84,6 @@ class WebCoreThreadWatchdog implements Runnable { } } - public synchronized static void quit() { - if (sInstance != null) { - sInstance.quitWatchdog(); - } - } - private void setContext(Context context) { mContext = context; } @@ -103,19 +93,6 @@ class WebCoreThreadWatchdog implements Runnable { mWebCoreThreadHandler = webCoreThreadHandler; } - private void quitWatchdog() { - if (mHandler == null) { - // The thread hasn't started yet, so set a flag to stop it starting. - mPendingQuit = true; - return; - } - // Clear any pending messages, and then post a quit to the WatchDog handler. - mHandler.removeMessages(TIMED_OUT); - mHandler.removeMessages(IS_ALIVE); - mWebCoreThreadHandler.removeMessages(EventHub.HEARTBEAT); - mHandler.obtainMessage(QUIT).sendToTarget(); - } - private void pauseWatchdog() { mPaused = true; @@ -146,12 +123,8 @@ class WebCoreThreadWatchdog implements Runnable { mHandler.sendMessageDelayed(mHandler.obtainMessage(TIMED_OUT), TIMEOUT_PERIOD); } - private boolean createHandler() { + private void createHandler() { synchronized (WebCoreThreadWatchdog.class) { - if (mPendingQuit) { - return false; - } - mHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -206,15 +179,9 @@ class WebCoreThreadWatchdog implements Runnable { .setIcon(android.R.drawable.ic_dialog_alert) .show(); break; - - case QUIT: - Looper.myLooper().quit(); - break; } } }; - - return true; } } @@ -222,9 +189,7 @@ class WebCoreThreadWatchdog implements Runnable { public void run() { Looper.prepare(); - if (!createHandler()) { - return; - } + createHandler(); // Send the initial control to WebViewCore and start the timeout timer as long as we aren't // paused. diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index cc8eef26055d..2c2cf644f775 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -789,12 +789,18 @@ public class WebView extends AbsoluteLayout // know to handle Shift and arrows natively first private boolean mAccessibilityScriptInjected; - static final boolean USE_JAVA_TEXT_SELECTION = true; - static final boolean DEBUG_TEXT_HANDLES = false; - private Region mTextSelectionRegion = new Region(); - private Paint mTextSelectionPaint; private Drawable mSelectHandleLeft; private Drawable mSelectHandleRight; + private Rect mSelectCursorBase = new Rect(); + private int mSelectCursorBaseLayerId; + private Rect mSelectCursorExtent = new Rect(); + private int mSelectCursorExtentLayerId; + private Rect mSelectDraggingCursor; + private Point mSelectDraggingOffset = new Point(); + static final int HANDLE_ID_START = 0; + static final int HANDLE_ID_END = 1; + static final int HANDLE_ID_BASE = 2; + static final int HANDLE_ID_EXTENT = 3; static boolean sDisableNavcache = false; // the color used to highlight the touch rectangles @@ -2656,12 +2662,6 @@ public class WebView extends AbsoluteLayout return mZoomManager.getScale(); } - // Called by JNI. Returns the scale to apply to the text selection handles - /* package */ float getTextHandleScale() { - float density = mContext.getResources().getDisplayMetrics().density; - return density / getScale(); - } - /** * Compute the reading level scale of the WebView * @param scale The current scale. @@ -3852,6 +3852,16 @@ public class WebView extends AbsoluteLayout if (x == mScrollingLayerRect.left && y == mScrollingLayerRect.top) { return; } + if (mSelectingText) { + int dx = mScrollingLayerRect.left - x; + int dy = mScrollingLayerRect.top - y; + if (mSelectCursorBaseLayerId == mCurrentScrollingLayerId) { + mSelectCursorBase.offset(dx, dy); + } + if (mSelectCursorExtentLayerId == mCurrentScrollingLayerId) { + mSelectCursorExtent.offset(dx, dy); + } + } nativeScrollLayer(mCurrentScrollingLayerId, x, y); mScrollingLayerRect.left = x; mScrollingLayerRect.top = y; @@ -4624,12 +4634,7 @@ public class WebView extends AbsoluteLayout * Select the word at the indicated content coordinates. */ boolean selectText(int x, int y) { - if (!setUpSelect(true, x, y)) { - return false; - } - nativeSetExtendSelection(); - mDrawSelectionPointer = false; - mTouchMode = TOUCH_DRAG_MODE; + mWebViewCore.sendMessage(EventHub.SELECT_WORD_AT, x, y); return true; } @@ -4830,11 +4835,8 @@ public class WebView extends AbsoluteLayout int extras = DRAW_EXTRAS_NONE; if (mFindIsUp) { extras = DRAW_EXTRAS_FIND; - } else if (mSelectingText && (!USE_JAVA_TEXT_SELECTION || DEBUG_TEXT_HANDLES)) { + } else if (mSelectingText) { extras = DRAW_EXTRAS_SELECTION; - nativeSetSelectionPointer(mNativeClass, - mDrawSelectionPointer, - mZoomManager.getInvScale(), mSelectX, mSelectY - getTitleHeight()); } else if (drawCursorRing) { extras = DRAW_EXTRAS_CURSOR_RING; } @@ -4879,7 +4881,7 @@ public class WebView extends AbsoluteLayout } canvas.restoreToCount(saveCount); - if (mSelectingText && USE_JAVA_TEXT_SELECTION) { + if (mSelectingText) { drawTextSelectionHandles(canvas); } @@ -4901,30 +4903,12 @@ public class WebView extends AbsoluteLayout } private void drawTextSelectionHandles(Canvas canvas) { - if (mTextSelectionPaint == null) { - mTextSelectionPaint = new Paint(); - mTextSelectionPaint.setColor(HIGHLIGHT_COLOR); - } - mTextSelectionRegion.setEmpty(); - nativeGetTextSelectionRegion(mNativeClass, mTextSelectionRegion); - Rect r = new Rect(); - RegionIterator iter = new RegionIterator(mTextSelectionRegion); - Rect clip = canvas.getClipBounds(); - while (iter.next(r)) { - r.set(contentToViewDimension(r.left), - contentToViewDimension(r.top), - contentToViewDimension(r.right), - contentToViewDimension(r.bottom)); - if (r.intersect(clip)) { - canvas.drawRect(r, mTextSelectionPaint); - } - } if (mSelectHandleLeft == null) { mSelectHandleLeft = mContext.getResources().getDrawable( com.android.internal.R.drawable.text_select_handle_left); } int[] handles = new int[4]; - nativeGetSelectionHandles(mNativeClass, handles); + getSelectionHandles(handles); int start_x = contentToViewDimension(handles[0]); int start_y = contentToViewDimension(handles[1]); int end_x = contentToViewDimension(handles[2]); @@ -4942,14 +4926,31 @@ public class WebView extends AbsoluteLayout mSelectHandleRight.setBounds(end_x, end_y, end_x + mSelectHandleRight.getIntrinsicWidth(), end_y + mSelectHandleRight.getIntrinsicHeight()); - if (DEBUG_TEXT_HANDLES) { - mSelectHandleLeft.setAlpha(125); - mSelectHandleRight.setAlpha(125); - } mSelectHandleLeft.draw(canvas); mSelectHandleRight.draw(canvas); } + /** + * Takes an int[4] array as an output param with the values being + * startX, startY, endX, endY + */ + private void getSelectionHandles(int[] handles) { + handles[0] = mSelectCursorBase.right; + handles[1] = mSelectCursorBase.bottom - + (mSelectCursorBase.height() / 4); + handles[2] = mSelectCursorExtent.left; + handles[3] = mSelectCursorExtent.bottom + - (mSelectCursorExtent.height() / 4); + if (!nativeIsBaseFirst(mNativeClass)) { + int swap = handles[0]; + handles[0] = handles[2]; + handles[2] = swap; + swap = handles[1]; + handles[1] = handles[3]; + handles[3] = swap; + } + } + // draw history private boolean mDrawHistory = false; private Picture mHistoryPicture = null; @@ -5009,7 +5010,7 @@ public class WebView extends AbsoluteLayout /* package */ void deleteSelection(int start, int end) { mTextGeneration++; WebViewCore.TextSelectionData data - = new WebViewCore.TextSelectionData(start, end); + = new WebViewCore.TextSelectionData(start, end, 0); mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, mTextGeneration, 0, data); } @@ -5462,15 +5463,6 @@ public class WebView extends AbsoluteLayout return pinScrollTo(mContentWidth, mScrollY, true, 0); } } - if (mSelectingText) { - int xRate = keyCode == KeyEvent.KEYCODE_DPAD_LEFT - ? -1 : keyCode == KeyEvent.KEYCODE_DPAD_RIGHT ? 1 : 0; - int yRate = keyCode == KeyEvent.KEYCODE_DPAD_UP ? - -1 : keyCode == KeyEvent.KEYCODE_DPAD_DOWN ? 1 : 0; - int multiplier = event.getRepeatCount() + 1; - moveSelection(xRate * multiplier, yRate * multiplier); - return true; - } if (navHandledKey(keyCode, 1, false, event.getEventTime())) { playSoundEffect(keyCodeToSoundsEffect(keyCode)); return true; @@ -5623,14 +5615,8 @@ public class WebView extends AbsoluteLayout mGotCenterDown = false; if (mSelectingText) { - if (mExtendSelection) { - copySelection(); - selectionDone(); - } else { - mExtendSelection = true; - nativeSetExtendSelection(); - invalidate(); // draw the i-beam instead of the arrow - } + copySelection(); + selectionDone(); return true; // discard press if copy in progress } @@ -5676,21 +5662,7 @@ public class WebView extends AbsoluteLayout return false; } - /* - * Enter selecting text mode, and see if CAB should be shown. - * Returns true if the WebView is now in - * selecting text mode (including if it was already in that mode, and this - * method did nothing). - */ - private boolean setUpSelect(boolean selectWord, int x, int y) { - if (0 == mNativeClass) return false; // client isn't initialized - if (inFullScreenMode()) return false; - if (mSelectingText) return true; - nativeResetSelection(); - if (selectWord && !nativeWordSelection(x, y)) { - selectionDone(); - return false; - } + private boolean startSelectActionMode() { mSelectCallback = new SelectActionModeCallback(); mSelectCallback.setWebView(this); if (startActionMode(mSelectCallback) == null) { @@ -5699,52 +5671,41 @@ public class WebView extends AbsoluteLayout selectionDone(); return false; } - mExtendSelection = false; - mSelectingText = mDrawSelectionPointer = true; - if (DEBUG_TEXT_HANDLES) { - // Debugging text handles requires running in software mode - setLayerType(LAYER_TYPE_SOFTWARE, null); + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + return true; + } + + private void syncSelectionCursors() { + mSelectCursorBaseLayerId = + nativeGetHandleLayerId(mNativeClass, HANDLE_ID_BASE, mSelectCursorBase); + mSelectCursorExtentLayerId = + nativeGetHandleLayerId(mNativeClass, HANDLE_ID_EXTENT, mSelectCursorExtent); + } + + private boolean setupWebkitSelect() { + syncSelectionCursors(); + if (!startSelectActionMode()) { + selectionDone(); + return false; } - // don't let the picture change during text selection - WebViewCore.pauseUpdatePicture(mWebViewCore); - if (nativeHasCursorNode()) { - Rect rect = nativeCursorNodeBounds(); - mSelectX = contentToViewX(rect.left); - mSelectY = contentToViewY(rect.top); - } else if (mLastTouchY > getVisibleTitleHeightImpl()) { - mSelectX = mScrollX + mLastTouchX; - mSelectY = mScrollY + mLastTouchY; + mSelectingText = true; + mTouchMode = TOUCH_DRAG_MODE; + return true; + } + + private void updateWebkitSelection() { + int[] handles = null; + if (mSelectingText) { + handles = new int[4]; + handles[0] = mSelectCursorBase.centerX(); + handles[1] = mSelectCursorBase.centerY(); + handles[2] = mSelectCursorExtent.centerX(); + handles[3] = mSelectCursorExtent.centerY(); } else { - mSelectX = mScrollX + getViewWidth() / 2; - mSelectY = mScrollY + getViewHeightWithTitle() / 2; + nativeSetTextSelection(mNativeClass, 0); } - nativeHideCursor(); - mMinAutoScrollX = 0; - mMaxAutoScrollX = getViewWidth(); - mMinAutoScrollY = 0; - mMaxAutoScrollY = getViewHeightWithTitle(); - mCurrentScrollingLayerId = nativeScrollableLayer(viewToContentX(mSelectX), - viewToContentY(mSelectY), mScrollingLayerRect, - mScrollingLayerBounds); - if (mCurrentScrollingLayerId != 0) { - if (mScrollingLayerRect.left != mScrollingLayerRect.right) { - mMinAutoScrollX = Math.max(mMinAutoScrollX, - contentToViewX(mScrollingLayerBounds.left)); - mMaxAutoScrollX = Math.min(mMaxAutoScrollX, - contentToViewX(mScrollingLayerBounds.right)); - } - if (mScrollingLayerRect.top != mScrollingLayerRect.bottom) { - mMinAutoScrollY = Math.max(mMinAutoScrollY, - contentToViewY(mScrollingLayerBounds.top)); - mMaxAutoScrollY = Math.min(mMaxAutoScrollY, - contentToViewY(mScrollingLayerBounds.bottom)); - } - } - mMinAutoScrollX += SELECT_SCROLL; - mMaxAutoScrollX -= SELECT_SCROLL; - mMinAutoScrollY += SELECT_SCROLL; - mMaxAutoScrollY -= SELECT_SCROLL; - return true; + mWebViewCore.removeMessages(EventHub.SELECT_TEXT); + mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SELECT_TEXT, handles); } /** @@ -5755,7 +5716,6 @@ public class WebView extends AbsoluteLayout @Deprecated public void emulateShiftHeld() { checkThread(); - setUpSelect(false, 0, 0); } /** @@ -5764,17 +5724,7 @@ public class WebView extends AbsoluteLayout * @hide This is an implementation detail. */ public void selectAll() { - if (0 == mNativeClass) return; // client isn't initialized - if (inFullScreenMode()) return; - if (!mSelectingText) { - // retrieve a point somewhere within the text - Point select = nativeSelectableText(); - if (!selectText(select.x, select.y)) return; - } - nativeSelectAll(); - mDrawSelectionPointer = false; - mExtendSelection = true; - invalidate(); + mWebViewCore.sendMessage(EventHub.SELECT_ALL); } /** @@ -5783,17 +5733,11 @@ public class WebView extends AbsoluteLayout void selectionDone() { if (mSelectingText) { mSelectingText = false; - if (DEBUG_TEXT_HANDLES) { - // Debugging text handles required running in software mode, set - // back to default now - setLayerType(LAYER_TYPE_NONE, null); - } // finish is idempotent, so this is fine even if selectionDone was // called by mSelectCallback.onDestroyActionMode mSelectCallback.finish(); mSelectCallback = null; - WebViewCore.resumePriority(); - WebViewCore.resumeUpdatePicture(mWebViewCore); + updateWebkitSelection(); invalidate(); // redraw without selection mAutoScrollX = 0; mAutoScrollY = 0; @@ -5821,7 +5765,7 @@ public class WebView extends AbsoluteLayout .getSystemService(Context.CLIPBOARD_SERVICE); cm.setText(selection); int[] handles = new int[4]; - nativeGetSelectionHandles(mNativeClass, handles); + getSelectionHandles(handles); mWebViewCore.sendMessage(EventHub.COPY_TEXT, handles); } invalidate(); // remove selection region and pointer @@ -5836,7 +5780,7 @@ public class WebView extends AbsoluteLayout public void cutSelection() { copySelection(); int[] handles = new int[4]; - nativeGetSelectionHandles(mNativeClass, handles); + getSelectionHandles(handles); mWebViewCore.sendMessage(EventHub.DELETE_TEXT, handles); } @@ -5854,7 +5798,7 @@ public class WebView extends AbsoluteLayout CharSequence pasteText = clipItem.getText(); if (pasteText != null) { int[] handles = new int[4]; - nativeGetSelectionHandles(mNativeClass, handles); + getSelectionHandles(handles); mWebViewCore.sendMessage(EventHub.DELETE_TEXT, handles); mWebViewCore.sendMessage(EventHub.INSERT_TEXT, pasteText.toString()); @@ -6407,12 +6351,26 @@ public class WebView extends AbsoluteLayout (eventTime - mLastTouchUpTime), eventTime); } if (mSelectingText) { - mDrawSelectionPointer = false; - mSelectionStarted = nativeStartSelection(contentX, contentY); + mSelectionStarted = false; + int shiftedY = y - getTitleHeight() + mScrollY; + int shiftedX = x + mScrollX; + if (mSelectHandleLeft.getBounds() + .contains(shiftedX, shiftedY)) { + mSelectionStarted = true; + mSelectDraggingCursor = mSelectCursorBase; + } else if (mSelectHandleRight.getBounds() + .contains(shiftedX, shiftedY)) { + mSelectionStarted = true; + mSelectDraggingCursor = mSelectCursorExtent; + } + if (mSelectDraggingCursor != null) { + mSelectDraggingOffset.set( + mSelectDraggingCursor.left - contentX, + mSelectDraggingCursor.top - contentY); + } if (DebugFlags.WEB_VIEW) { Log.v(LOGTAG, "select=" + contentX + "," + contentY); } - invalidate(); } } // Trigger the link @@ -6478,6 +6436,26 @@ public class WebView extends AbsoluteLayout removeTouchHighlight(); } } + if (mSelectingText && mSelectionStarted) { + if (DebugFlags.WEB_VIEW) { + Log.v(LOGTAG, "extend=" + contentX + "," + contentY); + } + ViewParent parent = getParent(); + if (parent != null) { + parent.requestDisallowInterceptTouchEvent(true); + } + if (deltaX != 0 || deltaY != 0) { + mSelectDraggingCursor.offsetTo( + contentX + mSelectDraggingOffset.x, + contentY + mSelectDraggingOffset.y); + updateWebkitSelection(); + mLastTouchX = x; + mLastTouchY = y; + invalidate(); + } + break; + } + // pass the touch events from UI thread to WebCore thread if (shouldForwardTouchEvent() && mConfirmMove && (firstMove || eventTime - mLastSentTouchTime > mCurrentTouchInterval)) { @@ -6520,30 +6498,6 @@ public class WebView extends AbsoluteLayout } else { mVelocityTracker.addMovement(ev); } - if (mSelectingText && mSelectionStarted) { - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "extend=" + contentX + "," + contentY); - } - ViewParent parent = getParent(); - if (parent != null) { - parent.requestDisallowInterceptTouchEvent(true); - } - mAutoScrollX = x <= mMinAutoScrollX ? -SELECT_SCROLL - : x >= mMaxAutoScrollX ? SELECT_SCROLL : 0; - mAutoScrollY = y <= mMinAutoScrollY ? -SELECT_SCROLL - : y >= mMaxAutoScrollY ? SELECT_SCROLL : 0; - if ((mAutoScrollX != 0 || mAutoScrollY != 0) - && !mSentAutoScrollMessage) { - mSentAutoScrollMessage = true; - mPrivateHandler.sendEmptyMessageDelayed( - SCROLL_SELECT_TEXT, SELECT_SCROLL_INTERVAL); - } - if (deltaX != 0 || deltaY != 0) { - nativeExtendSelection(contentX, contentY); - invalidate(); - } - break; - } if (mTouchMode != TOUCH_DRAG_MODE && mTouchMode != TOUCH_DRAG_LAYER_MODE) { @@ -6758,7 +6712,7 @@ public class WebView extends AbsoluteLayout } else { if (mSelectingText) { // tapping on selection or controls does nothing - if (!nativeHitSelection(contentX, contentY)) { + if (!mSelectionStarted) { selectionDone(); } break; @@ -7055,6 +7009,12 @@ public class WebView extends AbsoluteLayout if (mOverScrollGlow != null) { mOverScrollGlow.releaseAll(); } + + if (mSelectingText) { + mSelectionStarted = false; + syncSelectionCursors(); + invalidate(); + } } private void cancelTouch() { @@ -7119,8 +7079,6 @@ public class WebView extends AbsoluteLayout private int mTrackballYMove = 0; private boolean mSelectingText = false; private boolean mSelectionStarted = false; - private boolean mExtendSelection = false; - private boolean mDrawSelectionPointer = false; private static final int TRACKBALL_KEY_TIMEOUT = 1000; private static final int TRACKBALL_TIMEOUT = 200; private static final int TRACKBALL_WAIT = 100; @@ -7189,14 +7147,8 @@ public class WebView extends AbsoluteLayout mTrackballDown = false; mTrackballUpTime = time; if (mSelectingText) { - if (mExtendSelection) { - copySelection(); - selectionDone(); - } else { - mExtendSelection = true; - nativeSetExtendSelection(); - invalidate(); // draw the i-beam instead of the arrow - } + copySelection(); + selectionDone(); return true; // discard press if copy in progress } if (DebugFlags.WEB_VIEW) { @@ -7239,42 +7191,6 @@ public class WebView extends AbsoluteLayout return true; } - void moveSelection(float xRate, float yRate) { - if (mNativeClass == 0) - return; - int width = getViewWidth(); - int height = getViewHeight(); - mSelectX += xRate; - mSelectY += yRate; - int maxX = width + mScrollX; - int maxY = height + mScrollY; - mSelectX = Math.min(maxX, Math.max(mScrollX - SELECT_CURSOR_OFFSET - , mSelectX)); - mSelectY = Math.min(maxY, Math.max(mScrollY - SELECT_CURSOR_OFFSET - , mSelectY)); - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "moveSelection" - + " mSelectX=" + mSelectX - + " mSelectY=" + mSelectY - + " mScrollX=" + mScrollX - + " mScrollY=" + mScrollY - + " xRate=" + xRate - + " yRate=" + yRate - ); - } - nativeMoveSelection(viewToContentX(mSelectX), viewToContentY(mSelectY)); - int scrollX = mSelectX < mScrollX ? -SELECT_CURSOR_OFFSET - : mSelectX > maxX - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET - : 0; - int scrollY = mSelectY < mScrollY ? -SELECT_CURSOR_OFFSET - : mSelectY > maxY - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET - : 0; - pinScrollBy(scrollX, scrollY, true, 0); - Rect select = new Rect(mSelectX, mSelectY, mSelectX + 1, mSelectY + 1); - requestRectangleOnScreen(select); - invalidate(); - } - private int scaleTrackballX(float xRate, int width) { int xMove = (int) (xRate / TRACKBALL_SCALE * width); int nextXMove = xMove; @@ -7328,21 +7244,6 @@ public class WebView extends AbsoluteLayout float yRate = mTrackballRemainsY * 1000 / elapsed; int viewWidth = getViewWidth(); int viewHeight = getViewHeight(); - if (mSelectingText) { - if (!mDrawSelectionPointer) { - // The last selection was made by touch, disabling drawing the - // selection pointer. Allow the trackball to adjust the - // position of the touch control. - mSelectX = contentToViewX(nativeSelectionX()); - mSelectY = contentToViewY(nativeSelectionY()); - mDrawSelectionPointer = mExtendSelection = true; - nativeSetExtendSelection(); - } - moveSelection(scaleTrackballX(xRate, viewWidth), - scaleTrackballY(yRate, viewHeight)); - mTrackballRemainsX = mTrackballRemainsY = 0; - return; - } float ax = Math.abs(xRate); float ay = Math.abs(yRate); float maxA = Math.max(ax, ay); @@ -9204,6 +9105,18 @@ public class WebView extends AbsoluteLayout mInputConnection.setSelection(data.mStart, data.mEnd); } } + + nativeSetTextSelection(mNativeClass, data.mSelectTextPtr); + if (data.mSelectTextPtr != 0) { + if (!mSelectingText) { + setupWebkitSelect(); + } else if (!mSelectionStarted) { + syncSelectionCursors(); + } + } else { + selectionDone(); + } + invalidate(); } // Class used to use a dropdown for a <select> element @@ -9952,7 +9865,6 @@ public class WebView extends AbsoluteLayout private native boolean nativeMoveCursor(int keyCode, int count, boolean noScroll); private native int nativeMoveGeneration(); - private native void nativeMoveSelection(int x, int y); /** * @return true if the page should get the shift and arrow keys, rather * than select text/navigation. @@ -9962,15 +9874,8 @@ public class WebView extends AbsoluteLayout */ private native boolean nativePageShouldHandleShiftAndArrows(); private native boolean nativePointInNavCache(int x, int y, int slop); - // Like many other of our native methods, you must make sure that - // mNativeClass is not null before calling this method. - private native void nativeResetSelection(); - private native Point nativeSelectableText(); - private native void nativeSelectAll(); private native void nativeSelectBestAt(Rect rect); private native void nativeSelectAt(int x, int y); - private native int nativeSelectionX(); - private native int nativeSelectionY(); private native int nativeFindIndex(); private native void nativeSetExtendSelection(); private native void nativeSetFindIsEmpty(); @@ -10026,12 +9931,14 @@ public class WebView extends AbsoluteLayout private native int nativeGetBackgroundColor(); native boolean nativeSetProperty(String key, String value); native String nativeGetProperty(String key); - private native void nativeGetTextSelectionRegion(int instance, Region region); - private native void nativeGetSelectionHandles(int instance, int[] handles); /** * See {@link ComponentCallbacks2} for the trim levels and descriptions */ private static native void nativeOnTrimMemory(int level); private static native void nativeSetPauseDrawing(int instance, boolean pause); private static native boolean nativeDisableNavcache(); + private static native void nativeSetTextSelection(int instance, int selection); + private static native int nativeGetHandleLayerId(int instance, int handle, + Rect cursorLocation); + private static native boolean nativeIsBaseFirst(int instance); } diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index fe5158177540..395a638ab942 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -835,12 +835,14 @@ public final class WebViewCore { } static class TextSelectionData { - public TextSelectionData(int start, int end) { + public TextSelectionData(int start, int end, int selectTextPtr) { mStart = start; mEnd = end; + mSelectTextPtr = selectTextPtr; } int mStart; int mEnd; + int mSelectTextPtr; } static class TouchUpData { @@ -1118,6 +1120,9 @@ public final class WebViewCore { static final int COPY_TEXT = 210; static final int DELETE_TEXT = 211; static final int INSERT_TEXT = 212; + static final int SELECT_TEXT = 213; + static final int SELECT_WORD_AT = 214; + static final int SELECT_ALL = 215; // Private handler for WebCore messages. private Handler mHandler; @@ -1194,7 +1199,6 @@ public final class WebViewCore { mSettings.onDestroyed(); mNativeClass = 0; mWebView = null; - WebCoreThreadWatchdog.quit(); } break; @@ -1747,6 +1751,25 @@ public final class WebViewCore { case INSERT_TEXT: nativeInsertText(mNativeClass, (String) msg.obj); break; + case SELECT_TEXT: { + int[] args = (int[]) msg.obj; + if (args == null) { + nativeClearTextSelection(mNativeClass); + } else { + nativeSelectText(mNativeClass, args[0], + args[1], args[2], args[3]); + } + break; + } + case SELECT_WORD_AT: { + int x = msg.arg1; + int y = msg.arg2; + nativeSelectWordAt(mNativeClass, x, y); + break; + } + case SELECT_ALL: + nativeSelectAll(mNativeClass); + break; } } }; @@ -2700,11 +2723,11 @@ public final class WebViewCore { // called by JNI private void updateTextSelection(int pointer, int start, int end, - int textGeneration) { + int textGeneration, int selectionPtr) { if (mWebView != null) { Message.obtain(mWebView.mPrivateHandler, WebView.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration, - new TextSelectionData(start, end)).sendToTarget(); + new TextSelectionData(start, end, selectionPtr)).sendToTarget(); } } @@ -2771,7 +2794,7 @@ public final class WebViewCore { if (mWebView != null) { Message.obtain(mWebView.mPrivateHandler, WebView.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer, - textGeneration, new TextSelectionData(selStart, selEnd)) + textGeneration, new TextSelectionData(selStart, selEnd, 0)) .sendToTarget(); } } @@ -3026,4 +3049,9 @@ public final class WebViewCore { */ private native String nativeGetText(int nativeClass, int startX, int startY, int endX, int endY); + private native void nativeSelectText(int nativeClass, + int startX, int startY, int endX, int endY); + private native void nativeClearTextSelection(int nativeClass); + private native void nativeSelectWordAt(int nativeClass, int x, int y); + private native void nativeSelectAll(int nativeClass); } diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index d395fb2fd287..84e86af0840d 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -56,13 +56,13 @@ import com.android.internal.R; /** * A widget that enables the user to select a number form a predefined range. * The widget presents an input field and up and down buttons for selecting the - * current value. Pressing/long pressing the up and down buttons increments and + * current value. Pressing/long-pressing the up and down buttons increments and * decrements the current value respectively. Touching the input field shows a - * scroll wheel, tapping on which while shown and not moving allows direct edit - * of the current value. Sliding motions up or down hide the buttons and the - * input field, show the scroll wheel, and rotate the latter. Flinging is + * scroll wheel, which when touched allows direct edit + * of the current value. Sliding gestures up or down hide the buttons and the + * input filed, show and rotate the scroll wheel. Flinging is * also supported. The widget enables mapping from positions to strings such - * that instead the position index the corresponding string is displayed. + * that, instead of the position index, the corresponding string is displayed. * <p> * For an example of using this widget, see {@link android.widget.TimePicker}. * </p> diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 40d8a779c914..164bc64c711f 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -256,10 +256,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private float mShadowRadius, mShadowDx, mShadowDy; - private static final int PREDRAW_NOT_REGISTERED = 0; - private static final int PREDRAW_PENDING = 1; - private static final int PREDRAW_DONE = 2; - private int mPreDrawState = PREDRAW_NOT_REGISTERED; + private boolean mPreDrawRegistered; private TextUtils.TruncateAt mEllipsize = null; @@ -365,8 +362,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private SpellChecker mSpellChecker; - private boolean mSoftInputShownOnFocus = true; - // The alignment to pass to Layout, or null if not resolved. private Layout.Alignment mLayoutAlignment; @@ -2384,29 +2379,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Sets whether the soft input method will be made visible when this - * TextView gets focused. The default is true. - * - * @attr ref android.R.styleable#TextView_softInputShownOnFocus - * @hide - */ - @android.view.RemotableViewMethod - public final void setSoftInputShownOnFocus(boolean show) { - mSoftInputShownOnFocus = show; - } - - /** - * Returns whether the soft input method will be made visible when this - * TextView gets focused. The default is true. - * - * @attr ref android.R.styleable#TextView_softInputShownOnFocus - * @hide - */ - public final boolean getSoftInputShownOnFocus() { - return mSoftInputShownOnFocus; - } - - /** * Returns the list of URLSpans attached to the text * (by {@link Linkify} or otherwise) if any. You can call * {@link URLSpan#getURL} on them to find where they link to @@ -4387,26 +4359,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private void registerForPreDraw() { - final ViewTreeObserver observer = getViewTreeObserver(); - - if (mPreDrawState == PREDRAW_NOT_REGISTERED) { - observer.addOnPreDrawListener(this); - mPreDrawState = PREDRAW_PENDING; - } else if (mPreDrawState == PREDRAW_DONE) { - mPreDrawState = PREDRAW_PENDING; + if (!mPreDrawRegistered) { + getViewTreeObserver().addOnPreDrawListener(this); + mPreDrawRegistered = true; } - - // else state is PREDRAW_PENDING, so keep waiting. } /** * {@inheritDoc} */ public boolean onPreDraw() { - if (mPreDrawState != PREDRAW_PENDING) { - return true; - } - if (mLayout == null) { assumeLayout(); } @@ -4457,7 +4419,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener startSelectionActionMode(); } - mPreDrawState = PREDRAW_DONE; + getViewTreeObserver().removeOnPreDrawListener(this); + mPreDrawRegistered = false; + return !changed; } @@ -4492,10 +4456,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - final ViewTreeObserver observer = getViewTreeObserver(); - if (mPreDrawState != PREDRAW_NOT_REGISTERED) { - observer.removeOnPreDrawListener(this); - mPreDrawState = PREDRAW_NOT_REGISTERED; + if (mPreDrawRegistered) { + getViewTreeObserver().removeOnPreDrawListener(this); + mPreDrawRegistered = false; } if (mError != null) { @@ -4768,12 +4731,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override protected void onDraw(Canvas canvas) { - if (mPreDrawState == PREDRAW_DONE) { - final ViewTreeObserver observer = getViewTreeObserver(); - observer.removeOnPreDrawListener(this); - mPreDrawState = PREDRAW_NOT_REGISTERED; - } - if (mCurrentAlpha <= ViewConfiguration.ALPHA_THRESHOLD_INT) return; restartMarqueeIfNeeded(); @@ -5040,7 +4997,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mTextDisplayList == null || !mTextDisplayList.isValid() || !mTextDisplayListIsValid) { if (mTextDisplayList == null) { - mTextDisplayList = getHardwareRenderer().createDisplayList(); + mTextDisplayList = getHardwareRenderer().createDisplayList("Text"); } final HardwareCanvas hardwareCanvas = mTextDisplayList.start(); @@ -5539,7 +5496,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener && mLayout != null && onCheckIsTextEditor()) { InputMethodManager imm = InputMethodManager.peekInstance(); viewClicked(imm); - if (imm != null && mSoftInputShownOnFocus) { + if (imm != null) { imm.showSoftInput(this, 0); } } @@ -8395,7 +8352,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Show the IME, except when selecting in read-only text. final InputMethodManager imm = InputMethodManager.peekInstance(); viewClicked(imm); - if (!mTextIsSelectable && mSoftInputShownOnFocus) { + if (!mTextIsSelectable) { handled |= imm != null && imm.showSoftInput(this, 0); } @@ -10190,7 +10147,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } final boolean selectionStarted = mSelectionActionMode != null || willExtract; - if (selectionStarted && !mTextIsSelectable && mSoftInputShownOnFocus) { + if (selectionStarted && !mTextIsSelectable) { // Show the IME to be able to replace text, except when selecting non editable text. final InputMethodManager imm = InputMethodManager.peekInstance(); if (imm != null) { diff --git a/core/java/com/android/internal/util/FastMath.java b/core/java/com/android/internal/util/FastMath.java index efd087193a3c..88a17e6ba259 100644 --- a/core/java/com/android/internal/util/FastMath.java +++ b/core/java/com/android/internal/util/FastMath.java @@ -26,8 +26,8 @@ public class FastMath { * thought it may return slightly different results. It does not try to * handle (in any meaningful way) NaN or infinities. */ - public static int round(float x) { - long lx = (long)(x * (65536 * 256f)); - return (int)((lx + 0x800000) >> 24); + public static int round(float value) { + long lx = (long) (value * (65536 * 256f)); + return (int) ((lx + 0x800000) >> 24); } } diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index 6a533c02ede0..e19bb38dc53d 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -188,6 +188,14 @@ static void android_view_GLES20Canvas_finish(JNIEnv* env, jobject clazz, renderer->finish(); } +static jint android_view_GLES20Canvas_getStencilSize(JNIEnv* env, jobject clazz) { + return OpenGLRenderer::getStencilSize(); +} + +// ---------------------------------------------------------------------------- +// Functor +// ---------------------------------------------------------------------------- + static bool android_view_GLES20Canvas_callDrawGLFunction(JNIEnv* env, jobject clazz, OpenGLRenderer* renderer, Functor *functor) { android::uirenderer::Rect dirty; @@ -618,6 +626,15 @@ static jint android_view_GLES20Canvas_getDisplayListSize(JNIEnv* env, return displayList->getSize(); } +static void android_view_GLES20Canvas_setDisplayListName(JNIEnv* env, + jobject clazz, DisplayList* displayList, jstring name) { + if (name != NULL) { + const char* textArray = env->GetStringUTFChars(name, NULL); + displayList->setName(textArray); + env->ReleaseStringUTFChars(name, textArray); + } +} + static OpenGLRenderer* android_view_GLES20Canvas_createDisplayListRenderer(JNIEnv* env, jobject clazz) { return new DisplayListRenderer; @@ -808,6 +825,8 @@ static JNINativeMethod gMethods[] = { { "nPrepareDirty", "(IIIIIZ)V", (void*) android_view_GLES20Canvas_prepareDirty }, { "nFinish", "(I)V", (void*) android_view_GLES20Canvas_finish }, + { "nGetStencilSize", "()I", (void*) android_view_GLES20Canvas_getStencilSize }, + { "nCallDrawGLFunction", "(II)Z", (void*) android_view_GLES20Canvas_callDrawGLFunction }, @@ -880,10 +899,14 @@ static JNINativeMethod gMethods[] = { { "nGetDisplayList", "(II)I", (void*) android_view_GLES20Canvas_getDisplayList }, { "nDestroyDisplayList", "(I)V", (void*) android_view_GLES20Canvas_destroyDisplayList }, { "nGetDisplayListSize", "(I)I", (void*) android_view_GLES20Canvas_getDisplayListSize }, - { "nCreateDisplayListRenderer", "()I", (void*) android_view_GLES20Canvas_createDisplayListRenderer }, - { "nResetDisplayListRenderer", "(I)V", (void*) android_view_GLES20Canvas_resetDisplayListRenderer }, + { "nSetDisplayListName", "(ILjava/lang/String;)V", + (void*) android_view_GLES20Canvas_setDisplayListName }, { "nDrawDisplayList", "(IIIILandroid/graphics/Rect;)Z", (void*) android_view_GLES20Canvas_drawDisplayList }, + + { "nCreateDisplayListRenderer", "()I", (void*) android_view_GLES20Canvas_createDisplayListRenderer }, + { "nResetDisplayListRenderer", "(I)V", (void*) android_view_GLES20Canvas_resetDisplayListRenderer }, + { "nOutputDisplayList", "(II)V", (void*) android_view_GLES20Canvas_outputDisplayList }, { "nInterrupt", "(I)V", (void*) android_view_GLES20Canvas_interrupt }, { "nResume", "(I)V", (void*) android_view_GLES20Canvas_resume }, diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 030a6b3b9865..1175cbbe2033 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -881,7 +881,7 @@ <string name="replace" msgid="5781686059063148930">"Erstat..."</string> <string name="delete" msgid="6098684844021697789">"Slet"</string> <string name="copyUrl" msgid="2538211579596067402">"Kopier webadresse"</string> - <string name="selectTextMode" msgid="1018691815143165326">"Marker tekst"</string> + <string name="selectTextMode" msgid="1018691815143165326">"Markér tekst"</string> <string name="textSelectionCABTitle" msgid="5236850394370820357">"Tekstmarkering"</string> <string name="addToDictionary" msgid="9090375111134433012">"føj til ordbog"</string> <string name="deleteText" msgid="7070985395199629156">"slet"</string> diff --git a/core/tests/coretests/src/android/widget/focus/RequestFocus.java b/core/tests/coretests/src/android/widget/focus/RequestFocus.java index af9ee170abe4..21d762a55061 100644 --- a/core/tests/coretests/src/android/widget/focus/RequestFocus.java +++ b/core/tests/coretests/src/android/widget/focus/RequestFocus.java @@ -21,9 +21,7 @@ import com.android.frameworks.coretests.R; import android.app.Activity; import android.os.Bundle; import android.os.Handler; -import android.widget.LinearLayout; import android.widget.Button; -import android.view.View; /** * Exercises cases where elements of the UI are requestFocus()ed. diff --git a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java index a78b0c932c4b..baf587e39c1b 100644 --- a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java +++ b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java @@ -16,21 +16,27 @@ package android.widget.focus; -import android.widget.focus.RequestFocus; -import com.android.frameworks.coretests.R; - import android.os.Handler; -import android.test.ActivityInstrumentationTestCase; +import android.test.ActivityInstrumentationTestCase2; +import android.test.UiThreadTest; import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.MediumTest; -import android.widget.Button; import android.util.AndroidRuntimeException; +import android.view.View; +import android.view.View.OnFocusChangeListener; +import android.view.ViewTreeObserver.OnGlobalFocusChangeListener; +import android.widget.Button; + +import com.android.frameworks.coretests.R; + +import java.util.ArrayList; +import java.util.List; /** * {@link RequestFocusTest} is set up to exercise cases where the views that * have focus become invisible or GONE. */ -public class RequestFocusTest extends ActivityInstrumentationTestCase<RequestFocus> { +public class RequestFocusTest extends ActivityInstrumentationTestCase2<RequestFocus> { private Button mTopLeftButton; private Button mBottomLeftButton; @@ -39,7 +45,7 @@ public class RequestFocusTest extends ActivityInstrumentationTestCase<RequestFoc private Handler mHandler; public RequestFocusTest() { - super("com.android.frameworks.coretests", RequestFocus.class); + super(RequestFocus.class); } @Override @@ -94,4 +100,145 @@ public class RequestFocusTest extends ActivityInstrumentationTestCase<RequestFoc e.getClass().getName()); } } + + /** + * This tests checks the case in which the first focusable View clears focus. + * In such a case the framework tries to give the focus to another View starting + * from the top. Hence, the framework will try to give focus to the view that + * wants to clear its focus. From a client perspective, the view does not loose + * focus after the call, therefore no callback for focus change should be invoked. + * + * @throws Exception If an error occurs. + */ + @UiThreadTest + public void testOnFocusChangeNotCalledIfFocusDoesNotMove() throws Exception { + // Get the first focusable. + Button button = mTopLeftButton; + + // Make sure that the button is the first focusable and focus it. + button.getRootView().requestFocus(View.FOCUS_DOWN); + assertTrue(button.hasFocus()); + + // Attach on focus change listener that should not be called. + button.setOnFocusChangeListener(new OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + throw new IllegalStateException("Unexpeced call to" + + "OnFocusChangeListener#onFocusChange"); + } + }); + + // Attach on global focus change listener that should not be called. + button.getViewTreeObserver().addOnGlobalFocusChangeListener( + new OnGlobalFocusChangeListener() { + @Override + public void onGlobalFocusChanged(View oldFocus, View newFocus) { + throw new IllegalStateException("Unexpeced call to" + + "OnFocusChangeListener#onFocusChange"); + } + }); + + // Try to clear focus. + button.clearFocus(); + } + + /** + * This tests check whether the on focus change callbacks are invoked in + * the proper order when a View loses focus and the framework gives it to + * the fist focusable one. + * + * @throws Exception + */ + @UiThreadTest + public void testOnFocusChangeCallbackOrder() throws Exception { + // Get the first focusable. + Button clearingFocusButton = mTopRightButton; + Button gainingFocusButton = mTopLeftButton; + + // Make sure that the clearing focus is not the first focusable. + View focusCandidate = clearingFocusButton.getRootView().getParent().focusSearch(null, + View.FOCUS_FORWARD); + assertNotSame("The clearing focus button is not the first focusable.", + clearingFocusButton, focusCandidate); + assertSame("The gaining focus button is the first focusable.", + gainingFocusButton, focusCandidate); + + // Focus the clearing focus button. + clearingFocusButton.requestFocus(); + assertTrue(clearingFocusButton.hasFocus()); + + // Register the invocation order checker. + CallbackOrderChecker checker = new CallbackOrderChecker(clearingFocusButton, + gainingFocusButton); + clearingFocusButton.setOnFocusChangeListener(checker); + gainingFocusButton.setOnFocusChangeListener(checker); + clearingFocusButton.getViewTreeObserver().addOnGlobalFocusChangeListener(checker); + + // Try to clear focus. + clearingFocusButton.clearFocus(); + + // Check that no callback was invoked since focus did not move. + checker.verify(); + } + + /** + * This class check whether the focus change callback are invoked in order. + */ + private class CallbackOrderChecker implements OnFocusChangeListener, + OnGlobalFocusChangeListener { + + private class CallbackInvocation { + final String mMethodName; + final Object[] mArguments; + + CallbackInvocation(String methodName, Object[] arguments) { + mMethodName = methodName; + mArguments = arguments; + } + } + + private final View mClearingFocusView; + private final View mGainingFocusView; + + private final List<CallbackInvocation> mInvocations = new ArrayList<CallbackInvocation>(); + + public CallbackOrderChecker(View clearingFocusView, View gainingFocusView) { + mClearingFocusView = clearingFocusView; + mGainingFocusView = gainingFocusView; + } + + @Override + public void onFocusChange(View view, boolean hasFocus) { + CallbackInvocation invocation = new CallbackInvocation( + "OnFocusChangeListener#onFocusChange", new Object[] {view, hasFocus}); + mInvocations.add(invocation); + } + + @Override + public void onGlobalFocusChanged(View oldFocus, View newFocus) { + CallbackInvocation invocation = new CallbackInvocation( + "OnFocusChangeListener#onFocusChange", new Object[] {oldFocus, newFocus}); + mInvocations.add(invocation); + } + + public void verify() { + assertSame("All focus change callback should be invoked.", 3, mInvocations.size()); + assertInvioked("Callback for View clearing focus explected.", 0, + "OnFocusChangeListener#onFocusChange", + new Object[] {mClearingFocusView, false}); + assertInvioked("Callback for View global focus change explected.", 1, + "OnFocusChangeListener#onFocusChange", new Object[] {mClearingFocusView, + mGainingFocusView}); + assertInvioked("Callback for View gaining focus explected.", 2, + "OnFocusChangeListener#onFocusChange", new Object[] {mGainingFocusView, true}); + } + + private void assertInvioked(String message, int order, String methodName, + Object[] arguments) { + CallbackInvocation invocation = mInvocations.get(order); + assertEquals(message, methodName, invocation.mMethodName); + assertEquals(message, arguments[0], invocation.mArguments[0]); + assertEquals(message, arguments[1], invocation.mArguments[1]); + } + } } diff --git a/docs/html/guide/appendix/market-filters.jd b/docs/html/guide/appendix/market-filters.jd index 07b9370d6740..d9b2155f8e46 100644 --- a/docs/html/guide/appendix/market-filters.jd +++ b/docs/html/guide/appendix/market-filters.jd @@ -165,10 +165,10 @@ default.</li> <p><strong>Example 1</strong><br /> The manifest declares <code><uses-sdk android:minSdkVersion="3"></code> - and does not does not include a <code><supports-screens></code> element. - <strong>Result</strong>: Android Market will not show the app to a user of a - small-screen device, but will show it to users of normal and large-screen - devices, users, unless other filters apply. </p> + and does not include a <code><supports-screens></code> element. + <strong>Result</strong>: Android Market does not show the app to a user of a + small-screen device, but does show it to users of normal and large-screen + devices, unless other filters also exclude those devices. </p> <p><strong>Example 2<br /> </strong>The manifest declares <code><uses-sdk android:minSdkVersion="3" android:targetSdkVersion="4"></code> and does not include a diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs index 4e5badd695ef..6c01d44edfce 100644 --- a/docs/html/guide/guide_toc.cs +++ b/docs/html/guide/guide_toc.cs @@ -8,15 +8,16 @@ <ul> <li> - <h2><span class="en">Android Basics</span> - <span class="de" style="display:none">Einführung in Android</span> - <span class="es" style="display:none">Información básica sobre Android</span> - <span class="fr" style="display:none">Présentation d'Android</span> - <span class="it" style="display:none">Nozioni di base su Android</span> - <span class="ja" style="display:none">Android の基本</span> - <span class="zh-CN" style="display:none">Android 基础知识</span> - <span class="zh-TW" style="display:none">Android 簡介</span> - </h2> + <span class="heading"> + <span class="en">Android Basics</span> + <span class="de" style="display:none">Einführung in Android</span> + <span class="es" style="display:none">Información básica sobre Android</span> + <span class="fr" style="display:none">Présentation d'Android</span> + <span class="it" style="display:none">Nozioni di base su Android</span> + <span class="ja" style="display:none">Android の基本</span> + <span class="zh-CN" style="display:none">Android 基础知识</span> + <span class="zh-TW" style="display:none">Android 簡介</span> + </span> <ul> <li><a href="<?cs var:toroot ?>guide/basics/what-is-android.html"> <span class="en">What Is Android?</span> @@ -46,7 +47,7 @@ </li> <li> - <h2> + <span class="heading"> <span class="en">Framework Topics</span> <span class="de" style="display:none">Framework-Themen</span> <span class="es" style="display:none">Temas sobre el framework</span> @@ -55,7 +56,7 @@ <span class="ja" style="display:none">フレームワーク トピック</span> <span class="zh-CN" style="display:none">框架主题</span> <span class="zh-TW" style="display:none">架構主題</span> - </h2> + </span> <ul> <li class="toggle-list"> <div><a href="<?cs var:toroot ?>guide/topics/fundamentals/activities.html"> @@ -443,9 +444,9 @@ </li> <li> - <h2> + <span class="heading"> <span class="en">Android Market Topics</span> - </h2> + </span> <ul> <li><a href="<?cs var:toroot ?>guide/publishing/publishing.html"> <span class="en">Publishing on Android Market</span> @@ -489,15 +490,16 @@ <li> - <h2><span class="en">Developing</span> - <span class="de" style="display:none">Entwicklung</span> - <span class="es" style="display:none">Desarrollo</span> - <span class="fr" style="display:none">Développement</span> - <span class="it" style="display:none">Sviluppo</span> - <span class="ja" style="display:none">開発</span> - <span class="zh-CN" style="display:none">开发</span> - <span class="zh-TW" style="display:none">開發</span> - </h2> + <span class="heading"> + <span class="en">Developing</span> + <span class="de" style="display:none">Entwicklung</span> + <span class="es" style="display:none">Desarrollo</span> + <span class="fr" style="display:none">Développement</span> + <span class="it" style="display:none">Sviluppo</span> + <span class="ja" style="display:none">開発</span> + <span class="zh-CN" style="display:none">开发</span> + <span class="zh-TW" style="display:none">開發</span> + </span> <ul> <!--<li><a href="">Developing for Android</a></li> signing, upgrading, selecting a package name, select device profile, touch, trackball, dpad available, etc. --> @@ -683,15 +685,16 @@ </li> <li> - <h2><span class="en">Publishing</span> - <span class="de" style="display:none">Veröffentlichung</span> - <span class="es" style="display:none">Publicación</span> - <span class="fr" style="display:none">Publication</span> - <span class="it" style="display:none">Pubblicazione</span> - <span class="ja" style="display:none">公開</span> - <span class="zh-CN" style="display:none">发布</span> - <span class="zh-TW" style="display:none">發佈</span> - </h2> + <span class="heading"> + <span class="en">Publishing</span> + <span class="de" style="display:none">Veröffentlichung</span> + <span class="es" style="display:none">Publicación</span> + <span class="fr" style="display:none">Publication</span> + <span class="it" style="display:none">Pubblicazione</span> + <span class="ja" style="display:none">公開</span> + <span class="zh-CN" style="display:none">发布</span> + <span class="zh-TW" style="display:none">發佈</span> + </span> <ul> <li><a href="<?cs var:toroot ?>guide/publishing/publishing_overview.html"> <span class="en">Publishing Overview</span> @@ -733,15 +736,16 @@ applications</span> </li> <li> - <h2><span class="en">Best Practices</span> - <span class="de" style="display:none">Bewährte Verfahren</span> - <span class="es" style="display:none">Prácticas recomendadas</span> - <span class="fr" style="display:none">Meilleures pratiques</span> - <span class="it" style="display:none">Best practice</span> - <span class="ja" style="display:none">ベスト プラクティス</span> - <span class="zh-CN" style="display:none">最佳实践</span> - <span class="zh-TW" style="display:none">最佳實務</span> - </h2> + <span class="heading"> + <span class="en">Best Practices</span> + <span class="de" style="display:none">Bewährte Verfahren</span> + <span class="es" style="display:none">Prácticas recomendadas</span> + <span class="fr" style="display:none">Meilleures pratiques</span> + <span class="it" style="display:none">Best practice</span> + <span class="ja" style="display:none">ベスト プラクティス</span> + <span class="zh-CN" style="display:none">最佳实践</span> + <span class="zh-TW" style="display:none">最佳實務</span> + </span> <ul> <li><a href="<?cs var:toroot ?>guide/practices/compatibility.html"> <span class="en">Compatibility</span> @@ -837,8 +841,9 @@ applications</span> </li> <li> - <h2><span class="en">Web Applications</span> - </h2> + <span class="heading"> + <span class="en">Web Applications</span> + </span> <ul> <li><a href="<?cs var:toroot ?>guide/webapps/index.html"> <span class="en">Web Apps Overview</span> @@ -859,15 +864,16 @@ applications</span> </li> <li> - <h2><span class="en">Appendix</span> - <span class="de" style="display:none">Anhang</span> - <span class="es" style="display:none">Apéndice</span> - <span class="fr" style="display:none">Annexes</span> - <span class="it" style="display:none">Appendice</span> - <span class="ja" style="display:none">付録</span> - <span class="zh-CN" style="display:none">附录</span> - <span class="zh-TW" style="display:none">附錄</span> - </h2> + <span class="heading"> + <span class="en">Appendix</span> + <span class="de" style="display:none">Anhang</span> + <span class="es" style="display:none">Apéndice</span> + <span class="fr" style="display:none">Annexes</span> + <span class="it" style="display:none">Appendice</span> + <span class="ja" style="display:none">付録</span> + <span class="zh-CN" style="display:none">附录</span> + <span class="zh-TW" style="display:none">附錄</span> + </span> <ul> <li><a href="<?cs var:toroot ?>guide/appendix/api-levels.html"> <span class="en">Android API Levels</span> diff --git a/docs/html/guide/topics/ui/how-android-draws.jd b/docs/html/guide/topics/ui/how-android-draws.jd index 3a57afacaa27..6a8cd869e535 100644 --- a/docs/html/guide/topics/ui/how-android-draws.jd +++ b/docs/html/guide/topics/ui/how-android-draws.jd @@ -62,7 +62,7 @@ and each View is responsible for drawing itself. <p> The measure pass uses two classes to communicate dimensions. The - {@link android.view.View.MeasureSpec} class is used by Views to tell their parents how they + {@link android.view.ViewGroup.LayoutParams} class is used by Views to tell their parents how they want to be measured and positioned. The base LayoutParams class just describes how big the View wants to be for both width and height. For each dimension, it can specify one of:</p> diff --git a/docs/html/guide/topics/ui/notifiers/notifications.jd b/docs/html/guide/topics/ui/notifiers/notifications.jd index 33b0fecc9ed5..bef9671100bf 100644 --- a/docs/html/guide/topics/ui/notifiers/notifications.jd +++ b/docs/html/guide/topics/ui/notifiers/notifications.jd @@ -245,31 +245,27 @@ an application to be part of that application's UI flow, so simply launching the activity like this can cause it to be mixed with your normal application back stack in undesired ways. To make it behave correctly, in the manifest declaration for the activity the attributes -<code>android:launchMode="singleInstance"</code> and +<code>android:launchMode="singleTask"</code>, +<code>android:taskAffinity=""</code> and <code>android:excludeFromRecents="true"</code> must be set. The full activity declaration for this sample is:</p> {@sample development/samples/ApiDemos/AndroidManifest.xml interstitial_affinity} -<p>Because of the use of <code>singleInstance</code>, you must be careful about launching -any other activities from this one. These activities will be launched -in their own task, and care must be taken to make sure this interacts -well with the current state of your application's task. This is essentially +<p>You must be careful when launching other activities from this initial activity, +because this is not a top-level part of the application, does not appear in +recents, and needs to be relaunched at any point from the notification with new data +to show. This best approach is to make sure any activity launched from it is +launched in its own task. When doing this care must be taken to make sure this +new task interacts well with the current state of your exiting application's +task. This is essentially the same as switching to the main application as described for the Email style notification shown before. Given the <code>makeMessageIntentStack()</code> -method previously shown, handling a click here would look something like this:</p> +method previously shown, handling a click then would look something like this:</p> {@sample development/samples/ApiDemos/src/com/example/android/apis/app/IncomingMessageInterstitial.java app_launch} -<p>If you don't want to use the <code>singleInstance</code> launch mode for -this activity, an alternative approach is to use <code>android:taskAffinity=""</code>. -This tells Android that the activity should not be treated as part of the -main application flow, so it will not get mixed together with that. All of the -other issues discussed here do still apply, though this would allow you to start -additional activities that are part of this notification task instead of switching -to and replacing the main application task.</p> - <h2 id="ManageYourNotifications">Managing your Notifications</h2> <p>The {@link android.app.NotificationManager} is a system service that manages all diff --git a/docs/html/index.jd b/docs/html/index.jd index c14c0ae05c63..fdf860a880d8 100644 --- a/docs/html/index.jd +++ b/docs/html/index.jd @@ -1,4 +1,5 @@ home=true +metaDescription=The official site for Android developers. Provides the Android SDK and documentation for app developers and designers. @jd:body @@ -153,7 +154,7 @@ href="{@docRoot}resources/dashboard/platform-versions.html">Learn more »</ + "href='https://plus.google.com/108967384991768947849'>+Android Developers</a>. " + "We'll use it to host Hangouts for developers, talk about the latest releases, " + "development and design tips, and much more.</p>" -+ "<div style='margin-top:.7em'><g:plus href='https://plus.google.com/108967384991768947849' " ++ "<div style='margin:.7em 0 0 -1.2em'><g:plus href='https://plus.google.com/108967384991768947849' " + "size=\"smallbadge\" width=\"275\"></g:plus></div>" }, @@ -213,6 +214,3 @@ po.type = "text/javascript"; po.async = true;po.src = "https://apis.google.com/j var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(po, s); })();</script> -<style type="text/css"> - #___plus_0, #___plus_0 iframe, #___plus_0 { width: 275px !important; } -</style> diff --git a/docs/html/resources/resources_toc.cs b/docs/html/resources/resources_toc.cs index 628df9ecfe46..848303751a1c 100644 --- a/docs/html/resources/resources_toc.cs +++ b/docs/html/resources/resources_toc.cs @@ -1,7 +1,8 @@ <ul> <li> - <h2><span class="en">Android Training</span> - </h2> + <span class="heading"> + <span class="en">Android Training</span> + </span> <ul> <li><a href="<?cs var:toroot ?>training/index.html"> @@ -237,8 +238,9 @@ <li> - <h2><span class="en">Technical Resources</span> - </h2> + <span class="heading"> + <span class="en">Technical Resources</span> + </span> <ul> <li class="toggle-list"> <div><a href="<?cs var:toroot ?>resources/browser.html?tag=sample"> @@ -288,15 +290,16 @@ </ul> </li> <li> - <h2><span class="en">Community</span> - <span style="display:none" class="de"></span> - <span style="display:none" class="es">Comunidad</span> - <span style="display:none" class="fr">Communauté</span> - <span style="display:none" class="it"></span> - <span style="display:none" class="ja">コミュニティ</span> - <span style="display:none" class="zh-CN">社区</span> - <span style="display:none" class="zh-TW">社群</span> - </h2> + <span class="heading"> + <span class="en">Community</span> + <span style="display:none" class="de"></span> + <span style="display:none" class="es">Comunidad</span> + <span style="display:none" class="fr">Communauté</span> + <span style="display:none" class="it"></span> + <span style="display:none" class="ja">コミュニティ</span> + <span style="display:none" class="zh-CN">社区</span> + <span style="display:none" class="zh-TW">社群</span> + </span> <ul> <li><a href="<?cs var:toroot ?>resources/community-groups.html"> <span class="en">Developer Forums</span> @@ -309,8 +312,9 @@ <?cs if:android.whichdoc == "online" ?> <li> - <h2><span class="en">Device Dashboard</span> - </h2> + <span class="heading"> + <span class="en">Device Dashboard</span> + </span> <ul> <li><a href="<?cs var:toroot ?>resources/dashboard/platform-versions.html"> <span class="en">Platform Versions</span> @@ -327,7 +331,9 @@ ?> <li> - <h2><span class="en">More</span></h2> + <span class="heading"> + <span class="en">More</span> + </span> <ul> <li><a href="<?cs var:toroot ?>resources/faq/commontasks.html"> <span class="en">Common Tasks </span> diff --git a/docs/html/sdk/android-2.3.3.jd b/docs/html/sdk/android-2.3.3.jd index 7a5b044c9687..023e2e41294a 100644 --- a/docs/html/sdk/android-2.3.3.jd +++ b/docs/html/sdk/android-2.3.3.jd @@ -336,7 +336,7 @@ descriptor).</p> <li>English, New Zealand (en_NZ)</li> <li>English, Singapore(en_SG)</li> <li>English, US (en_US)</li> -<li>English, Zimbabwe (en_ZA)</li> +<li>English, South Africa (en_ZA)</li> <li>Spanish (es_ES)</li> <li>Spanish, US (es_US)</li> <li>Finnish, Finland (fi_FI)</li> diff --git a/docs/html/sdk/android-2.3.4.jd b/docs/html/sdk/android-2.3.4.jd index 4cb44b921b5a..eeaa69ab169f 100644 --- a/docs/html/sdk/android-2.3.4.jd +++ b/docs/html/sdk/android-2.3.4.jd @@ -296,7 +296,7 @@ descriptor).</p> <li>English, New Zealand (en_NZ)</li> <li>English, Singapore(en_SG)</li> <li>English, US (en_US)</li> -<li>English, Zimbabwe (en_ZA)</li> +<li>English, South Africa (en_ZA)</li> <li>Spanish (es_ES)</li> <li>Spanish, US (es_US)</li> <li>Finnish, Finland (fi_FI)</li> diff --git a/docs/html/sdk/android-2.3.jd b/docs/html/sdk/android-2.3.jd index e7aa0fac727a..fc4f5aa95a24 100644 --- a/docs/html/sdk/android-2.3.jd +++ b/docs/html/sdk/android-2.3.jd @@ -859,7 +859,7 @@ descriptor).</p> <li>English, New Zealand (en_NZ)</li> <li>English, Singapore(en_SG)</li> <li>English, US (en_US)</li> -<li>English, Zimbabwe (en_ZA)</li> +<li>English, South Africa (en_ZA)</li> <li>Spanish (es_ES)</li> <li>Spanish, US (es_US)</li> <li>Finnish, Finland (fi_FI)</li> diff --git a/docs/html/sdk/android-3.0.jd b/docs/html/sdk/android-3.0.jd index 96b92d9a59e3..49fefee535d4 100644 --- a/docs/html/sdk/android-3.0.jd +++ b/docs/html/sdk/android-3.0.jd @@ -1119,7 +1119,7 @@ descriptor).</p> <li>English, New Zealand (en_NZ)</li> <li>English, Singapore(en_SG)</li> <li>English, US (en_US)</li> -<li>English, Zimbabwe (en_ZA)</li> +<li>English, South Africa (en_ZA)</li> <li>Spanish (es_ES)</li> <li>Spanish, US (es_US)</li> <li>Finnish, Finland (fi_FI)</li> diff --git a/docs/html/sdk/android-3.1.jd b/docs/html/sdk/android-3.1.jd index 78f265d2f055..b9cf96940364 100644 --- a/docs/html/sdk/android-3.1.jd +++ b/docs/html/sdk/android-3.1.jd @@ -1040,7 +1040,7 @@ descriptor).</p> <li>English, New Zealand (en_NZ)</li> <li>English, Singapore(en_SG)</li> <li>English, US (en_US)</li> -<li>English, Zimbabwe (en_ZA)</li> +<li>English, South Africa (en_ZA)</li> <li>Spanish (es_ES)</li> <li>Spanish, US (es_US)</li> <li>Finnish, Finland (fi_FI)</li> diff --git a/docs/html/sdk/android-4.0.3.jd b/docs/html/sdk/android-4.0.3.jd index 1fca8dff35ea..809c83c6e61d 100644 --- a/docs/html/sdk/android-4.0.3.jd +++ b/docs/html/sdk/android-4.0.3.jd @@ -435,7 +435,7 @@ image are listed below (with <em>language</em>_<em>country/region</em> locale de <li>English, New Zealand (en_NZ)</li> <li>English, Singapore(en_SG)</li> <li>English, US (en_US)</li> -<li>English, Zimbabwe (en_ZA)</li> +<li>English, South Africa (en_ZA)</li> <li>Spanish (es_ES)</li> <li>Spanish, US (es_US)</li> <li>Finnish, Finland (fi_FI)</li> diff --git a/docs/html/sdk/android-4.0.jd b/docs/html/sdk/android-4.0.jd index 5f55947bb0e6..2cad86b7a686 100644 --- a/docs/html/sdk/android-4.0.jd +++ b/docs/html/sdk/android-4.0.jd @@ -1963,7 +1963,7 @@ image are listed below (with <em>language</em>_<em>country/region</em> locale de <li>English, New Zealand (en_NZ)</li> <li>English, Singapore(en_SG)</li> <li>English, US (en_US)</li> -<li>English, Zimbabwe (en_ZA)</li> +<li>English, South Africa (en_ZA)</li> <li>Spanish (es_ES)</li> <li>Spanish, US (es_US)</li> <li>Finnish, Finland (fi_FI)</li> diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd index f15da78710ad..30825eec060c 100644 --- a/docs/html/sdk/eclipse-adt.jd +++ b/docs/html/sdk/eclipse-adt.jd @@ -42,7 +42,7 @@ export signed (or unsigned) {@code .apk} files in order to distribute your appli <p>Developing in Eclipse with ADT is highly recommended and is the fastest way to get started. With the guided project setup it provides, as well as tools -integration, custom XML editors, and debug ouput pane, ADT gives you an +integration, custom XML editors, and debug output pane, ADT gives you an incredible boost in developing Android applications. </p> <p>This document provides step-by-step instructions on how to download the ADT @@ -1104,7 +1104,7 @@ ADT installation as described in the steps below. </p> <h3 id="downloading">Downloading the ADT Plugin</h3> <p>Use the Update Manager feature of your Eclipse installation to install the latest -revision of ADT on your development computer.<> +revision of ADT on your development computer.</p> <p>Assuming that you have a compatible version of the Eclipse IDE installed, as described in <a href="#preparing">Preparing for Installation</a>, above, follow diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd index 65a1f465c475..5cf05e0ec1bc 100644 --- a/docs/html/sdk/index.jd +++ b/docs/html/sdk/index.jd @@ -1,4 +1,5 @@ page.title=Android SDK +page.metaDescription=Download the official Android SDK to develop apps for Android-powered devices. sdk.redirect=0 sdk.win_installer=installer_r16-windows.exe diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs index 5f6a57fc2168..0de477a25dfa 100644 --- a/docs/html/sdk/sdk_toc.cs +++ b/docs/html/sdk/sdk_toc.cs @@ -1,7 +1,7 @@ <?cs if:!sdk.redirect ?> <ul> <li> - <h2> + <span class="heading"> <span class="en">Android SDK Starter Package</span> <span style="display:none" class="de">Aktuelle SDK-Version</span> <span style="display:none" class="es">Versión actual del SDK</span> @@ -10,7 +10,7 @@ <span style="display:none" class="ja">現在リリースされている SDK</span> <span style="display:none" class="zh-CN">当前的 SDK 版本</span> <span style="display:none" class="zh-TW">目前 SDK 發行版本</span> - </h2> + </span> <ul><?cs if:android.whichdoc == "online" ?> <li><a href="<?cs var:toroot ?>sdk/index.html"> @@ -37,7 +37,8 @@ </ul> </li><?cs if:sdk.preview ?> - <li><h2>Android 3.0 Preview SDK</h2> + <li> + <span class="heading">Android 3.0 Preview SDK</span> <ul> <li><a href="<?cs var:toroot ?>sdk/preview/start.html">Getting Started</a> <span class="new">new!</span></li> @@ -46,13 +47,14 @@ class="new">new!</span></li> /if ?> <?cs if:sdk.preview ?> - <li><h2>Android x.x Preview</h2> + <li> + <span class="heading">Android x.x Preview</span> <ul> </ul> </li><?cs /if ?> <li> - <h2> + <span class="heading"> <span class="en">Downloadable SDK Components</span> <span style="display:none" class="de"></span> <span style="display:none" class="es"></span> @@ -61,7 +63,7 @@ class="new">new!</span></li> <span style="display:none" class="ja"></span> <span style="display:none" class="zh-CN"></span> <span style="display:none" class="zh-TW"></span> - </h2> + </span> <ul> <li><a href="<?cs var:toroot ?>sdk/adding-components.html"> <span class="en">Adding SDK Components</span> @@ -158,7 +160,7 @@ class="new">new!</span></li> </ul> </li> <li> - <h2> + <span class="heading"> <span class="en">ADT Plugin for Eclipse</span> <span style="display:none" class="de"></span> <span style="display:none" class="es"></span> @@ -167,7 +169,7 @@ class="new">new!</span></li> <span style="display:none" class="ja"></span> <span style="display:none" class="zh-CN"></span> <span style="display:none" class="zh-TW"></span> - </h2> + </span> <ul> <li><a href="<?cs var:toroot ?>sdk/eclipse-adt.html">ADT 16.0.1 <span style="display:none" class="de"></span> @@ -182,7 +184,8 @@ class="new">new!</span> </ul> </li> <li> - <h2><span class="en">Native Development Tools</span> + <span class="heading"> + <span class="en">Native Development Tools</span> <span style="display:none" class="de"></span> <span style="display:none" class="es"></span> <span style="display:none" class="fr"></span> @@ -190,7 +193,7 @@ class="new">new!</span> <span style="display:none" class="ja"></span> <span style="display:none" class="zh-CN"></span> <span style="display:none" class="zh-TW"></span> - </h2> + </span> <ul> <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r7</a> <span class="new">new!</span> @@ -200,7 +203,7 @@ class="new">new!</span> </li> <li> - <h2> + <span class="heading"> <span class="en">More Information</span> <span style="display:none" class="de"></span> <span style="display:none" class="es"></span> @@ -209,7 +212,7 @@ class="new">new!</span> <span style="display:none" class="ja"></span> <span style="display:none" class="zh-CN"></span> <span style="display:none" class="zh-TW"></span> - </h2> + </span> <ul> <li><a href="<?cs var:toroot ?>sdk/oem-usb.html"> <span class="en">OEM USB Drivers</span> diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java index 82ed1992bac3..96a71e390713 100644 --- a/graphics/java/android/graphics/LinearGradient.java +++ b/graphics/java/android/graphics/LinearGradient.java @@ -28,8 +28,8 @@ public class LinearGradient extends Shader { the the colors are distributed evenly along the gradient line. @param tile The Shader tiling mode */ - public LinearGradient(float x0, float y0, float x1, float y1, - int colors[], float positions[], TileMode tile) { + public LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[], + TileMode tile) { if (colors.length < 2) { throw new IllegalArgumentException("needs >= 2 number of colors"); } @@ -50,8 +50,8 @@ public class LinearGradient extends Shader { @param color1 The color at the end of the gradient line. @param tile The Shader tiling mode */ - public LinearGradient(float x0, float y0, float x1, float y1, - int color0, int color1, TileMode tile) { + public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, + TileMode tile) { native_instance = nativeCreate2(x0, y0, x1, y1, color0, color1, tile.nativeInt); native_shader = nativePostCreate2(native_instance, x0, y0, x1, y1, color0, color1, tile.nativeInt); diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java index 78302244e1c1..0ada1fbdcd8d 100644 --- a/graphics/java/android/graphics/Rect.java +++ b/graphics/java/android/graphics/Rect.java @@ -76,13 +76,21 @@ public final class Rect implements Parcelable { } @Override - public boolean equals(Object obj) { - Rect r = (Rect) obj; - if (r != null) { - return left == r.left && top == r.top && right == r.right - && bottom == r.bottom; - } - return false; + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Rect r = (Rect) o; + return left == r.left && top == r.top && right == r.right && bottom == r.bottom; + } + + @Override + public int hashCode() { + int result = left; + result = 31 * result + top; + result = 31 * result + right; + result = 31 * result + bottom; + return result; } @Override diff --git a/graphics/java/android/graphics/RectF.java b/graphics/java/android/graphics/RectF.java index 00e9609a71bf..293dfccb4b5d 100644 --- a/graphics/java/android/graphics/RectF.java +++ b/graphics/java/android/graphics/RectF.java @@ -79,6 +79,24 @@ public class RectF implements Parcelable { bottom = r.bottom; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Rect r = (Rect) o; + return left == r.left && top == r.top && right == r.right && bottom == r.bottom; + } + + @Override + public int hashCode() { + int result = (left != +0.0f ? Float.floatToIntBits(left) : 0); + result = 31 * result + (top != +0.0f ? Float.floatToIntBits(top) : 0); + result = 31 * result + (right != +0.0f ? Float.floatToIntBits(right) : 0); + result = 31 * result + (bottom != +0.0f ? Float.floatToIntBits(bottom) : 0); + return result; + } + public String toString() { return "RectF(" + left + ", " + top + ", " + right + ", " + bottom + ")"; diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java index 50964d5e5546..5b50bebd64ac 100644 --- a/graphics/java/android/graphics/drawable/GradientDrawable.java +++ b/graphics/java/android/graphics/drawable/GradientDrawable.java @@ -173,9 +173,20 @@ public class GradientDrawable extends Drawable { } /** - * Specify radii for each of the 4 corners. For each corner, the array - * contains 2 values, [X_radius, Y_radius]. The corners are ordered - * top-left, top-right, bottom-right, bottom-left + * <p>Specify radii for each of the 4 corners. For each corner, the array + * contains 2 values, <code>[X_radius, Y_radius]</code>. The corners are ordered + * top-left, top-right, bottom-right, bottom-left. This property + * is honored only when the shape is of type {@link #RECTANGLE}.</p> + * <p><strong>Note</strong>: changing this property will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing this property.</p> + * + * @param radii 4 pairs of X and Y radius for each corner, specified in pixels. + * The length of this array must be >= 8 + * + * @see #mutate() + * @see #setCornerRadii(float[]) + * @see #setShape(int) */ public void setCornerRadii(float[] radii) { mGradientState.setCornerRadii(radii); @@ -184,23 +195,57 @@ public class GradientDrawable extends Drawable { } /** - * Specify radius for the corners of the gradient. If this is > 0, then the - * drawable is drawn in a round-rectangle, rather than a rectangle. + * <p>Specify radius for the corners of the gradient. If this is > 0, then the + * drawable is drawn in a round-rectangle, rather than a rectangle. This property + * is honored only when the shape is of type {@link #RECTANGLE}.</p> + * <p><strong>Note</strong>: changing this property will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing this property.</p> + * + * @param radius The radius in pixels of the corners of the rectangle shape + * + * @see #mutate() + * @see #setCornerRadii(float[]) + * @see #setShape(int) */ public void setCornerRadius(float radius) { mGradientState.setCornerRadius(radius); mPathIsDirty = true; invalidateSelf(); } - + /** - * Set the stroke width and color for the drawable. If width is zero, - * then no stroke is drawn. + * <p>Set the stroke width and color for the drawable. If width is zero, + * then no stroke is drawn.</p> + * <p><strong>Note</strong>: changing this property will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing this property.</p> + * + * @param width The width in pixels of the stroke + * @param color The color of the stroke + * + * @see #mutate() + * @see #setStroke(int, int, float, float) */ public void setStroke(int width, int color) { setStroke(width, color, 0, 0); } - + + /** + * <p>Set the stroke width and color for the drawable. If width is zero, + * then no stroke is drawn. This method can also be used to dash the stroke.</p> + * <p><strong>Note</strong>: changing this property will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing this property.</p> + * + * @param width The width in pixels of the stroke + * @param color The color of the stroke + * @param dashWidth The length in pixels of the dashes, set to 0 to disable dashes + * @param dashGap The gap in pixels between dashes + * + * @see #mutate() + * @see #setStroke(int, int) + */ public void setStroke(int width, int color, float dashWidth, float dashGap) { mGradientState.setStroke(width, color, dashWidth, dashGap); @@ -218,13 +263,37 @@ public class GradientDrawable extends Drawable { mStrokePaint.setPathEffect(e); invalidateSelf(); } - + + + /** + * <p>Sets the size of the shape drawn by this drawable.</p> + * <p><strong>Note</strong>: changing this property will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing this property.</p> + * + * @param width The width of the shape used by this drawable + * @param height The height of the shape used by this drawable + * + * @see #mutate() + * @see #setGradientType(int) + */ public void setSize(int width, int height) { mGradientState.setSize(width, height); mPathIsDirty = true; invalidateSelf(); } - + + /** + * <p>Sets the type of shape used to draw the gradient.</p> + * <p><strong>Note</strong>: changing this property will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing this property.</p> + * + * @param shape The desired shape for this drawable: {@link #LINE}, + * {@link #OVAL}, {@link #RECTANGLE} or {@link #RING} + * + * @see #mutate() + */ public void setShape(int shape) { mRingPath = null; mPathIsDirty = true; @@ -232,24 +301,73 @@ public class GradientDrawable extends Drawable { invalidateSelf(); } + /** + * <p>Sets the type of gradient used by this drawable..</p> + * <p><strong>Note</strong>: changing this property will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing this property.</p> + * + * @param gradient The type of the gradient: {@link #LINEAR_GRADIENT}, + * {@link #RADIAL_GRADIENT} or {@link #SWEEP_GRADIENT} + * + * @see #mutate() + */ public void setGradientType(int gradient) { mGradientState.setGradientType(gradient); mRectIsDirty = true; invalidateSelf(); } + /** + * <p>Sets the center location of the gradient. The radius is honored only when + * the gradient type is set to {@link #RADIAL_GRADIENT} or {@link #SWEEP_GRADIENT}.</p> + * <p><strong>Note</strong>: changing this property will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing this property.</p> + * + * @param x The x coordinate of the gradient's center + * @param y The y coordinate of the gradient's center + * + * @see #mutate() + * @see #setGradientType(int) + */ public void setGradientCenter(float x, float y) { mGradientState.setGradientCenter(x, y); mRectIsDirty = true; invalidateSelf(); } + /** + * <p>Sets the radius of the gradient. The radius is honored only when the + * gradient type is set to {@link #RADIAL_GRADIENT}.</p> + * <p><strong>Note</strong>: changing this property will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing this property.</p> + * + * @param gradientRadius The radius of the gradient in pixels + * + * @see #mutate() + * @see #setGradientType(int) + */ public void setGradientRadius(float gradientRadius) { mGradientState.setGradientRadius(gradientRadius); mRectIsDirty = true; invalidateSelf(); } + /** + * <p>Sets whether or not this drawable will honor its <code>level</code> + * property.</p> + * <p><strong>Note</strong>: changing this property will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing this property.</p> + * + * @param useLevel True if this drawable should honor its level, false otherwise + * + * @see #mutate() + * @see #setLevel(int) + * @see #getLevel() + */ public void setUseLevel(boolean useLevel) { mGradientState.mUseLevel = useLevel; mRectIsDirty = true; @@ -261,6 +379,47 @@ public class GradientDrawable extends Drawable { return alpha * scale >> 8; } + /** + * Returns the orientation of the gradient defined in this drawable. + */ + public Orientation getOrientation() { + return mGradientState.mOrientation; + } + + /** + * <p>Changes the orientation of the gradient defined in this drawable.</p> + * <p><strong>Note</strong>: changing orientation will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing the orientation.</p> + * + * @param orientation The desired orientation (angle) of the gradient + * + * @see #mutate() + */ + public void setOrientation(Orientation orientation) { + mGradientState.mOrientation = orientation; + mRectIsDirty = true; + invalidateSelf(); + } + + /** + * <p>Sets the colors used to draw the gradient. Each color is specified as an + * ARGB integer and the array must contain at least 2 colors.</p> + * <p><strong>Note</strong>: changing orientation will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing the orientation.</p> + * + * @param colors 2 or more ARGB colors + * + * @see #mutate() + * @see #setColor(int) + */ + public void setColors(int[] colors) { + mGradientState.setColors(colors); + mRectIsDirty = true; + invalidateSelf(); + } + @Override public void draw(Canvas canvas) { if (!ensureValidRect()) { @@ -442,6 +601,17 @@ public class GradientDrawable extends Drawable { return ringPath; } + /** + * <p>Changes this drawbale to use a single color instead of a gradient.</p> + * <p><strong>Note</strong>: changing orientation will affect all instances + * of a drawable loaded from a resource. It is recommended to invoke + * {@link #mutate()} before changing the orientation.</p> + * + * @param argb The color used to fill the shape + * + * @see #mutate() + * @see #setColors(int[]) + */ public void setColor(int argb) { mGradientState.setSolidColor(argb); mFillPaint.setColor(argb); @@ -450,10 +620,9 @@ public class GradientDrawable extends Drawable { @Override public int getChangingConfigurations() { - return super.getChangingConfigurations() - | mGradientState.mChangingConfigurations; + return super.getChangingConfigurations() | mGradientState.mChangingConfigurations; } - + @Override public void setAlpha(int alpha) { if (alpha != mAlpha) { @@ -480,7 +649,6 @@ public class GradientDrawable extends Drawable { @Override public int getOpacity() { - // XXX need to figure out the actual opacity... return PixelFormat.TRANSLUCENT; } @@ -911,11 +1079,6 @@ public class GradientDrawable extends Drawable { private float mGradientRadius = 0.5f; private boolean mUseLevel; private boolean mUseLevelForShape; - - - GradientState() { - mOrientation = Orientation.TOP_BOTTOM; - } GradientState(Orientation orientation, int[] colors) { mOrientation = orientation; @@ -987,6 +1150,11 @@ public class GradientDrawable extends Drawable { mCenterY = y; } + public void setColors(int[] colors) { + mHasSolidColor = false; + mColors = colors; + } + public void setSolidColor(int argb) { mHasSolidColor = true; mSolidColor = argb; @@ -1055,4 +1223,3 @@ public class GradientDrawable extends Drawable { } } } - diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp index bc1db4dc3310..07bf7bf62f82 100644 --- a/graphics/jni/android_renderscript_RenderScript.cpp +++ b/graphics/jni/android_renderscript_RenderScript.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2011-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. @@ -567,7 +567,7 @@ nAllocationElementData1D(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint len = _env->GetArrayLength(data); LOG_API("nAllocationElementData1D, con(%p), alloc(%p), offset(%i), comp(%i), len(%i), sizeBytes(%i)", con, (RsAllocation)alloc, offset, compIdx, len, sizeBytes); jbyte *ptr = _env->GetByteArrayElements(data, NULL); - rsAllocation1DElementData(con, (RsAllocation)alloc, offset, lod, ptr, compIdx, sizeBytes); + rsAllocation1DElementData(con, (RsAllocation)alloc, offset, lod, ptr, sizeBytes, compIdx); _env->ReleaseByteArrayElements(data, ptr, JNI_ABORT); } diff --git a/include/media/IOMX.h b/include/media/IOMX.h index c4cc947a8902..a295e9a19281 100644 --- a/include/media/IOMX.h +++ b/include/media/IOMX.h @@ -42,10 +42,10 @@ public: typedef void *buffer_id; typedef void *node_id; - // Given the calling process' pid, returns true iff + // Given a node_id and the calling process' pid, returns true iff // the implementation of the OMX interface lives in the same // process. - virtual bool livesLocally(pid_t pid) = 0; + virtual bool livesLocally(node_id node, pid_t pid) = 0; struct ComponentInfo { String8 mName; diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index d1af1a3161a8..0ef846903343 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -55,6 +55,16 @@ Caches::Caches(): Singleton<Caches>(), mInitialized(false) { glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); + if (extensions.hasDebugMarker()) { + eventMark = glInsertEventMarkerEXT; + startMark = glPushGroupMarkerEXT; + endMark = glPopGroupMarkerEXT; + } else { + eventMark = eventMarkNull; + startMark = startMarkNull; + endMark = endMarkNull; + } + init(); mDebugLevel = readDebugLevel(); diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index 409584fb0e06..f8c7bccad628 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -240,7 +240,15 @@ public: GammaFontRenderer fontRenderer; ResourceCache resourceCache; + PFNGLINSERTEVENTMARKEREXTPROC eventMark; + PFNGLPUSHGROUPMARKEREXTPROC startMark; + PFNGLPOPGROUPMARKEREXTPROC endMark; + private: + static void eventMarkNull(GLsizei length, const GLchar *marker) { } + static void startMarkNull(GLsizei length, const GLchar *marker) { } + static void endMarkNull() { } + GLuint mCurrentBuffer; GLuint mCurrentIndicesBuffer; void* mCurrentPositionPointer; diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index ee935e44381f..1a11fbcfad68 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -19,9 +19,10 @@ #include "DisplayListLogBuffer.h" #include "DisplayListRenderer.h" -#include <utils/String8.h> #include "Caches.h" +#include <utils/String8.h> + namespace android { namespace uirenderer { @@ -217,7 +218,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) { indent[i] = ' '; } indent[count] = '\0'; - ALOGD("%sStart display list (%p)", (char*) indent + 2, this); + ALOGD("%sStart display list (%p, %s)", (char*) indent + 2, this, mName.string()); int saveCount = renderer.getSaveCount() - 1; @@ -562,9 +563,11 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) indent[i] = ' '; } indent[count] = '\0'; - DISPLAY_LIST_LOGD("%sStart display list (%p)", (char*) indent + 2, this); + DISPLAY_LIST_LOGD("%sStart display list (%p, %s)", (char*) indent + 2, this, mName.string()); #endif + renderer.startMark(mName.string()); + DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); int saveCount = renderer.getSaveCount() - 1; while (!mReader.eof()) { @@ -575,7 +578,9 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) case DrawGLFunction: { Functor *functor = (Functor *) getInt(); DISPLAY_LIST_LOGD("%s%s %p", (char*) indent, OP_NAMES[op], functor); + renderer.startMark("GL functor"); needsInvalidate |= renderer.callDrawGLFunction(functor, dirty); + renderer.endMark(); } break; case Save: { @@ -934,6 +939,8 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) } } + renderer.endMark(); + DISPLAY_LIST_LOGD("%sDone, returning %d", (char*) indent + 2, needsInvalidate); return needsInvalidate; } diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index 6fe4205b7443..46506e4e9a96 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -28,6 +28,8 @@ #include <cutils/compiler.h> +#include <utils/String8.h> + #include "DisplayListLogBuffer.h" #include "OpenGLRenderer.h" #include "utils/Functor.h" @@ -128,6 +130,12 @@ public: return mIsRenderable; } + void setName(const char* name) { + if (name) { + mName.setTo(name); + } + } + private: void init(); @@ -224,6 +232,8 @@ private: size_t mSize; bool mIsRenderable; + + String8 mName; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h index 1069e3f7fbd0..f11fecc7ae9c 100644 --- a/libs/hwui/Extensions.h +++ b/libs/hwui/Extensions.h @@ -67,6 +67,7 @@ public: mHasNPot = hasExtension("GL_OES_texture_npot"); mHasFramebufferFetch = hasExtension("GL_NV_shader_framebuffer_fetch"); mHasDiscardFramebuffer = hasExtension("GL_EXT_discard_framebuffer"); + mHasDebugMarker = hasExtension("GL_EXT_debug_marker"); const char* vendor = (const char*) glGetString(GL_VENDOR); EXT_LOGD("Vendor: %s", vendor); @@ -82,6 +83,7 @@ public: inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; } inline bool needsHighpTexCoords() const { return mNeedsHighpTexCoords; } inline bool hasDiscardFramebuffer() const { return mHasDiscardFramebuffer; } + inline bool hasDebugMarker() const { return mHasDebugMarker; } bool hasExtension(const char* extension) const { const String8 s(extension); @@ -101,6 +103,7 @@ private: bool mNeedsHighpTexCoords; bool mHasFramebufferFetch; bool mHasDiscardFramebuffer; + bool mHasDebugMarker; }; // class Extensions }; // namespace uirenderer diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index cc0e05ef914b..bd213d52b453 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -124,9 +124,25 @@ OpenGLRenderer::~OpenGLRenderer() { } /////////////////////////////////////////////////////////////////////////////// +// Debug +/////////////////////////////////////////////////////////////////////////////// + +void OpenGLRenderer::startMark(const char* name) const { + mCaches.startMark(0, name); +} + +void OpenGLRenderer::endMark() const { + mCaches.endMark(); +} + +/////////////////////////////////////////////////////////////////////////////// // Setup /////////////////////////////////////////////////////////////////////////////// +uint32_t OpenGLRenderer::getStencilSize() { + return STENCIL_BUFFER_SIZE; +} + void OpenGLRenderer::setViewport(int width, int height) { mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1); diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index ae355bba4e9b..3c2d09e81769 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -141,6 +141,11 @@ public: SkPaint* filterPaint(SkPaint* paint); + ANDROID_API static uint32_t getStencilSize(); + + void startMark(const char* name) const; + void endMark() const; + protected: /** * Compose the layer defined in the current snapshot with the layer diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 2eae0f18bbb8..71fb8da6e817 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -37,6 +37,11 @@ // Textures used by layers must have dimensions multiples of this number #define LAYER_SIZE 64 +// Defines the size in bits of the stencil buffer +// Note: We only want 1 bit, but in practice we'll get 8 bits on all GPUs +// for the foreseeable future +#define STENCIL_BUFFER_SIZE 0 + /** * Debug level for app developers. */ diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec index 20b1f52104da..6887b22095e1 100644 --- a/libs/rs/rs.spec +++ b/libs/rs/rs.spec @@ -162,7 +162,7 @@ Allocation1DElementData { param uint32_t x param uint32_t lod param const void *data - param uint32_t comp_offset + param size_t comp_offset } Allocation2DData { @@ -183,7 +183,7 @@ Allocation2DElementData { param uint32_t lod param RsAllocationCubemapFace face param const void *data - param uint32_t element_offset + param size_t element_offset } AllocationGenerateMipmaps { diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp index 2773d5c661c5..972e3d655115 100644 --- a/libs/rs/rsAllocation.cpp +++ b/libs/rs/rsAllocation.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * Copyright (C) 2009-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. @@ -71,11 +71,11 @@ void Allocation::read(void *data) { } void Allocation::data(Context *rsc, uint32_t xoff, uint32_t lod, - uint32_t count, const void *data, uint32_t sizeBytes) { - const uint32_t eSize = mHal.state.type->getElementSizeBytes(); + uint32_t count, const void *data, size_t sizeBytes) { + const size_t eSize = mHal.state.type->getElementSizeBytes(); if ((count * eSize) != sizeBytes) { - ALOGE("Allocation::subData called with mismatched size expected %i, got %i", + ALOGE("Allocation::subData called with mismatched size expected %zu, got %zu", (count * eSize), sizeBytes); mHal.state.type->dumpLOGV("type info"); return; @@ -86,14 +86,14 @@ void Allocation::data(Context *rsc, uint32_t xoff, uint32_t lod, } void Allocation::data(Context *rsc, uint32_t xoff, uint32_t yoff, uint32_t lod, RsAllocationCubemapFace face, - uint32_t w, uint32_t h, const void *data, uint32_t sizeBytes) { - const uint32_t eSize = mHal.state.elementSizeBytes; - const uint32_t lineSize = eSize * w; + uint32_t w, uint32_t h, const void *data, size_t sizeBytes) { + const size_t eSize = mHal.state.elementSizeBytes; + const size_t lineSize = eSize * w; //ALOGE("data2d %p, %i %i %i %i %i %i %p %i", this, xoff, yoff, lod, face, w, h, data, sizeBytes); if ((lineSize * h) != sizeBytes) { - ALOGE("Allocation size mismatch, expected %i, got %i", (lineSize * h), sizeBytes); + ALOGE("Allocation size mismatch, expected %zu, got %zu", (lineSize * h), sizeBytes); rsAssert(!"Allocation::subData called with mismatched size"); return; } @@ -104,12 +104,12 @@ void Allocation::data(Context *rsc, uint32_t xoff, uint32_t yoff, uint32_t lod, void Allocation::data(Context *rsc, uint32_t xoff, uint32_t yoff, uint32_t zoff, uint32_t lod, RsAllocationCubemapFace face, - uint32_t w, uint32_t h, uint32_t d, const void *data, uint32_t sizeBytes) { + uint32_t w, uint32_t h, uint32_t d, const void *data, size_t sizeBytes) { } void Allocation::elementData(Context *rsc, uint32_t x, const void *data, - uint32_t cIdx, uint32_t sizeBytes) { - uint32_t eSize = mHal.state.elementSizeBytes; + uint32_t cIdx, size_t sizeBytes) { + size_t eSize = mHal.state.elementSizeBytes; if (cIdx >= mHal.state.type->getElement()->getFieldCount()) { ALOGE("Error Allocation::subElementData component %i out of range.", cIdx); @@ -125,7 +125,7 @@ void Allocation::elementData(Context *rsc, uint32_t x, const void *data, const Element * e = mHal.state.type->getElement()->getField(cIdx); if (sizeBytes != e->getSizeBytes()) { - ALOGE("Error Allocation::subElementData data size %i does not match field size %zu.", sizeBytes, e->getSizeBytes()); + ALOGE("Error Allocation::subElementData data size %zu does not match field size %zu.", sizeBytes, e->getSizeBytes()); rsc->setError(RS_ERROR_BAD_VALUE, "subElementData bad size."); return; } @@ -135,8 +135,8 @@ void Allocation::elementData(Context *rsc, uint32_t x, const void *data, } void Allocation::elementData(Context *rsc, uint32_t x, uint32_t y, - const void *data, uint32_t cIdx, uint32_t sizeBytes) { - uint32_t eSize = mHal.state.elementSizeBytes; + const void *data, uint32_t cIdx, size_t sizeBytes) { + size_t eSize = mHal.state.elementSizeBytes; if (x >= mHal.state.dimensionX) { ALOGE("Error Allocation::subElementData X offset %i out of range.", x); @@ -159,7 +159,7 @@ void Allocation::elementData(Context *rsc, uint32_t x, uint32_t y, const Element * e = mHal.state.type->getElement()->getField(cIdx); if (sizeBytes != e->getSizeBytes()) { - ALOGE("Error Allocation::subElementData data size %i does not match field size %zu.", sizeBytes, e->getSizeBytes()); + ALOGE("Error Allocation::subElementData data size %zu does not match field size %zu.", sizeBytes, e->getSizeBytes()); rsc->setError(RS_ERROR_BAD_VALUE, "subElementData bad size."); return; } @@ -249,7 +249,7 @@ void Allocation::writePackedData(const Type *type, delete[] sizeUnpadded; } -void Allocation::unpackVec3Allocation(const void *data, uint32_t dataSize) { +void Allocation::unpackVec3Allocation(const void *data, size_t dataSize) { const uint8_t *src = (const uint8_t*)data; uint8_t *dst = (uint8_t*)getPtr(); @@ -519,13 +519,13 @@ void rsi_Allocation1DData(Context *rsc, RsAllocation va, uint32_t xoff, uint32_t } void rsi_Allocation2DElementData(Context *rsc, RsAllocation va, uint32_t x, uint32_t y, uint32_t lod, RsAllocationCubemapFace face, - const void *data, size_t eoff, uint32_t sizeBytes) { // TODO: this seems wrong, eoff and sizeBytes may be swapped + const void *data, size_t sizeBytes, size_t eoff) { Allocation *a = static_cast<Allocation *>(va); a->elementData(rsc, x, y, data, eoff, sizeBytes); } void rsi_Allocation1DElementData(Context *rsc, RsAllocation va, uint32_t x, uint32_t lod, - const void *data, size_t eoff, uint32_t sizeBytes) { // TODO: this seems wrong, eoff and sizeBytes may be swapped + const void *data, size_t sizeBytes, size_t eoff) { Allocation *a = static_cast<Allocation *>(va); a->elementData(rsc, x, data, eoff, sizeBytes); } diff --git a/libs/rs/rsAllocation.h b/libs/rs/rsAllocation.h index 4ce863ab927f..20201ca793ea 100644 --- a/libs/rs/rsAllocation.h +++ b/libs/rs/rsAllocation.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * Copyright (C) 2009-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. @@ -80,16 +80,16 @@ public: void resize1D(Context *rsc, uint32_t dimX); void resize2D(Context *rsc, uint32_t dimX, uint32_t dimY); - void data(Context *rsc, uint32_t xoff, uint32_t lod, uint32_t count, const void *data, uint32_t sizeBytes); + void data(Context *rsc, uint32_t xoff, uint32_t lod, uint32_t count, const void *data, size_t sizeBytes); void data(Context *rsc, uint32_t xoff, uint32_t yoff, uint32_t lod, RsAllocationCubemapFace face, - uint32_t w, uint32_t h, const void *data, uint32_t sizeBytes); + uint32_t w, uint32_t h, const void *data, size_t sizeBytes); void data(Context *rsc, uint32_t xoff, uint32_t yoff, uint32_t zoff, uint32_t lod, RsAllocationCubemapFace face, - uint32_t w, uint32_t h, uint32_t d, const void *data, uint32_t sizeBytes); + uint32_t w, uint32_t h, uint32_t d, const void *data, size_t sizeBytes); void elementData(Context *rsc, uint32_t x, - const void *data, uint32_t elementOff, uint32_t sizeBytes); + const void *data, uint32_t elementOff, size_t sizeBytes); void elementData(Context *rsc, uint32_t x, uint32_t y, - const void *data, uint32_t elementOff, uint32_t sizeBytes); + const void *data, uint32_t elementOff, size_t sizeBytes); void read(void *data); @@ -138,7 +138,7 @@ private: uint32_t getPackedSize() const; static void writePackedData(const Type *type, uint8_t *dst, const uint8_t *src, bool dstPadded); - void unpackVec3Allocation(const void *data, uint32_t dataSize); + void unpackVec3Allocation(const void *data, size_t dataSize); void packVec3Allocation(OStream *stream) const; }; diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp index d2f5f71b572d..27c7e03b0cd1 100644 --- a/media/libmedia/IOMX.cpp +++ b/media/libmedia/IOMX.cpp @@ -59,9 +59,10 @@ public: : BpInterface<IOMX>(impl) { } - virtual bool livesLocally(pid_t pid) { + virtual bool livesLocally(node_id node, pid_t pid) { Parcel data, reply; data.writeInterfaceToken(IOMX::getInterfaceDescriptor()); + data.writeIntPtr((intptr_t)node); data.writeInt32(pid); remote()->transact(LIVES_LOCALLY, data, &reply); @@ -417,7 +418,9 @@ status_t BnOMX::onTransact( case LIVES_LOCALLY: { CHECK_INTERFACE(IOMX, data, reply); - reply->writeInt32(livesLocally((pid_t)data.readInt32())); + node_id node = (void *)data.readIntPtr(); + pid_t pid = (pid_t)data.readInt32(); + reply->writeInt32(livesLocally(node, pid)); return OK; } diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 4d61067508c8..a452ad57d34e 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -74,6 +74,7 @@ LOCAL_SHARED_LIBRARIES := \ libcrypto \ libssl \ libgui \ + libstagefright_omx \ LOCAL_STATIC_LIBRARIES := \ libstagefright_color_conversion \ diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 8480b6d7466f..8073af88ce76 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -30,7 +30,7 @@ #include "include/MPEG2TSExtractor.h" #include "include/WVMExtractor.h" -#include "timedtext/TimedTextPlayer.h" +#include "timedtext/TimedTextDriver.h" #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> @@ -192,7 +192,7 @@ AwesomePlayer::AwesomePlayer() mVideoBuffer(NULL), mDecryptHandle(NULL), mLastVideoTimeUs(-1), - mTextPlayer(NULL) { + mTextDriver(NULL) { CHECK_EQ(mClient.connect(), (status_t)OK); DataSource::RegisterDefaultSniffers(); @@ -530,9 +530,9 @@ void AwesomePlayer::reset_l() { delete mAudioPlayer; mAudioPlayer = NULL; - if (mTextPlayer != NULL) { - delete mTextPlayer; - mTextPlayer = NULL; + if (mTextDriver != NULL) { + delete mTextDriver; + mTextDriver = NULL; } mVideoRenderer.clear(); @@ -1118,7 +1118,7 @@ status_t AwesomePlayer::pause_l(bool at_eos) { } if (mFlags & TEXTPLAYER_STARTED) { - mTextPlayer->pause(); + mTextDriver->pause(); modifyFlags(TEXT_RUNNING, CLEAR); } @@ -1272,9 +1272,9 @@ status_t AwesomePlayer::seekTo(int64_t timeUs) { } status_t AwesomePlayer::setTimedTextTrackIndex(int32_t index) { - if (mTextPlayer != NULL) { + if (mTextDriver != NULL) { if (index >= 0) { // to turn on a text track - status_t err = mTextPlayer->setTimedTextTrackIndex(index); + status_t err = mTextDriver->setTimedTextTrackIndex(index); if (err != OK) { return err; } @@ -1290,7 +1290,7 @@ status_t AwesomePlayer::setTimedTextTrackIndex(int32_t index) { modifyFlags(TEXTPLAYER_STARTED, CLEAR); } - return mTextPlayer->setTimedTextTrackIndex(index); + return mTextDriver->setTimedTextTrackIndex(index); } } else { return INVALID_OPERATION; @@ -1319,7 +1319,7 @@ status_t AwesomePlayer::seekTo_l(int64_t timeUs) { seekAudioIfNecessary_l(); if (mFlags & TEXTPLAYER_STARTED) { - mTextPlayer->seekTo(mSeekTimeUs); + mTextDriver->seekToAsync(mSeekTimeUs); } if (!(mFlags & PLAYING)) { @@ -1364,11 +1364,11 @@ void AwesomePlayer::addTextSource(sp<MediaSource> source) { Mutex::Autolock autoLock(mTimedTextLock); CHECK(source != NULL); - if (mTextPlayer == NULL) { - mTextPlayer = new TimedTextPlayer(this, mListener, &mQueue); + if (mTextDriver == NULL) { + mTextDriver = new TimedTextDriver(mListener); } - mTextPlayer->addTextSource(source); + mTextDriver->addInBandTextSource(source); } status_t AwesomePlayer::initAudioDecoder() { @@ -1695,7 +1695,7 @@ void AwesomePlayer::onVideoEvent() { } if ((mFlags & TEXTPLAYER_STARTED) && !(mFlags & (TEXT_RUNNING | SEEK_PREVIEW))) { - mTextPlayer->resume(); + mTextDriver->resume(); modifyFlags(TEXT_RUNNING, SET); } @@ -2241,11 +2241,11 @@ status_t AwesomePlayer::setParameter(int key, const Parcel &request) { case KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE: { Mutex::Autolock autoLock(mTimedTextLock); - if (mTextPlayer == NULL) { - mTextPlayer = new TimedTextPlayer(this, mListener, &mQueue); + if (mTextDriver == NULL) { + mTextDriver = new TimedTextDriver(mListener); } - return mTextPlayer->setParameter(key, request); + return mTextDriver->addOutOfBandTextSource(request); } case KEY_PARAMETER_CACHE_STAT_COLLECT_FREQ_MS: { diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index bc8801514346..6c95d4e5d351 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -20,7 +20,6 @@ #include "include/MPEG4Extractor.h" #include "include/SampleTable.h" #include "include/ESDS.h" -#include "timedtext/TimedTextPlayer.h" #include <arpa/inet.h> @@ -2430,4 +2429,3 @@ bool SniffMPEG4( } } // namespace android - diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp index 9de873eceed5..7a805aa73afc 100644 --- a/media/libstagefright/OMXClient.cpp +++ b/media/libstagefright/OMXClient.cpp @@ -20,11 +20,299 @@ #include <binder/IServiceManager.h> #include <media/IMediaPlayerService.h> -#include <media/stagefright/MediaDebug.h> +#include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/OMXClient.h> +#include <utils/KeyedVector.h> + +#include "include/OMX.h" namespace android { +struct MuxOMX : public IOMX { + MuxOMX(const sp<IOMX> &remoteOMX); + virtual ~MuxOMX(); + + virtual IBinder *onAsBinder() { return NULL; } + + virtual bool livesLocally(node_id node, pid_t pid); + + virtual status_t listNodes(List<ComponentInfo> *list); + + virtual status_t allocateNode( + const char *name, const sp<IOMXObserver> &observer, + node_id *node); + + virtual status_t freeNode(node_id node); + + virtual status_t sendCommand( + node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param); + + virtual status_t getParameter( + node_id node, OMX_INDEXTYPE index, + void *params, size_t size); + + virtual status_t setParameter( + node_id node, OMX_INDEXTYPE index, + const void *params, size_t size); + + virtual status_t getConfig( + node_id node, OMX_INDEXTYPE index, + void *params, size_t size); + + virtual status_t setConfig( + node_id node, OMX_INDEXTYPE index, + const void *params, size_t size); + + virtual status_t getState( + node_id node, OMX_STATETYPE* state); + + virtual status_t storeMetaDataInBuffers( + node_id node, OMX_U32 port_index, OMX_BOOL enable); + + virtual status_t enableGraphicBuffers( + node_id node, OMX_U32 port_index, OMX_BOOL enable); + + virtual status_t getGraphicBufferUsage( + node_id node, OMX_U32 port_index, OMX_U32* usage); + + virtual status_t useBuffer( + node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms, + buffer_id *buffer); + + virtual status_t useGraphicBuffer( + node_id node, OMX_U32 port_index, + const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer); + + virtual status_t allocateBuffer( + node_id node, OMX_U32 port_index, size_t size, + buffer_id *buffer, void **buffer_data); + + virtual status_t allocateBufferWithBackup( + node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms, + buffer_id *buffer); + + virtual status_t freeBuffer( + node_id node, OMX_U32 port_index, buffer_id buffer); + + virtual status_t fillBuffer(node_id node, buffer_id buffer); + + virtual status_t emptyBuffer( + node_id node, + buffer_id buffer, + OMX_U32 range_offset, OMX_U32 range_length, + OMX_U32 flags, OMX_TICKS timestamp); + + virtual status_t getExtensionIndex( + node_id node, + const char *parameter_name, + OMX_INDEXTYPE *index); + +private: + mutable Mutex mLock; + + sp<IOMX> mRemoteOMX; + sp<IOMX> mLocalOMX; + + KeyedVector<node_id, bool> mIsLocalNode; + + bool isLocalNode(node_id node) const; + bool isLocalNode_l(node_id node) const; + const sp<IOMX> &getOMX(node_id node) const; + const sp<IOMX> &getOMX_l(node_id node) const; + + static bool IsSoftwareComponent(const char *name); + + DISALLOW_EVIL_CONSTRUCTORS(MuxOMX); +}; + +MuxOMX::MuxOMX(const sp<IOMX> &remoteOMX) + : mRemoteOMX(remoteOMX) { +} + +MuxOMX::~MuxOMX() { +} + +bool MuxOMX::isLocalNode(node_id node) const { + Mutex::Autolock autoLock(mLock); + + return isLocalNode_l(node); +} + +bool MuxOMX::isLocalNode_l(node_id node) const { + return mIsLocalNode.indexOfKey(node) >= 0; +} + +// static +bool MuxOMX::IsSoftwareComponent(const char *name) { + return !strncasecmp(name, "OMX.google.", 11); +} + +const sp<IOMX> &MuxOMX::getOMX(node_id node) const { + return isLocalNode(node) ? mLocalOMX : mRemoteOMX; +} + +const sp<IOMX> &MuxOMX::getOMX_l(node_id node) const { + return isLocalNode_l(node) ? mLocalOMX : mRemoteOMX; +} + +bool MuxOMX::livesLocally(node_id node, pid_t pid) { + return getOMX(node)->livesLocally(node, pid); +} + +status_t MuxOMX::listNodes(List<ComponentInfo> *list) { + Mutex::Autolock autoLock(mLock); + + if (mLocalOMX == NULL) { + mLocalOMX = new OMX; + } + + return mLocalOMX->listNodes(list); +} + +status_t MuxOMX::allocateNode( + const char *name, const sp<IOMXObserver> &observer, + node_id *node) { + Mutex::Autolock autoLock(mLock); + + sp<IOMX> omx; + + if (IsSoftwareComponent(name)) { + if (mLocalOMX == NULL) { + mLocalOMX = new OMX; + } + omx = mLocalOMX; + } else { + omx = mRemoteOMX; + } + + status_t err = omx->allocateNode(name, observer, node); + + if (err != OK) { + return err; + } + + if (omx == mLocalOMX) { + mIsLocalNode.add(*node, true); + } + + return OK; +} + +status_t MuxOMX::freeNode(node_id node) { + Mutex::Autolock autoLock(mLock); + + status_t err = getOMX_l(node)->freeNode(node); + + if (err != OK) { + return err; + } + + mIsLocalNode.removeItem(node); + + return OK; +} + +status_t MuxOMX::sendCommand( + node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) { + return getOMX(node)->sendCommand(node, cmd, param); +} + +status_t MuxOMX::getParameter( + node_id node, OMX_INDEXTYPE index, + void *params, size_t size) { + return getOMX(node)->getParameter(node, index, params, size); +} + +status_t MuxOMX::setParameter( + node_id node, OMX_INDEXTYPE index, + const void *params, size_t size) { + return getOMX(node)->setParameter(node, index, params, size); +} + +status_t MuxOMX::getConfig( + node_id node, OMX_INDEXTYPE index, + void *params, size_t size) { + return getOMX(node)->getConfig(node, index, params, size); +} + +status_t MuxOMX::setConfig( + node_id node, OMX_INDEXTYPE index, + const void *params, size_t size) { + return getOMX(node)->setConfig(node, index, params, size); +} + +status_t MuxOMX::getState( + node_id node, OMX_STATETYPE* state) { + return getOMX(node)->getState(node, state); +} + +status_t MuxOMX::storeMetaDataInBuffers( + node_id node, OMX_U32 port_index, OMX_BOOL enable) { + return getOMX(node)->storeMetaDataInBuffers(node, port_index, enable); +} + +status_t MuxOMX::enableGraphicBuffers( + node_id node, OMX_U32 port_index, OMX_BOOL enable) { + return getOMX(node)->enableGraphicBuffers(node, port_index, enable); +} + +status_t MuxOMX::getGraphicBufferUsage( + node_id node, OMX_U32 port_index, OMX_U32* usage) { + return getOMX(node)->getGraphicBufferUsage(node, port_index, usage); +} + +status_t MuxOMX::useBuffer( + node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms, + buffer_id *buffer) { + return getOMX(node)->useBuffer(node, port_index, params, buffer); +} + +status_t MuxOMX::useGraphicBuffer( + node_id node, OMX_U32 port_index, + const sp<GraphicBuffer> &graphicBuffer, buffer_id *buffer) { + return getOMX(node)->useGraphicBuffer( + node, port_index, graphicBuffer, buffer); +} + +status_t MuxOMX::allocateBuffer( + node_id node, OMX_U32 port_index, size_t size, + buffer_id *buffer, void **buffer_data) { + return getOMX(node)->allocateBuffer( + node, port_index, size, buffer, buffer_data); +} + +status_t MuxOMX::allocateBufferWithBackup( + node_id node, OMX_U32 port_index, const sp<IMemory> ¶ms, + buffer_id *buffer) { + return getOMX(node)->allocateBufferWithBackup( + node, port_index, params, buffer); +} + +status_t MuxOMX::freeBuffer( + node_id node, OMX_U32 port_index, buffer_id buffer) { + return getOMX(node)->freeBuffer(node, port_index, buffer); +} + +status_t MuxOMX::fillBuffer(node_id node, buffer_id buffer) { + return getOMX(node)->fillBuffer(node, buffer); +} + +status_t MuxOMX::emptyBuffer( + node_id node, + buffer_id buffer, + OMX_U32 range_offset, OMX_U32 range_length, + OMX_U32 flags, OMX_TICKS timestamp) { + return getOMX(node)->emptyBuffer( + node, buffer, range_offset, range_length, flags, timestamp); +} + +status_t MuxOMX::getExtensionIndex( + node_id node, + const char *parameter_name, + OMX_INDEXTYPE *index) { + return getOMX(node)->getExtensionIndex(node, parameter_name, index); +} + OMXClient::OMXClient() { } @@ -38,6 +326,11 @@ status_t OMXClient::connect() { mOMX = service->getOMX(); CHECK(mOMX.get() != NULL); + if (!mOMX->livesLocally(NULL /* node */, getpid())) { + ALOGI("Using client-side OMX mux."); + mOMX = new MuxOMX(mOMX); + } + return OK; } diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 7597f642ac50..1f26cbee44b7 100755 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -1479,7 +1479,7 @@ OMXCodec::OMXCodec( const sp<MediaSource> &source, const sp<ANativeWindow> &nativeWindow) : mOMX(omx), - mOMXLivesLocally(omx->livesLocally(getpid())), + mOMXLivesLocally(omx->livesLocally(node, getpid())), mNode(node), mQuirks(quirks), mFlags(flags), diff --git a/media/libstagefright/codecs/aacdec/SoftAAC.cpp b/media/libstagefright/codecs/aacdec/SoftAAC.cpp index da9d28067793..ea6c3608bbf6 100644 --- a/media/libstagefright/codecs/aacdec/SoftAAC.cpp +++ b/media/libstagefright/codecs/aacdec/SoftAAC.cpp @@ -218,6 +218,18 @@ OMX_ERRORTYPE SoftAAC::internalSetParameter( return OMX_ErrorNone; } + case OMX_IndexParamAudioPcm: + { + const OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams = + (OMX_AUDIO_PARAM_PCMMODETYPE *)params; + + if (pcmParams->nPortIndex != 1) { + return OMX_ErrorUndefined; + } + + return OMX_ErrorNone; + } + default: return SimpleSoftOMXComponent::internalSetParameter(index, params); } diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index 82c647623e5b..a7a3d4781633 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -41,7 +41,7 @@ struct ISurfaceTexture; class DrmManagerClinet; class DecryptHandle; -class TimedTextPlayer; +class TimedTextDriver; struct WVMExtractor; struct AwesomeRenderer : public RefBase { @@ -232,7 +232,7 @@ private: sp<DecryptHandle> mDecryptHandle; int64_t mLastVideoTimeUs; - TimedTextPlayer *mTextPlayer; + TimedTextDriver *mTextDriver; mutable Mutex mTimedTextLock; sp<WVMExtractor> mWVMExtractor; @@ -326,4 +326,3 @@ private: } // namespace android #endif // AWESOME_PLAYER_H_ - diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h index 53e764fd504b..2c87b34e42a8 100644 --- a/media/libstagefright/include/OMX.h +++ b/media/libstagefright/include/OMX.h @@ -31,7 +31,7 @@ class OMX : public BnOMX, public: OMX(); - virtual bool livesLocally(pid_t pid); + virtual bool livesLocally(node_id node, pid_t pid); virtual status_t listNodes(List<ComponentInfo> *list); diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index 694b12d8279b..ace883c7f6b5 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -185,7 +185,7 @@ void OMX::binderDied(const wp<IBinder> &the_late_who) { instance->onObserverDied(mMaster); } -bool OMX::livesLocally(pid_t pid) { +bool OMX::livesLocally(node_id node, pid_t pid) { return pid == getpid(); } diff --git a/media/libstagefright/timedtext/Android.mk b/media/libstagefright/timedtext/Android.mk index 59d0e154c597..8b23dee0c0c7 100644 --- a/media/libstagefright/timedtext/Android.mk +++ b/media/libstagefright/timedtext/Android.mk @@ -3,7 +3,10 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ TextDescriptions.cpp \ - TimedTextParser.cpp \ + TimedTextDriver.cpp \ + TimedTextInBandSource.cpp \ + TimedTextSource.cpp \ + TimedTextSRTSource.cpp \ TimedTextPlayer.cpp LOCAL_CFLAGS += -Wno-multichar diff --git a/media/libstagefright/timedtext/TimedTextDriver.cpp b/media/libstagefright/timedtext/TimedTextDriver.cpp new file mode 100644 index 000000000000..9ec94153fc89 --- /dev/null +++ b/media/libstagefright/timedtext/TimedTextDriver.cpp @@ -0,0 +1,223 @@ + /* + * 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. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "TimedTextDriver" +#include <utils/Log.h> + +#include <binder/IPCThreadState.h> + +#include <media/MediaPlayerInterface.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/DataSource.h> +#include <media/stagefright/Utils.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/ALooper.h> + +#include "TimedTextDriver.h" + +#include "TextDescriptions.h" +#include "TimedTextPlayer.h" +#include "TimedTextSource.h" + +namespace android { + +TimedTextDriver::TimedTextDriver( + const wp<MediaPlayerBase> &listener) + : mLooper(new ALooper), + mListener(listener), + mState(UNINITIALIZED) { + mLooper->setName("TimedTextDriver"); + mLooper->start(); + mPlayer = new TimedTextPlayer(listener); + mLooper->registerHandler(mPlayer); +} + +TimedTextDriver::~TimedTextDriver() { + mTextInBandVector.clear(); + mTextOutOfBandVector.clear(); + mLooper->stop(); +} + +status_t TimedTextDriver::setTimedTextTrackIndex_l(int32_t index) { + if (index >= + (int)(mTextInBandVector.size() + mTextOutOfBandVector.size())) { + return BAD_VALUE; + } + + sp<TimedTextSource> source; + if (index < mTextInBandVector.size()) { + source = mTextInBandVector.itemAt(index); + } else { + source = mTextOutOfBandVector.itemAt(index - mTextInBandVector.size()); + } + mPlayer->setDataSource(source); + return OK; +} + +status_t TimedTextDriver::start() { + Mutex::Autolock autoLock(mLock); + switch (mState) { + case UNINITIALIZED: + return INVALID_OPERATION; + case STOPPED: + mPlayer->start(); + break; + case PLAYING: + return OK; + case PAUSED: + mPlayer->resume(); + break; + default: + TRESPASS(); + } + mState = PLAYING; + return OK; +} + +status_t TimedTextDriver::stop() { + return pause(); +} + +// TODO: Test if pause() works properly. +// Scenario 1: start - pause - resume +// Scenario 2: start - seek +// Scenario 3: start - pause - seek - resume +status_t TimedTextDriver::pause() { + Mutex::Autolock autoLock(mLock); + switch (mState) { + case UNINITIALIZED: + return INVALID_OPERATION; + case STOPPED: + return OK; + case PLAYING: + mPlayer->pause(); + break; + case PAUSED: + return OK; + default: + TRESPASS(); + } + mState = PAUSED; + 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 ret = OK; + Mutex::Autolock autoLock(mLock); + switch (mState) { + case UNINITIALIZED: + ret = INVALID_OPERATION; + break; + case PAUSED: + ret = setTimedTextTrackIndex_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); + if (ret != OK) { + break; + } + mPlayer->start(); + break; + defaut: + TRESPASS(); + } + return ret; +} + +status_t TimedTextDriver::addInBandTextSource( + const sp<MediaSource>& mediaSource) { + sp<TimedTextSource> source = + TimedTextSource::CreateTimedTextSource(mediaSource); + if (source == NULL) { + return ERROR_UNSUPPORTED; + } + Mutex::Autolock autoLock(mLock); + mTextInBandVector.add(source); + if (mState == UNINITIALIZED) { + mState = STOPPED; + } + return OK; +} + +status_t TimedTextDriver::addOutOfBandTextSource( + const Parcel &request) { + // 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)) { + return ERROR_UNSUPPORTED; + } + sp<DataSource> dataSource = + DataSource::CreateFromURI(uri); + if (dataSource == NULL) { + return ERROR_UNSUPPORTED; + } + + sp<TimedTextSource> source; + if (uri.getPathExtension() == String8(".srt")) { + source = TimedTextSource::CreateTimedTextSource( + dataSource, TimedTextSource::OUT_OF_BAND_FILE_SRT); + } + + if (source == NULL) { + return ERROR_UNSUPPORTED; + } + + Mutex::Autolock autoLock(mLock); + + mTextOutOfBandVector.add(source); + if (mState == UNINITIALIZED) { + mState = STOPPED; + } + return OK; +} + +} // namespace android diff --git a/media/libstagefright/timedtext/TimedTextDriver.h b/media/libstagefright/timedtext/TimedTextDriver.h new file mode 100644 index 000000000000..efedb6e9028e --- /dev/null +++ b/media/libstagefright/timedtext/TimedTextDriver.h @@ -0,0 +1,81 @@ +/* + * 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. + */ + +#ifndef TIMED_TEXT_DRIVER_H_ +#define TIMED_TEXT_DRIVER_H_ + +#include <media/stagefright/foundation/ABase.h> // for DISALLOW_* macro +#include <utils/Errors.h> // for status_t +#include <utils/RefBase.h> +#include <utils/threads.h> + +namespace android { + +class ALooper; +class MediaPlayerBase; +class MediaSource; +class Parcel; +class TimedTextPlayer; +class TimedTextSource; + +class TimedTextDriver { +public: + TimedTextDriver(const wp<MediaPlayerBase> &listener); + + ~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 seekToAsync(int64_t timeUs); + + status_t addInBandTextSource(const sp<MediaSource>& source); + status_t addOutOfBandTextSource(const Parcel &request); + + status_t setTimedTextTrackIndex(int32_t index); + +private: + Mutex mLock; + + enum State { + UNINITIALIZED, + STOPPED, + PLAYING, + PAUSED, + }; + + sp<ALooper> mLooper; + sp<TimedTextPlayer> mPlayer; + wp<MediaPlayerBase> mListener; + + // Variables to be guarded by mLock. + State mState; + Vector<sp<TimedTextSource> > mTextInBandVector; + Vector<sp<TimedTextSource> > mTextOutOfBandVector; + // -- End of variables to be guarded by mLock + + status_t setTimedTextTrackIndex_l(int32_t index); + + DISALLOW_EVIL_CONSTRUCTORS(TimedTextDriver); +}; + +} // namespace android + +#endif // TIMED_TEXT_DRIVER_H_ diff --git a/media/libstagefright/timedtext/TimedTextInBandSource.cpp b/media/libstagefright/timedtext/TimedTextInBandSource.cpp new file mode 100644 index 000000000000..f2c4d54c4773 --- /dev/null +++ b/media/libstagefright/timedtext/TimedTextInBandSource.cpp @@ -0,0 +1,118 @@ + /* + * 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. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "TimedTextInBandSource" +#include <utils/Log.h> + +#include <binder/Parcel.h> +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/MediaDebug.h> // CHECK_XX macro +#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 "TimedTextInBandSource.h" +#include "TextDescriptions.h" + +namespace android { + +TimedTextInBandSource::TimedTextInBandSource(const sp<MediaSource>& mediaSource) + : mSource(mediaSource) { +} + +TimedTextInBandSource::~TimedTextInBandSource() { +} + +status_t TimedTextInBandSource::read( + int64_t *timeUs, Parcel *parcel, const MediaSource::ReadOptions *options) { + MediaBuffer *textBuffer = NULL; + status_t err = mSource->read(&textBuffer, options); + if (err != OK) { + return err; + } + CHECK(textBuffer != NULL); + textBuffer->meta_data()->findInt64(kKeyTime, timeUs); + // TODO: this is legacy code. when 'timeUs' can be <= 0? + if (*timeUs > 0) { + extractAndAppendLocalDescriptions(*timeUs, textBuffer, parcel); + } + textBuffer->release(); + return OK; +} + +// Each text sample consists of a string of text, optionally with sample +// modifier description. The modifier description could specify a new +// text style for the string of text. These descriptions are present only +// if they are needed. This method is used to extract the modifier +// description and append it at the end of the text. +status_t TimedTextInBandSource::extractAndAppendLocalDescriptions( + int64_t timeUs, const MediaBuffer *textBuffer, Parcel *parcel) { + const void *data; + size_t size = 0; + int32_t flag = TextDescriptions::LOCAL_DESCRIPTIONS; + + const char *mime; + CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime)); + + if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0) { + data = textBuffer->data(); + size = textBuffer->size(); + + if (size > 0) { + parcel->freeData(); + flag |= TextDescriptions::IN_BAND_TEXT_3GPP; + return TextDescriptions::getParcelOfDescriptions( + (const uint8_t *)data, size, flag, timeUs / 1000, parcel); + } + return OK; + } + return ERROR_UNSUPPORTED; +} + +// To extract and send the global text descriptions for all the text samples +// in the text track or text file. +// TODO: send error message to application via notifyListener()...? +status_t TimedTextInBandSource::extractGlobalDescriptions(Parcel *parcel) { + const void *data; + size_t size = 0; + int32_t flag = TextDescriptions::GLOBAL_DESCRIPTIONS; + + const char *mime; + CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime)); + + // support 3GPP only for now + if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0) { + uint32_t type; + // get the 'tx3g' box content. This box contains the text descriptions + // used to render the text track + if (!mSource->getFormat()->findData( + kKeyTextFormatData, &type, &data, &size)) { + return ERROR_MALFORMED; + } + + if (size > 0) { + flag |= TextDescriptions::IN_BAND_TEXT_3GPP; + return TextDescriptions::getParcelOfDescriptions( + (const uint8_t *)data, size, flag, 0, parcel); + } + return OK; + } + return ERROR_UNSUPPORTED; +} + +} // namespace android diff --git a/media/libstagefright/timedtext/TimedTextInBandSource.h b/media/libstagefright/timedtext/TimedTextInBandSource.h new file mode 100644 index 000000000000..26e5737f06ee --- /dev/null +++ b/media/libstagefright/timedtext/TimedTextInBandSource.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#ifndef TIMED_TEXT_IN_BAND_SOURCE_H_ +#define TIMED_TEXT_IN_BAND_SOURCE_H_ + +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaSource.h> + +#include "TimedTextSource.h" + +namespace android { + +class MediaBuffer; +class Parcel; + +class TimedTextInBandSource : public TimedTextSource { + public: + TimedTextInBandSource(const sp<MediaSource>& mediaSource); + virtual status_t start() { return mSource->start(); } + virtual status_t stop() { return mSource->stop(); } + virtual status_t read( + int64_t *timeUs, + Parcel *parcel, + const MediaSource::ReadOptions *options = NULL); + virtual status_t extractGlobalDescriptions(Parcel *parcel); + + protected: + virtual ~TimedTextInBandSource(); + + private: + sp<MediaSource> mSource; + + status_t extractAndAppendLocalDescriptions( + int64_t timeUs, const MediaBuffer *textBuffer, Parcel *parcel); + + DISALLOW_EVIL_CONSTRUCTORS(TimedTextInBandSource); +}; + +} // namespace android + +#endif // TIMED_TEXT_IN_BAND_SOURCE_H_ diff --git a/media/libstagefright/timedtext/TimedTextParser.h b/media/libstagefright/timedtext/TimedTextParser.h deleted file mode 100644 index 44774c296932..000000000000 --- a/media/libstagefright/timedtext/TimedTextParser.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -#ifndef TIMED_TEXT_PARSER_H_ - -#define TIMED_TEXT_PARSER_H_ - -#include <media/MediaPlayerInterface.h> -#include <media/stagefright/foundation/ABase.h> -#include <media/stagefright/foundation/AString.h> -#include <media/stagefright/MediaSource.h> - -namespace android { - -class DataSource; - -class TimedTextParser : public RefBase { -public: - TimedTextParser(); - virtual ~TimedTextParser(); - - enum FileType { - OUT_OF_BAND_FILE_SRT = 1, - }; - - status_t getText(AString *text, int64_t *startTimeUs, int64_t *endTimeUs, - const MediaSource::ReadOptions *options = NULL); - status_t init(const sp<DataSource> &dataSource, FileType fileType); - void reset(); - -private: - Mutex mLock; - - sp<DataSource> mDataSource; - off64_t mOffset; - - struct TextInfo { - int64_t endTimeUs; - // the offset of the text in the original file - off64_t offset; - int textLen; - }; - - int mIndex; - FileType mFileType; - - // the key indicated the start time of the text - KeyedVector<int64_t, TextInfo> mTextVector; - - status_t getNextInSrtFileFormat( - off64_t *offset, int64_t *startTimeUs, TextInfo *info); - status_t readNextLine(off64_t *offset, AString *data); - - status_t scanFile(); - - DISALLOW_EVIL_CONSTRUCTORS(TimedTextParser); -}; - -} // namespace android - -#endif // TIMED_TEXT_PARSER_H_ - diff --git a/media/libstagefright/timedtext/TimedTextPlayer.cpp b/media/libstagefright/timedtext/TimedTextPlayer.cpp index 3014b0bc0548..8c2df88f4253 100644 --- a/media/libstagefright/timedtext/TimedTextPlayer.cpp +++ b/media/libstagefright/timedtext/TimedTextPlayer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * 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. @@ -18,399 +18,164 @@ #define LOG_TAG "TimedTextPlayer" #include <utils/Log.h> -#include <binder/IPCThreadState.h> - +#include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/MediaDebug.h> -#include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> -#include <media/stagefright/MediaSource.h> -#include <media/stagefright/MetaData.h> -#include <media/stagefright/MediaBuffer.h> -#include <media/stagefright/FileSource.h> -#include <media/stagefright/Utils.h> +#include <media/MediaPlayerInterface.h> -#include "include/AwesomePlayer.h" #include "TimedTextPlayer.h" -#include "TimedTextParser.h" -#include "TextDescriptions.h" - -namespace android { -struct TimedTextEvent : public TimedEventQueue::Event { - TimedTextEvent( - TimedTextPlayer *player, - void (TimedTextPlayer::*method)()) - : mPlayer(player), - mMethod(method) { - } - -protected: - virtual ~TimedTextEvent() {} - - virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) { - (mPlayer->*mMethod)(); - } +#include "TimedTextDriver.h" +#include "TimedTextSource.h" -private: - TimedTextPlayer *mPlayer; - void (TimedTextPlayer::*mMethod)(); +namespace android { - TimedTextEvent(const TimedTextEvent &); - TimedTextEvent &operator=(const TimedTextEvent &); -}; +static const int64_t kAdjustmentProcessingTimeUs = 100000ll; -TimedTextPlayer::TimedTextPlayer( - AwesomePlayer *observer, - const wp<MediaPlayerBase> &listener, - TimedEventQueue *queue) - : mSource(NULL), - mOutOfBandSource(NULL), - mSeekTimeUs(0), - mStarted(false), - mTextEventPending(false), - mQueue(queue), - mListener(listener), - mObserver(observer), - mTextBuffer(NULL), - mTextParser(NULL), - mTextType(kNoText) { - mTextEvent = new TimedTextEvent(this, &TimedTextPlayer::onTextEvent); +TimedTextPlayer::TimedTextPlayer(const wp<MediaPlayerBase> &listener) + : mListener(listener), + mSource(NULL), + mSendSubtitleGeneration(0) { } TimedTextPlayer::~TimedTextPlayer() { - if (mStarted) { - reset(); + if (mSource != NULL) { + mSource->stop(); + mSource.clear(); + mSource = NULL; } - - mTextTrackVector.clear(); - mTextOutOfBandVector.clear(); } -status_t TimedTextPlayer::start(uint8_t index) { - CHECK(!mStarted); - - if (index >= - mTextTrackVector.size() + mTextOutOfBandVector.size()) { - ALOGE("Incorrect text track index: %d", index); - return BAD_VALUE; - } - - status_t err; - if (index < mTextTrackVector.size()) { // start an in-band text - mSource = mTextTrackVector.itemAt(index); - - err = mSource->start(); - - if (err != OK) { - return err; - } - mTextType = kInBandText; - } else { // start an out-of-band text - OutOfBandText text = - mTextOutOfBandVector.itemAt(index - mTextTrackVector.size()); - - mOutOfBandSource = text.source; - TimedTextParser::FileType fileType = text.type; - - if (mTextParser == NULL) { - mTextParser = new TimedTextParser(); - } - - if ((err = mTextParser->init(mOutOfBandSource, fileType)) != OK) { - return err; - } - mTextType = kOutOfBandText; - } - - // send sample description format - if ((err = extractAndSendGlobalDescriptions()) != OK) { - return err; - } - - int64_t positionUs; - mObserver->getPosition(&positionUs); - seekTo(positionUs); - - postTextEvent(); - - mStarted = true; - - return OK; +void TimedTextPlayer::start() { + sp<AMessage> msg = new AMessage(kWhatSeek, id()); + msg->setInt64("seekTimeUs", -1); + msg->post(); } void TimedTextPlayer::pause() { - CHECK(mStarted); - - cancelTextEvent(); + (new AMessage(kWhatPause, id()))->post(); } void TimedTextPlayer::resume() { - CHECK(mStarted); - - postTextEvent(); -} - -void TimedTextPlayer::reset() { - CHECK(mStarted); - - // send an empty text to clear the screen - notifyListener(MEDIA_TIMED_TEXT); - - cancelTextEvent(); - - mSeeking = false; - mStarted = false; - - if (mTextType == kInBandText) { - if (mTextBuffer != NULL) { - mTextBuffer->release(); - mTextBuffer = NULL; - } - - if (mSource != NULL) { - mSource->stop(); - mSource.clear(); - mSource = NULL; - } - } else { - if (mTextParser != NULL) { - mTextParser.clear(); - mTextParser = NULL; - } - if (mOutOfBandSource != NULL) { - mOutOfBandSource.clear(); - mOutOfBandSource = NULL; - } - } + start(); } -status_t TimedTextPlayer::seekTo(int64_t time_us) { - Mutex::Autolock autoLock(mLock); - - mSeeking = true; - mSeekTimeUs = time_us; - - postTextEvent(); - - return OK; +void TimedTextPlayer::seekToAsync(int64_t timeUs) { + sp<AMessage> msg = new AMessage(kWhatSeek, id()); + msg->setInt64("seekTimeUs", timeUs); + msg->post(); } -status_t TimedTextPlayer::setTimedTextTrackIndex(int32_t index) { - if (index >= - (int)(mTextTrackVector.size() + mTextOutOfBandVector.size())) { - return BAD_VALUE; - } - - if (mStarted) { - reset(); - } - - if (index >= 0) { - return start(index); - } - return OK; +void TimedTextPlayer::setDataSource(sp<TimedTextSource> source) { + sp<AMessage> msg = new AMessage(kWhatSetSource, id()); + msg->setObject("source", source); + msg->post(); } -void TimedTextPlayer::onTextEvent() { - Mutex::Autolock autoLock(mLock); - - if (!mTextEventPending) { - return; - } - mTextEventPending = false; - - if (mData.dataSize() > 0) { - notifyListener(MEDIA_TIMED_TEXT, &mData); - mData.freeData(); - } - - MediaSource::ReadOptions options; - if (mSeeking) { - options.setSeekTo(mSeekTimeUs, - MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); - mSeeking = false; - - notifyListener(MEDIA_TIMED_TEXT); //empty text to clear the screen - } - - int64_t positionUs, timeUs; - mObserver->getPosition(&positionUs); - - if (mTextType == kInBandText) { - if (mSource->read(&mTextBuffer, &options) != OK) { - return; +void TimedTextPlayer::onMessageReceived(const sp<AMessage> &msg) { + switch (msg->what()) { + case kWhatPause: { + mSendSubtitleGeneration++; + break; } - - mTextBuffer->meta_data()->findInt64(kKeyTime, &timeUs); - } else { - int64_t endTimeUs; - if (mTextParser->getText( - &mText, &timeUs, &endTimeUs, &options) != OK) { - return; - } - } - - if (timeUs > 0) { - extractAndAppendLocalDescriptions(timeUs); - } - - if (mTextType == kInBandText) { - if (mTextBuffer != NULL) { - mTextBuffer->release(); - mTextBuffer = NULL; + case kWhatSeek: { + int64_t seekTimeUs = 0; + msg->findInt64("seekTimeUs", &seekTimeUs); + if (seekTimeUs < 0) { + sp<MediaPlayerBase> listener = mListener.promote(); + if (listener != NULL) { + int32_t positionMs = 0; + listener->getCurrentPosition(&positionMs); + seekTimeUs = positionMs * 1000ll; + } + } + doSeekAndRead(seekTimeUs); + break; + } + case kWhatSendSubtitle: { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + if (generation != mSendSubtitleGeneration) { + // Drop obsolete msg. + break; + } + sp<RefBase> obj; + msg->findObject("subtitle", &obj); + if (obj != NULL) { + sp<ParcelEvent> parcelEvent; + parcelEvent = static_cast<ParcelEvent*>(obj.get()); + notifyListener(MEDIA_TIMED_TEXT, &(parcelEvent->parcel)); + } else { + notifyListener(MEDIA_TIMED_TEXT); + } + doRead(); + break; + } + case kWhatSetSource: { + sp<RefBase> obj; + msg->findObject("source", &obj); + if (obj == NULL) break; + if (mSource != NULL) { + 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); + } + break; } - } else { - mText.clear(); - } - - //send the text now - if (timeUs <= positionUs + 100000ll) { - postTextEvent(); - } else { - postTextEvent(timeUs - positionUs - 100000ll); } } -void TimedTextPlayer::postTextEvent(int64_t delayUs) { - if (mTextEventPending) { - return; - } - - mTextEventPending = true; - mQueue->postEventWithDelay(mTextEvent, delayUs < 0 ? 10000 : delayUs); -} - -void TimedTextPlayer::cancelTextEvent() { - mQueue->cancelEvent(mTextEvent->eventID()); - mTextEventPending = false; +void TimedTextPlayer::doSeekAndRead(int64_t seekTimeUs) { + MediaSource::ReadOptions options; + options.setSeekTo(seekTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); + doRead(&options); } -void TimedTextPlayer::addTextSource(sp<MediaSource> source) { - Mutex::Autolock autoLock(mLock); - mTextTrackVector.add(source); +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 TimedTextPlayer::setParameter(int key, const Parcel &request) { - Mutex::Autolock autoLock(mLock); - - if (key == KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE) { - const String16 uri16 = request.readString16(); - String8 uri = String8(uri16); - KeyedVector<String8, String8> headers; - - // To support local subtitle file only for now - if (strncasecmp("file://", uri.string(), 7)) { - return INVALID_OPERATION; - } - sp<DataSource> dataSource = - DataSource::CreateFromURI(uri, &headers); - status_t err = dataSource->initCheck(); +void TimedTextPlayer::postTextEvent(const sp<ParcelEvent>& parcel, int64_t timeUs) { + sp<MediaPlayerBase> listener = mListener.promote(); + if (listener != NULL) { + int64_t positionUs, delayUs; + int32_t positionMs = 0; + listener->getCurrentPosition(&positionMs); + positionUs = positionMs * 1000; - if (err != OK) { - return err; - } - - OutOfBandText text; - text.source = dataSource; - if (uri.getPathExtension() == String8(".srt")) { - text.type = TimedTextParser::OUT_OF_BAND_FILE_SRT; + if (timeUs <= positionUs + kAdjustmentProcessingTimeUs) { + delayUs = 0; } else { - return ERROR_UNSUPPORTED; + delayUs = timeUs - positionUs - kAdjustmentProcessingTimeUs; } - - mTextOutOfBandVector.add(text); - - return OK; - } - return INVALID_OPERATION; -} - -void TimedTextPlayer::notifyListener(int msg, const Parcel *parcel) { - if (mListener != NULL) { - sp<MediaPlayerBase> listener = mListener.promote(); - - if (listener != NULL) { - if (parcel && (parcel->dataSize() > 0)) { - listener->sendEvent(msg, 0, 0, parcel); - } else { // send an empty timed text to clear the screen - listener->sendEvent(msg); - } + sp<AMessage> msg = new AMessage(kWhatSendSubtitle, id()); + msg->setInt32("generation", mSendSubtitleGeneration); + if (parcel != NULL) { + msg->setObject("subtitle", parcel); } + msg->post(delayUs); } } -// Each text sample consists of a string of text, optionally with sample -// modifier description. The modifier description could specify a new -// text style for the string of text. These descriptions are present only -// if they are needed. This method is used to extract the modifier -// description and append it at the end of the text. -status_t TimedTextPlayer::extractAndAppendLocalDescriptions(int64_t timeUs) { - const void *data; - size_t size = 0; - int32_t flag = TextDescriptions::LOCAL_DESCRIPTIONS; - - if (mTextType == kInBandText) { - const char *mime; - CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime)); - - if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) { - flag |= TextDescriptions::IN_BAND_TEXT_3GPP; - data = mTextBuffer->data(); - size = mTextBuffer->size(); - } else { - // support 3GPP only for now - return ERROR_UNSUPPORTED; +void TimedTextPlayer::notifyListener(int msg, const Parcel *parcel) { + sp<MediaPlayerBase> listener = mListener.promote(); + if (listener != NULL) { + if (parcel != NULL && (parcel->dataSize() > 0)) { + listener->sendEvent(msg, 0, 0, parcel); + } else { // send an empty timed text to clear the screen + listener->sendEvent(msg); } - } else { - data = mText.c_str(); - size = mText.size(); - flag |= TextDescriptions::OUT_OF_BAND_TEXT_SRT; } - - if ((size > 0) && (flag != TextDescriptions::LOCAL_DESCRIPTIONS)) { - mData.freeData(); - return TextDescriptions::getParcelOfDescriptions( - (const uint8_t *)data, size, flag, timeUs / 1000, &mData); - } - - return OK; } -// To extract and send the global text descriptions for all the text samples -// in the text track or text file. -status_t TimedTextPlayer::extractAndSendGlobalDescriptions() { - const void *data; - size_t size = 0; - int32_t flag = TextDescriptions::GLOBAL_DESCRIPTIONS; - - if (mTextType == kInBandText) { - const char *mime; - CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime)); - - // support 3GPP only for now - if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) { - uint32_t type; - // get the 'tx3g' box content. This box contains the text descriptions - // used to render the text track - if (!mSource->getFormat()->findData( - kKeyTextFormatData, &type, &data, &size)) { - return ERROR_MALFORMED; - } - - flag |= TextDescriptions::IN_BAND_TEXT_3GPP; - } - } - - if ((size > 0) && (flag != TextDescriptions::GLOBAL_DESCRIPTIONS)) { - Parcel parcel; - if (TextDescriptions::getParcelOfDescriptions( - (const uint8_t *)data, size, flag, 0, &parcel) == OK) { - if (parcel.dataSize() > 0) { - notifyListener(MEDIA_TIMED_TEXT, &parcel); - } - } - } - - return OK; -} -} +} // namespace android diff --git a/media/libstagefright/timedtext/TimedTextPlayer.h b/media/libstagefright/timedtext/TimedTextPlayer.h index a744db59417b..837beeb0ee3e 100644 --- a/media/libstagefright/timedtext/TimedTextPlayer.h +++ b/media/libstagefright/timedtext/TimedTextPlayer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * 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. @@ -15,99 +15,61 @@ */ #ifndef TIMEDTEXT_PLAYER_H_ - #define TIMEDTEXT_PLAYER_H_ -#include <media/MediaPlayerInterface.h> +#include <binder/Parcel.h> #include <media/stagefright/foundation/ABase.h> -#include <media/stagefright/foundation/AString.h> +#include <media/stagefright/foundation/AHandler.h> +#include <media/stagefright/MediaSource.h> +#include <utils/RefBase.h> -#include "include/TimedEventQueue.h" -#include "TimedTextParser.h" +#include "TimedTextSource.h" namespace android { -class MediaSource; -class AwesomePlayer; -class MediaBuffer; +class AMessage; +class MediaPlayerBase; +class TimedTextDriver; +class TimedTextSource; -class TimedTextPlayer { +class TimedTextPlayer : public AHandler { public: - TimedTextPlayer(AwesomePlayer *observer, - const wp<MediaPlayerBase> &listener, - TimedEventQueue *queue); + TimedTextPlayer(const wp<MediaPlayerBase> &listener); virtual ~TimedTextPlayer(); - // index: the index of the text track which will - // be turned on - status_t start(uint8_t index); - + void start(); void pause(); - void resume(); + void seekToAsync(int64_t timeUs); + void setDataSource(sp<TimedTextSource> source); - status_t seekTo(int64_t time_us); - - void addTextSource(sp<MediaSource> source); - - status_t setTimedTextTrackIndex(int32_t index); - status_t setParameter(int key, const Parcel &request); +protected: + virtual void onMessageReceived(const sp<AMessage> &msg); private: - enum TextType { - kNoText = 0, - kInBandText = 1, - kOutOfBandText = 2, + enum { + kWhatPause = 'paus', + kWhatSeek = 'seek', + kWhatSendSubtitle = 'send', + kWhatSetSource = 'ssrc', }; - Mutex mLock; - - sp<MediaSource> mSource; - sp<DataSource> mOutOfBandSource; - - bool mSeeking; - int64_t mSeekTimeUs; - - bool mStarted; - - sp<TimedEventQueue::Event> mTextEvent; - bool mTextEventPending; - - TimedEventQueue *mQueue; - - wp<MediaPlayerBase> mListener; - AwesomePlayer *mObserver; - - MediaBuffer *mTextBuffer; - Parcel mData; - - // for in-band timed text - Vector<sp<MediaSource> > mTextTrackVector; - - // for out-of-band timed text - struct OutOfBandText { - TimedTextParser::FileType type; - sp<DataSource> source; + // To add Parcel into an AMessage as an object, it should be 'RefBase'. + struct ParcelEvent : public RefBase { + Parcel parcel; }; - Vector<OutOfBandText > mTextOutOfBandVector; - sp<TimedTextParser> mTextParser; - AString mText; - - TextType mTextType; - - void reset(); + wp<MediaPlayerBase> mListener; + sp<TimedTextSource> mSource; + int32_t mSendSubtitleGeneration; + void doSeekAndRead(int64_t seekTimeUs); + void doRead(MediaSource::ReadOptions* options = NULL); void onTextEvent(); - void postTextEvent(int64_t delayUs = -1); - void cancelTextEvent(); - + void postTextEvent(const sp<ParcelEvent>& parcel = NULL, int64_t timeUs = -1); void notifyListener(int msg, const Parcel *parcel = NULL); - status_t extractAndAppendLocalDescriptions(int64_t timeUs); - status_t extractAndSendGlobalDescriptions(); - DISALLOW_EVIL_CONSTRUCTORS(TimedTextPlayer); }; diff --git a/media/libstagefright/timedtext/TimedTextParser.cpp b/media/libstagefright/timedtext/TimedTextSRTSource.cpp index caea0a4cc6ed..3752d34469a5 100644 --- a/media/libstagefright/timedtext/TimedTextParser.cpp +++ b/media/libstagefright/timedtext/TimedTextSRTSource.cpp @@ -1,5 +1,5 @@ -/* - * Copyright (C) 2011 The Android Open Source Project + /* + * 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. @@ -14,133 +14,106 @@ * limitations under the License. */ -#include "TimedTextParser.h" +//#define LOG_NDEBUG 0 +#define LOG_TAG "TimedTextSRTSource" +#include <utils/Log.h> + +#include <binder/Parcel.h> +#include <media/stagefright/foundation/AString.h> #include <media/stagefright/DataSource.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaSource.h> + +#include "TimedTextSRTSource.h" +#include "TextDescriptions.h" namespace android { -TimedTextParser::TimedTextParser() - : mDataSource(NULL), - mOffset(0), - mIndex(0) { +TimedTextSRTSource::TimedTextSRTSource(const sp<DataSource>& dataSource) + : mSource(dataSource), + mIndex(0) { } -TimedTextParser::~TimedTextParser() { - reset(); +TimedTextSRTSource::~TimedTextSRTSource() { } -status_t TimedTextParser::init( - const sp<DataSource> &dataSource, FileType fileType) { - mDataSource = dataSource; - mFileType = fileType; - - status_t err; - if ((err = scanFile()) != OK) { +status_t TimedTextSRTSource::start() { + status_t err = scanFile(); + if (err != OK) { reset(); - return err; } - - return OK; + return err; } -void TimedTextParser::reset() { - mDataSource.clear(); +void TimedTextSRTSource::reset() { mTextVector.clear(); - mOffset = 0; mIndex = 0; } -// scan the text file to get start/stop time and the -// offset of each piece of text content -status_t TimedTextParser::scanFile() { - if (mFileType != OUT_OF_BAND_FILE_SRT) { - return ERROR_UNSUPPORTED; +status_t TimedTextSRTSource::stop() { + reset(); + return OK; +} + +status_t TimedTextSRTSource::read( + int64_t *timeUs, + Parcel *parcel, + const MediaSource::ReadOptions *options) { + int64_t endTimeUs; + AString text; + status_t err = getText(options, &text, timeUs, &endTimeUs); + if (err != OK) { + return err; + } + + if (*timeUs > 0) { + extractAndAppendLocalDescriptions(*timeUs, text, parcel); } + return OK; +} +status_t TimedTextSRTSource::scanFile() { off64_t offset = 0; int64_t startTimeUs; bool endOfFile = false; while (!endOfFile) { TextInfo info; - status_t err = getNextInSrtFileFormat(&offset, &startTimeUs, &info); - - if (err != OK) { - if (err == ERROR_END_OF_STREAM) { + status_t err = getNextSubtitleInfo(&offset, &startTimeUs, &info); + switch (err) { + case OK: + mTextVector.add(startTimeUs, info); + break; + case ERROR_END_OF_STREAM: endOfFile = true; - } else { + break; + default: return err; - } - } else { - mTextVector.add(startTimeUs, info); } } - if (mTextVector.isEmpty()) { return ERROR_MALFORMED; } return OK; } -// read one line started from *offset and store it into data. -status_t TimedTextParser::readNextLine(off64_t *offset, AString *data) { - char character; - - data->clear(); - - while (true) { - ssize_t err; - if ((err = mDataSource->readAt(*offset, &character, 1)) < 1) { - if (err == 0) { - return ERROR_END_OF_STREAM; - } - return ERROR_IO; - } - - (*offset) ++; - - // a line could end with CR, LF or CR + LF - if (character == 10) { - break; - } else if (character == 13) { - if ((err = mDataSource->readAt(*offset, &character, 1)) < 1) { - if (err == 0) { // end of the stream - return OK; - } - return ERROR_IO; - } - - (*offset) ++; - - if (character != 10) { - (*offset) --; - } - break; - } - - data->append(character); - } - - return OK; -} - /* SRT format: - * Subtitle number - * Start time --> End time - * Text of subtitle (one or more lines) - * Blank lines + * Subtitle number + * Start time --> End time + * Text of subtitle (one or more lines) + * Blank lines * * .srt file example: - * 1 - * 00:00:20,000 --> 00:00:24,400 - * Altocumulus clouds occur between six thousand + * 1 + * 00:00:20,000 --> 00:00:24,400 + * Altocumulus clouds occr between six thousand * - * 2 - * 00:00:24,600 --> 00:00:27,800 - * and twenty thousand feet above ground level. + * 2 + * 00:00:24,600 --> 00:00:27,800 + * and twenty thousand feet above ground level. */ -status_t TimedTextParser::getNextInSrtFileFormat( - off64_t *offset, int64_t *startTimeUs, TextInfo *info) { +status_t TimedTextSRTSource::getNextSubtitleInfo( + off64_t *offset, int64_t *startTimeUs, TextInfo *info) { AString data; status_t err; @@ -150,10 +123,9 @@ status_t TimedTextParser::getNextInSrtFileFormat( return err; } data.trim(); - } while(data.empty()); + } while (data.empty()); // Just ignore the first non-blank line which is subtitle sequence number. - if ((err = readNextLine(offset, &data)) != OK) { return err; } @@ -161,7 +133,7 @@ status_t TimedTextParser::getNextInSrtFileFormat( // the start time format is: hours:minutes:seconds,milliseconds // 00:00:24,600 --> 00:00:27,800 if (sscanf(data.c_str(), "%02d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d", - &hour1, &min1, &sec1, &msec1, &hour2, &min2, &sec2, &msec2) != 8) { + &hour1, &min1, &sec1, &msec1, &hour2, &min2, &sec2, &msec2) != 8) { return ERROR_MALFORMED; } @@ -172,7 +144,6 @@ status_t TimedTextParser::getNextInSrtFileFormat( } info->offset = *offset; - bool needMoreData = true; while (needMoreData) { if ((err = readNextLine(offset, &data)) != OK) { @@ -191,25 +162,56 @@ status_t TimedTextParser::getNextInSrtFileFormat( } } } - info->textLen = *offset - info->offset; - return OK; } -status_t TimedTextParser::getText( - AString *text, int64_t *startTimeUs, int64_t *endTimeUs, - const MediaSource::ReadOptions *options) { - Mutex::Autolock autoLock(mLock); +status_t TimedTextSRTSource::readNextLine(off64_t *offset, AString *data) { + data->clear(); + while (true) { + ssize_t readSize; + char character; + if ((readSize = mSource->readAt(*offset, &character, 1)) < 1) { + if (readSize == 0) { + return ERROR_END_OF_STREAM; + } + return ERROR_IO; + } - text->clear(); + (*offset)++; + // a line could end with CR, LF or CR + LF + if (character == 10) { + break; + } else if (character == 13) { + if ((readSize = mSource->readAt(*offset, &character, 1)) < 1) { + if (readSize == 0) { // end of the stream + return OK; + } + return ERROR_IO; + } + + (*offset)++; + if (character != 10) { + (*offset)--; + } + break; + } + data->append(character); + } + return OK; +} + +status_t TimedTextSRTSource::getText( + const MediaSource::ReadOptions *options, + AString *text, int64_t *startTimeUs, int64_t *endTimeUs) { + text->clear(); int64_t seekTimeUs; MediaSource::ReadOptions::SeekMode mode; - if (options && options->getSeekTo(&seekTimeUs, &mode)) { - int64_t lastEndTimeUs = mTextVector.valueAt(mTextVector.size() - 1).endTimeUs; + if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) { + int64_t lastEndTimeUs = + mTextVector.valueAt(mTextVector.size() - 1).endTimeUs; int64_t firstStartTimeUs = mTextVector.keyAt(0); - if (seekTimeUs < 0 || seekTimeUs > lastEndTimeUs) { return ERROR_OUT_OF_RANGE; } else if (seekTimeUs < firstStartTimeUs) { @@ -232,31 +234,42 @@ status_t TimedTextParser::getText( low = mid + 1; } else { if ((high == mid + 1) - && (seekTimeUs < mTextVector.keyAt(high))) { + && (seekTimeUs < mTextVector.keyAt(high))) { break; } high = mid - 1; } } - mIndex = mid; } } - - TextInfo info = mTextVector.valueAt(mIndex); + const TextInfo &info = mTextVector.valueAt(mIndex); *startTimeUs = mTextVector.keyAt(mIndex); *endTimeUs = info.endTimeUs; - mIndex ++; + mIndex++; char *str = new char[info.textLen]; - if (mDataSource->readAt(info.offset, str, info.textLen) < info.textLen) { + if (mSource->readAt(info.offset, str, info.textLen) < info.textLen) { delete[] str; return ERROR_IO; } - text->append(str, info.textLen); delete[] str; return OK; } +status_t TimedTextSRTSource::extractAndAppendLocalDescriptions( + int64_t timeUs, const AString &text, Parcel *parcel) { + const void *data = text.c_str(); + size_t size = text.size(); + int32_t flag = TextDescriptions::LOCAL_DESCRIPTIONS | + TextDescriptions::OUT_OF_BAND_TEXT_SRT; + + if (size > 0) { + return TextDescriptions::getParcelOfDescriptions( + (const uint8_t *)data, size, flag, timeUs / 1000, parcel); + } + return OK; +} + } // namespace android diff --git a/media/libstagefright/timedtext/TimedTextSRTSource.h b/media/libstagefright/timedtext/TimedTextSRTSource.h new file mode 100644 index 000000000000..a0734d9b21b7 --- /dev/null +++ b/media/libstagefright/timedtext/TimedTextSRTSource.h @@ -0,0 +1,75 @@ +/* + * 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. + */ + +#ifndef TIMED_TEXT_SRT_SOURCE_H_ +#define TIMED_TEXT_SRT_SOURCE_H_ + +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaSource.h> +#include <utils/Compat.h> // off64_t + +#include "TimedTextSource.h" + +namespace android { + +class AString; +class DataSource; +class MediaBuffer; +class Parcel; + +class TimedTextSRTSource : public TimedTextSource { + public: + TimedTextSRTSource(const sp<DataSource>& dataSource); + virtual status_t start(); + virtual status_t stop(); + virtual status_t read( + int64_t *timeUs, + Parcel *parcel, + const MediaSource::ReadOptions *options = NULL); + + protected: + virtual ~TimedTextSRTSource(); + + private: + sp<DataSource> mSource; + + struct TextInfo { + int64_t endTimeUs; + // The offset of the text in the original file. + off64_t offset; + int textLen; + }; + + int mIndex; + KeyedVector<int64_t, TextInfo> mTextVector; + + void reset(); + status_t scanFile(); + status_t getNextSubtitleInfo( + off64_t *offset, int64_t *startTimeUs, TextInfo *info); + status_t readNextLine(off64_t *offset, AString *data); + status_t getText( + const MediaSource::ReadOptions *options, + AString *text, int64_t *startTimeUs, int64_t *endTimeUs); + status_t extractAndAppendLocalDescriptions( + int64_t timeUs, const AString &text, Parcel *parcel); + + DISALLOW_EVIL_CONSTRUCTORS(TimedTextSRTSource); +}; + +} // namespace android + +#endif // TIMED_TEXT_SRT_SOURCE_H_ diff --git a/media/libstagefright/timedtext/TimedTextSource.cpp b/media/libstagefright/timedtext/TimedTextSource.cpp new file mode 100644 index 000000000000..9efe67c45af0 --- /dev/null +++ b/media/libstagefright/timedtext/TimedTextSource.cpp @@ -0,0 +1,53 @@ + /* + * 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. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "TimedTextSource" +#include <utils/Log.h> + +#include <media/stagefright/DataSource.h> +#include <media/stagefright/MediaSource.h> + +#include "TimedTextSource.h" + +#include "TimedTextInBandSource.h" +#include "TimedTextSRTSource.h" + +namespace android { + +// static +sp<TimedTextSource> TimedTextSource::CreateTimedTextSource( + const sp<MediaSource>& mediaSource) { + return new TimedTextInBandSource(mediaSource); +} + +// static +sp<TimedTextSource> TimedTextSource::CreateTimedTextSource( + const sp<DataSource>& dataSource, FileType filetype) { + switch(filetype) { + case OUT_OF_BAND_FILE_SRT: + return new TimedTextSRTSource(dataSource); + case OUT_OF_BAND_FILE_SMI: + // TODO: Implement for SMI. + ALOGE("Supporting SMI is not implemented yet"); + break; + default: + ALOGE("Undefined subtitle format. : %d", filetype); + } + return NULL; +} + +} // namespace android diff --git a/media/libstagefright/timedtext/TimedTextSource.h b/media/libstagefright/timedtext/TimedTextSource.h new file mode 100644 index 000000000000..06bae7150db4 --- /dev/null +++ b/media/libstagefright/timedtext/TimedTextSource.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#ifndef TIMED_TEXT_SOURCE_H_ +#define TIMED_TEXT_SOURCE_H_ + +#include <media/stagefright/foundation/ABase.h> // for DISALLOW_XXX macro. +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaSource.h> // for MediaSource::ReadOptions +#include <utils/RefBase.h> + +namespace android { + +class DataSource; +class Parcel; + +class TimedTextSource : public RefBase { + public: + enum FileType { + OUT_OF_BAND_FILE_SRT = 1, + OUT_OF_BAND_FILE_SMI = 2, + }; + static sp<TimedTextSource> CreateTimedTextSource( + const sp<MediaSource>& source); + static sp<TimedTextSource> CreateTimedTextSource( + const sp<DataSource>& source, FileType filetype); + TimedTextSource() {} + virtual status_t start() = 0; + virtual status_t stop() = 0; + // Returns subtitle parcel and its start time. + virtual status_t read( + int64_t *timeUs, + Parcel *parcel, + const MediaSource::ReadOptions *options = NULL) = 0; + virtual status_t extractGlobalDescriptions(Parcel *parcel) { + return INVALID_OPERATION; + } + + protected: + virtual ~TimedTextSource() { } + + private: + DISALLOW_EVIL_CONSTRUCTORS(TimedTextSource); +}; + +} // namespace android + +#endif // TIMED_TEXT_SOURCE_H_ diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp index 2fc612522f7a..06be2ef1147f 100644 --- a/opengl/libs/EGL/Loader.cpp +++ b/opengl/libs/EGL/Loader.cpp @@ -253,6 +253,19 @@ void Loader::init_api(void* dso, if (f == NULL) { //ALOGD("%s", name); f = (__eglMustCastToProperFunctionPointerType)gl_unimplemented; + + /* + * GL_EXT_debug_label is special, we always report it as + * supported, it's handled by GLES_trace. If GLES_trace is not + * enabled, then these are no-ops. + */ + if (!strcmp(name, "glInsertEventMarkerEXT")) { + f = (__eglMustCastToProperFunctionPointerType)gl_noop; + } else if (!strcmp(name, "glPushGroupMarkerEXT")) { + f = (__eglMustCastToProperFunctionPointerType)gl_noop; + } else if (!strcmp(name, "glPopGroupMarkerEXT")) { + f = (__eglMustCastToProperFunctionPointerType)gl_noop; + } } *curr++ = f; api++; diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp index e053589a878b..83933e525211 100644 --- a/opengl/libs/EGL/egl.cpp +++ b/opengl/libs/EGL/egl.cpp @@ -233,6 +233,26 @@ EGLImageKHR egl_get_image_for_current_context(EGLImageKHR image) // ---------------------------------------------------------------------------- +const GLubyte * egl_get_string_for_current_context(GLenum name) { + // NOTE: returning NULL here will fall-back to the default + // implementation. + + EGLContext context = egl_tls_t::getContext(); + if (context == EGL_NO_CONTEXT) + return NULL; + + egl_context_t const * const c = get_context(context); + if (c == NULL) // this should never happen, by construction + return NULL; + + if (name != GL_EXTENSIONS) + return NULL; + + return (const GLubyte *)c->gl_extensions.string(); +} + +// ---------------------------------------------------------------------------- + // this mutex protects: // d->disp[] // egl_init_drivers_locked() @@ -290,6 +310,9 @@ void gl_unimplemented() { ALOGE("called unimplemented OpenGL ES API"); } +void gl_noop() { +} + // ---------------------------------------------------------------------------- #if USE_FAST_TLS_KEY diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp index 8b37da56c9e7..73aab26cc76c 100644 --- a/opengl/libs/EGL/eglApi.cpp +++ b/opengl/libs/EGL/eglApi.cpp @@ -566,27 +566,6 @@ EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) return result; } -static void loseCurrent(egl_context_t * cur_c) -{ - if (cur_c) { - egl_surface_t * cur_r = get_surface(cur_c->read); - egl_surface_t * cur_d = get_surface(cur_c->draw); - - // by construction, these are either 0 or valid (possibly terminated) - // it should be impossible for these to be invalid - ContextRef _cur_c(cur_c); - SurfaceRef _cur_r(cur_r); - SurfaceRef _cur_d(cur_d); - - cur_c->read = NULL; - cur_c->draw = NULL; - - _cur_c.release(); - _cur_r.release(); - _cur_d.release(); - } -} - EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) { @@ -663,21 +642,13 @@ EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw, impl_read = r->surface; } - EGLBoolean result; - if (c) { - result = c->cnx->egl.eglMakeCurrent( - dp->disp[c->impl].dpy, impl_draw, impl_read, impl_ctx); - } else { - result = cur_c->cnx->egl.eglMakeCurrent( - dp->disp[cur_c->impl].dpy, impl_draw, impl_read, impl_ctx); - } + EGLBoolean result = const_cast<egl_display_t*>(dp)->makeCurrent(c, cur_c, + draw, read, ctx, + impl_draw, impl_read, impl_ctx); if (result == EGL_TRUE) { - - loseCurrent(cur_c); - - if (ctx != EGL_NO_CONTEXT) { + if (c) { setGLHooksThreadSpecific(c->cnx->hooks[c->version]); egl_tls_t::setContext(ctx); #if EGL_TRACE @@ -687,8 +658,6 @@ EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw, _c.acquire(); _r.acquire(); _d.acquire(); - c->read = read; - c->draw = draw; } else { setGLHooksThreadSpecific(&gHooksNoContext); egl_tls_t::setContext(EGL_NO_CONTEXT); @@ -924,7 +893,8 @@ __eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname) cnx->hooks[GLESv1_INDEX]->ext.extensions[slot] = cnx->hooks[GLESv2_INDEX]->ext.extensions[slot] = #if EGL_TRACE - debugHooks->ext.extensions[slot] = gHooksTrace.ext.extensions[slot] = + debugHooks->ext.extensions[slot] = + gHooksTrace.ext.extensions[slot] = #endif cnx->egl.eglGetProcAddress(procname); } @@ -1180,7 +1150,7 @@ EGLBoolean eglReleaseThread(void) clearError(); // If there is context bound to the thread, release it - loseCurrent(get_context(getContext())); + egl_display_t::loseCurrent(get_context(getContext())); for (int i=0 ; i<IMPL_NUM_IMPLEMENTATIONS ; i++) { egl_connection_t* const cnx = &gEGLImpl[i]; diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp index 53eaf9a1366a..5cf5236b9009 100644 --- a/opengl/libs/EGL/egl_display.cpp +++ b/opengl/libs/EGL/egl_display.cpp @@ -342,6 +342,47 @@ EGLBoolean egl_display_t::terminate() { return res; } +void egl_display_t::loseCurrent(egl_context_t * cur_c) +{ + if (cur_c) { + egl_surface_t * cur_r = get_surface(cur_c->read); + egl_surface_t * cur_d = get_surface(cur_c->draw); + + // by construction, these are either 0 or valid (possibly terminated) + // it should be impossible for these to be invalid + ContextRef _cur_c(cur_c); + SurfaceRef _cur_r(cur_r); + SurfaceRef _cur_d(cur_d); + + cur_c->onLooseCurrent(); + + _cur_c.release(); + _cur_r.release(); + _cur_d.release(); + } +} + +EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c, + EGLSurface draw, EGLSurface read, EGLContext ctx, + EGLSurface impl_draw, EGLSurface impl_read, EGLContext impl_ctx) +{ + Mutex::Autolock _l(lock); + EGLBoolean result; + if (c) { + result = c->cnx->egl.eglMakeCurrent( + disp[c->impl].dpy, impl_draw, impl_read, impl_ctx); + } else { + result = cur_c->cnx->egl.eglMakeCurrent( + disp[cur_c->impl].dpy, impl_draw, impl_read, impl_ctx); + } + if (result == EGL_TRUE) { + loseCurrent(cur_c); + if (c) { + c->onMakeCurrent(draw, read); + } + } + return result; +} // ---------------------------------------------------------------------------- }; // namespace android diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h index 042ae07a91f6..4479e00ede26 100644 --- a/opengl/libs/EGL/egl_display.h +++ b/opengl/libs/EGL/egl_display.h @@ -39,6 +39,7 @@ namespace android { // ---------------------------------------------------------------------------- class egl_object_t; +class egl_context_t; class egl_connection_t; // ---------------------------------------------------------------------------- @@ -84,10 +85,14 @@ public: // add reference to this object. returns true if this is a valid object. bool getObject(egl_object_t* object) const; - static egl_display_t* get(EGLDisplay dpy); static EGLDisplay getFromNativeDisplay(EGLNativeDisplayType disp); + EGLBoolean makeCurrent(egl_context_t* c, egl_context_t* cur_c, + EGLSurface draw, EGLSurface read, EGLContext ctx, + EGLSurface impl_draw, EGLSurface impl_read, EGLContext impl_ctx); + static void loseCurrent(egl_context_t * cur_c); + inline bool isReady() const { return (refs > 0); } inline bool isValid() const { return magic == '_dpy'; } inline bool isAlive() const { return isValid(); } diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp index 26e8c3eb10c4..b660c53142ac 100644 --- a/opengl/libs/EGL/egl_object.cpp +++ b/opengl/libs/EGL/egl_object.cpp @@ -62,5 +62,41 @@ bool egl_object_t::get(egl_display_t const* display, egl_object_t* object) { } // ---------------------------------------------------------------------------- + +egl_context_t::egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config, + int impl, egl_connection_t const* cnx, int version) : + egl_object_t(get_display(dpy)), dpy(dpy), context(context), + config(config), read(0), draw(0), impl(impl), cnx(cnx), + version(version) +{ +} + +void egl_context_t::onLooseCurrent() { + read = NULL; + draw = NULL; +} + +void egl_context_t::onMakeCurrent(EGLSurface draw, EGLSurface read) { + this->read = read; + this->draw = draw; + + /* + * Here we cache the GL_EXTENSIONS string for this context and we + * add the extensions always handled by the wrapper + */ + + if (gl_extensions.isEmpty()) { + // call the implementation's glGetString(GL_EXTENSIONS) + const char* exts = (const char *)gEGLImpl[impl].hooks[version]->gl.glGetString(GL_EXTENSIONS); + gl_extensions.setTo(exts); + if (gl_extensions.find("GL_EXT_debug_marker") < 0) { + String8 temp("GL_EXT_debug_marker "); + temp.append(gl_extensions); + gl_extensions.setTo(temp); + } + } +} + +// ---------------------------------------------------------------------------- }; // namespace android // ---------------------------------------------------------------------------- diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h index 7106fa5ebf56..abd4cbb962a6 100644 --- a/opengl/libs/EGL/egl_object.h +++ b/opengl/libs/EGL/egl_object.h @@ -28,6 +28,7 @@ #include <GLES/glext.h> #include <utils/threads.h> +#include <utils/String8.h> #include <system/window.h> @@ -158,11 +159,11 @@ public: typedef egl_object_t::LocalRef<egl_context_t, EGLContext> Ref; egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config, - int impl, egl_connection_t const* cnx, int version) : - egl_object_t(get_display(dpy)), dpy(dpy), context(context), - config(config), read(0), draw(0), impl(impl), cnx(cnx), - version(version) { - } + int impl, egl_connection_t const* cnx, int version); + + void onLooseCurrent(); + void onMakeCurrent(EGLSurface draw, EGLSurface read); + EGLDisplay dpy; EGLContext context; EGLConfig config; @@ -171,6 +172,7 @@ public: int impl; egl_connection_t const* cnx; int version; + String8 gl_extensions; }; class egl_image_t: public egl_object_t { diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h index 107acd9b12b1..ff20957f3e41 100644 --- a/opengl/libs/EGL/egldefs.h +++ b/opengl/libs/EGL/egldefs.h @@ -58,6 +58,7 @@ extern gl_hooks_t gHooks[2][IMPL_NUM_IMPLEMENTATIONS]; extern gl_hooks_t gHooksNoContext; extern pthread_key_t gGLWrapperKey; extern "C" void gl_unimplemented(); +extern "C" void gl_noop(); extern char const * const gl_names[]; extern char const * const egl_names[]; diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp index f89c865761a4..8dcf38dbeccb 100644 --- a/opengl/libs/EGL/getProcAddress.cpp +++ b/opengl/libs/EGL/getProcAddress.cpp @@ -82,23 +82,41 @@ namespace android { #endif + #define GL_EXTENSION_LIST(name) \ - name(0) name(1) name(2) name(3) \ - name(4) name(5) name(6) name(7) \ - name(8) name(9) name(10) name(11) \ - name(12) name(13) name(14) name(15) \ - name(16) name(17) name(18) name(19) \ - name(20) name(21) name(22) name(23) \ - name(24) name(25) name(26) name(27) \ - name(28) name(29) name(30) name(31) \ - name(32) name(33) name(34) name(35) \ - name(36) name(37) name(38) name(39) \ - name(40) name(41) name(42) name(43) \ - name(44) name(45) name(46) name(47) \ - name(48) name(49) name(50) name(51) \ - name(52) name(53) name(54) name(55) \ - name(56) name(57) name(58) name(59) \ - name(60) name(61) name(62) name(63) + name(0) name(1) name(2) name(3) name(4) name(5) name(6) name(7) \ + name(8) name(9) name(10) name(11) name(12) name(13) name(14) name(15) \ + name(16) name(17) name(18) name(19) name(20) name(21) name(22) name(23) \ + name(24) name(25) name(26) name(27) name(28) name(29) name(30) name(31) \ + name(32) name(33) name(34) name(35) name(36) name(37) name(38) name(39) \ + name(40) name(41) name(42) name(43) name(44) name(45) name(46) name(47) \ + name(48) name(49) name(50) name(51) name(52) name(53) name(54) name(55) \ + name(56) name(57) name(58) name(59) name(60) name(61) name(62) name(63) \ + name(64) name(65) name(66) name(67) name(68) name(69) name(70) name(71) \ + name(72) name(73) name(74) name(75) name(76) name(77) name(78) name(79) \ + name(80) name(81) name(82) name(83) name(84) name(85) name(86) name(87) \ + name(88) name(89) name(90) name(91) name(92) name(93) name(94) name(95) \ + name(96) name(97) name(98) name(99) \ + name(100) name(101) name(102) name(103) name(104) name(105) name(106) name(107) \ + name(108) name(109) name(110) name(111) name(112) name(113) name(114) name(115) \ + name(116) name(117) name(118) name(119) name(120) name(121) name(122) name(123) \ + name(124) name(125) name(126) name(127) name(128) name(129) name(130) name(131) \ + name(132) name(133) name(134) name(135) name(136) name(137) name(138) name(139) \ + name(140) name(141) name(142) name(143) name(144) name(145) name(146) name(147) \ + name(148) name(149) name(150) name(151) name(152) name(153) name(154) name(155) \ + name(156) name(157) name(158) name(159) name(160) name(161) name(162) name(163) \ + name(164) name(165) name(166) name(167) name(168) name(169) name(170) name(171) \ + name(172) name(173) name(174) name(175) name(176) name(177) name(178) name(179) \ + name(180) name(181) name(182) name(183) name(184) name(185) name(186) name(187) \ + name(188) name(189) name(190) name(191) name(192) name(193) name(194) name(195) \ + name(196) name(197) name(198) name(199) \ + name(200) name(201) name(202) name(203) name(204) name(205) name(206) name(207) \ + name(208) name(209) name(210) name(211) name(212) name(213) name(214) name(215) \ + name(216) name(217) name(218) name(219) name(220) name(221) name(222) name(223) \ + name(224) name(225) name(226) name(227) name(228) name(229) name(230) name(231) \ + name(232) name(233) name(234) name(235) name(236) name(237) name(238) name(239) \ + name(240) name(241) name(242) name(243) name(244) name(245) name(246) name(247) \ + name(248) name(249) name(250) name(251) name(252) name(253) name(254) name(255) GL_EXTENSION_LIST( GL_EXTENSION ) diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp index df22b968a1b7..79aa3cd59eeb 100644 --- a/opengl/libs/GLES2/gl2.cpp +++ b/opengl/libs/GLES2/gl2.cpp @@ -110,6 +110,20 @@ extern "C" { #undef CALL_GL_API #undef CALL_GL_API_RETURN +/* + * glGetString() is special because we expose some extensions in the wrapper + */ + +extern "C" const GLubyte * __glGetString(GLenum name); + +const GLubyte * glGetString(GLenum name) +{ + const GLubyte * ret = egl_get_string_for_current_context(name); + if (ret == NULL) { + ret = __glGetString(name); + } + return ret; +} /* * These GL calls are special because they need to EGL to retrieve some diff --git a/opengl/libs/GLES2/gl2_api.in b/opengl/libs/GLES2/gl2_api.in index 5164450ba99f..9a89a52ca1ad 100644 --- a/opengl/libs/GLES2/gl2_api.in +++ b/opengl/libs/GLES2/gl2_api.in @@ -211,7 +211,7 @@ void API_ENTRY(glGetShaderPrecisionFormat)(GLenum shadertype, GLenum precisionty void API_ENTRY(glGetShaderSource)(GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* source) { CALL_GL_API(glGetShaderSource, shader, bufsize, length, source); } -const GLubyte* API_ENTRY(glGetString)(GLenum name) { +const GLubyte* API_ENTRY(__glGetString)(GLenum name) { CALL_GL_API_RETURN(glGetString, name); } void API_ENTRY(glGetTexParameterfv)(GLenum target, GLenum pname, GLfloat* params) { diff --git a/opengl/libs/GLES_CM/gl.cpp b/opengl/libs/GLES_CM/gl.cpp index 2d31a35177c2..adeaa5b8233d 100644 --- a/opengl/libs/GLES_CM/gl.cpp +++ b/opengl/libs/GLES_CM/gl.cpp @@ -165,6 +165,20 @@ extern "C" { #undef CALL_GL_API #undef CALL_GL_API_RETURN +/* + * glGetString() is special because we expose some extensions in the wrapper + */ + +extern "C" const GLubyte * __glGetString(GLenum name); + +const GLubyte * glGetString(GLenum name) +{ + const GLubyte * ret = egl_get_string_for_current_context(name); + if (ret == NULL) { + ret = __glGetString(name); + } + return ret; +} /* * These GL calls are special because they need to EGL to retrieve some diff --git a/opengl/libs/GLES_CM/gl_api.in b/opengl/libs/GLES_CM/gl_api.in index 7f20c4fda493..c8f6b0c4d6c3 100644 --- a/opengl/libs/GLES_CM/gl_api.in +++ b/opengl/libs/GLES_CM/gl_api.in @@ -262,7 +262,7 @@ void API_ENTRY(glGetMaterialxv)(GLenum face, GLenum pname, GLfixed *params) { void API_ENTRY(glGetPointerv)(GLenum pname, GLvoid **params) { CALL_GL_API(glGetPointerv, pname, params); } -const GLubyte * API_ENTRY(glGetString)(GLenum name) { +const GLubyte * API_ENTRY(__glGetString)(GLenum name) { CALL_GL_API_RETURN(glGetString, name); } void API_ENTRY(glGetTexEnviv)(GLenum env, GLenum pname, GLint *params) { diff --git a/opengl/libs/egl_impl.h b/opengl/libs/egl_impl.h index a8093168e079..8ff51eca4b7a 100644 --- a/opengl/libs/egl_impl.h +++ b/opengl/libs/egl_impl.h @@ -29,6 +29,7 @@ namespace android { // ---------------------------------------------------------------------------- +EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name); EGLAPI EGLImageKHR egl_get_image_for_current_context(EGLImageKHR image); // ---------------------------------------------------------------------------- diff --git a/opengl/libs/tools/glapigen b/opengl/libs/tools/glapigen index bd8dda362561..9be40cf67d48 100755 --- a/opengl/libs/tools/glapigen +++ b/opengl/libs/tools/glapigen @@ -43,6 +43,9 @@ while (my $line = <>) { if ($name eq "glEGLImageTargetRenderbufferStorageOES") { $prefix = "__"; } + if ($name eq "glGetString") { + $prefix = "__"; + } printf("%s API_ENTRY(%s%s)(%s)", $type, $prefix, $name, $args); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index d0f72a4bc5e6..d787e105c2f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -588,8 +588,12 @@ public class NetworkController extends BroadcastReceiver { } } - if ((isCdma() && isCdmaEri()) || mPhone.isNetworkRoaming()) { - mDataTypeIconId = R.drawable.stat_sys_data_connected_roam; + if (isCdma()) { + if (isCdmaEri()) { + mDataTypeIconId = R.drawable.stat_sys_data_connected_roam; + } + } else if (mPhone.isNetworkRoaming()) { + mDataTypeIconId = R.drawable.stat_sys_data_connected_roam; } } @@ -1019,10 +1023,13 @@ public class NetworkController extends BroadcastReceiver { mContentDescriptionCombinedSignal = mHasMobileDataFeature ? mContentDescriptionDataType : mContentDescriptionWifi; - if ((isCdma() && isCdmaEri()) || mPhone.isNetworkRoaming()) { + mDataTypeIconId = 0; + if (isCdma()) { + if (isCdmaEri()) { + mDataTypeIconId = R.drawable.stat_sys_data_connected_roam; + } + } else if (mPhone.isNetworkRoaming()) { mDataTypeIconId = R.drawable.stat_sys_data_connected_roam; - } else { - mDataTypeIconId = 0; } } diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp index 1f8cf630bc88..296c95ec937e 100644 --- a/services/input/EventHub.cpp +++ b/services/input/EventHub.cpp @@ -831,7 +831,7 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { ALOGV("Opening device: %s", devicePath); - int fd = open(devicePath, O_RDWR); + int fd = open(devicePath, O_RDWR | O_CLOEXEC); if(fd < 0) { ALOGE("could not open %s, %s\n", devicePath, strerror(errno)); return -1; @@ -1063,14 +1063,20 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { return -1; } + // Enable wake-lock behavior on kernels that support it. + // TODO: Only need this for devices that can really wake the system. + bool usingSuspendBlock = ioctl(fd, EVIOCSSUSPENDBLOCK, 1) == 0; + ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, " - "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s", + "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, " + "usingSuspendBlock=%s", deviceId, fd, devicePath, device->identifier.name.string(), device->classes, device->configurationFile.string(), device->keyMap.keyLayoutFile.string(), device->keyMap.keyCharacterMapFile.string(), - toString(mBuiltInKeyboardId == deviceId)); + toString(mBuiltInKeyboardId == deviceId), + toString(usingSuspendBlock)); mDevices.add(deviceId, device); diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk index 95d651acc224..732af5371ec0 100644 --- a/services/surfaceflinger/Android.mk +++ b/services/surfaceflinger/Android.mk @@ -23,18 +23,22 @@ LOCAL_SRC_FILES:= \ LOCAL_CFLAGS:= -DLOG_TAG=\"SurfaceFlinger\" LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES +ifeq ($(TARGET_HAS_WAITFORVSYNC), true) + LOCAL_CFLAGS += -DHAS_WAITFORVSYNC +endif + ifeq ($(TARGET_BOARD_PLATFORM), omap3) LOCAL_CFLAGS += -DNO_RGBX_8888 endif ifeq ($(TARGET_BOARD_PLATFORM), omap4) LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY + LOCAL_CFLAGS += -DUSE_TRIPLE_BUFFERING=1 endif ifeq ($(TARGET_BOARD_PLATFORM), s5pc110) LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY -DNEVER_DEFAULT_TO_ASYNC_MODE LOCAL_CFLAGS += -DREFRESH_RATE=56 endif - LOCAL_SHARED_LIBRARIES := \ libcutils \ libhardware \ diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp index 6796d7dee2a7..92d4266a8fd9 100644 --- a/services/surfaceflinger/EventThread.cpp +++ b/services/surfaceflinger/EventThread.cpp @@ -36,6 +36,7 @@ namespace android { EventThread::EventThread(const sp<SurfaceFlinger>& flinger) : mFlinger(flinger), mHw(flinger->graphicPlane(0).displayHardware()), + mLastVSyncTimestamp(0), mDeliveredEvents(0) { } @@ -44,6 +45,20 @@ void EventThread::onFirstRef() { run("EventThread", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE); } +sp<DisplayEventConnection> EventThread::createEventConnection() const { + return new DisplayEventConnection(const_cast<EventThread*>(this)); +} + +nsecs_t EventThread::getLastVSyncTimestamp() const { + Mutex::Autolock _l(mLock); + return mLastVSyncTimestamp; +} + +nsecs_t EventThread::getVSyncPeriod() const { + return mHw.getRefreshPeriod(); + +} + status_t EventThread::registerDisplayEventConnection( const sp<DisplayEventConnection>& connection) { Mutex::Autolock _l(mLock); @@ -80,8 +95,11 @@ void EventThread::setVsyncRate(uint32_t count, Mutex::Autolock _l(mLock); ConnectionInfo* info = getConnectionInfoLocked(connection); if (info) { - info->count = (count == 0) ? -1 : count; - mCondition.signal(); + const int32_t new_count = (count == 0) ? -1 : count; + if (info->count != new_count) { + info->count = new_count; + mCondition.signal(); + } } } } @@ -90,10 +108,8 @@ void EventThread::requestNextVsync( const wp<DisplayEventConnection>& connection) { Mutex::Autolock _l(mLock); ConnectionInfo* info = getConnectionInfoLocked(connection); - if (info) { - if (info->count < 0) { - info->count = 0; - } + if (info && info->count < 0) { + info->count = 0; mCondition.signal(); } } @@ -132,6 +148,7 @@ bool EventThread::threadLoop() { timestamp = mHw.waitForRefresh(); mLock.lock(); mDeliveredEvents++; + mLastVSyncTimestamp = timestamp; // now see if we still need to report this VSYNC event bool reportVsync = false; diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h index 35bd299a8675..3a3071ef8bea 100644 --- a/services/surfaceflinger/EventThread.h +++ b/services/surfaceflinger/EventThread.h @@ -36,6 +36,7 @@ namespace android { class SurfaceFlinger; class DisplayHardware; +class DisplayEventConnection; // --------------------------------------------------------------------------- @@ -45,6 +46,8 @@ class EventThread : public Thread { public: EventThread(const sp<SurfaceFlinger>& flinger); + sp<DisplayEventConnection> createEventConnection() const; + status_t registerDisplayEventConnection( const sp<DisplayEventConnection>& connection); @@ -56,6 +59,10 @@ public: void requestNextVsync(const wp<DisplayEventConnection>& connection); + nsecs_t getLastVSyncTimestamp() const; + + nsecs_t getVSyncPeriod() const; + void dump(String8& result, char* buffer, size_t SIZE) const; private: @@ -88,6 +95,7 @@ private: // protected by mLock KeyedVector< wp<DisplayEventConnection>, ConnectionInfo > mDisplayEventConnections; + nsecs_t mLastVSyncTimestamp; // main thread only size_t mDeliveredEvents; diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 8e87b88eeaf8..9c04d597205c 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -97,7 +97,12 @@ void Layer::onFirstRef() mSurfaceTexture = new SurfaceTextureLayer(mTextureName, this); mSurfaceTexture->setFrameAvailableListener(new FrameQueuedListener(this)); mSurfaceTexture->setSynchronousMode(true); +#ifdef USE_TRIPLE_BUFFERING +#warning "using triple buffering" + mSurfaceTexture->setBufferCountServer(3); +#else mSurfaceTexture->setBufferCountServer(2); +#endif } Layer::~Layer() diff --git a/services/surfaceflinger/MessageQueue.cpp b/services/surfaceflinger/MessageQueue.cpp index cbd530c822a5..70711e73ee46 100644 --- a/services/surfaceflinger/MessageQueue.cpp +++ b/services/surfaceflinger/MessageQueue.cpp @@ -18,12 +18,17 @@ #include <errno.h> #include <sys/types.h> +#include <binder/IPCThreadState.h> + #include <utils/threads.h> #include <utils/Timers.h> #include <utils/Log.h> -#include <binder/IPCThreadState.h> + +#include <gui/IDisplayEventConnection.h> +#include <gui/BitTube.h> #include "MessageQueue.h" +#include "EventThread.h" namespace android { @@ -51,6 +56,15 @@ MessageQueue::MessageQueue() MessageQueue::~MessageQueue() { } +void MessageQueue::setEventThread(const sp<EventThread>& eventThread) +{ + mEventThread = eventThread; + mEvents = eventThread->createEventConnection(); + mEventTube = mEvents->getDataChannel(); + mLooper->addFd(mEventTube->getFd(), 0, ALOOPER_EVENT_INPUT, + MessageQueue::cb_eventReceiver, this); +} + void MessageQueue::waitMessage() { do { IPCThreadState::self()->flushCommands(); @@ -93,13 +107,54 @@ status_t MessageQueue::postMessage( return NO_ERROR; } -status_t MessageQueue::invalidate() { +void MessageQueue::scheduleWorkASAP() { if (android_atomic_or(1, &mWorkPending) == 0) { mLooper->wake(); - } + } +} + +status_t MessageQueue::invalidate() { + mEvents->requestNextVsync(); return NO_ERROR; } +int MessageQueue::cb_eventReceiver(int fd, int events, void* data) { + MessageQueue* queue = reinterpret_cast<MessageQueue *>(data); + return queue->eventReceiver(fd, events); +} + +int MessageQueue::eventReceiver(int fd, int events) { + ssize_t n; + DisplayEventReceiver::Event buffer[8]; + while ((n = getEvents(buffer, 8)) > 0) { + for (int i=0 ; i<n ; i++) { + if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) { + scheduleWorkASAP(); + break; + } + } + } + return 1; +} + +ssize_t MessageQueue::getEvents( + DisplayEventReceiver::Event* events, size_t count) +{ + ssize_t size = mEventTube->read(events, sizeof(events[0])*count); + ALOGE_IF(size<0, "MessageQueue::getEvents error (%s)", strerror(-size)); + if (size >= 0) { + // Note: if (size % sizeof(events[0])) != 0, we've got a + // partial read. This can happen if the queue filed up (ie: if we + // didn't pull from it fast enough). + // We discard the partial event and rely on the sender to + // re-send the event if appropriate (some events, like VSYNC + // can be lost forever). + // returns number of events read + size /= sizeof(events[0]); + } + return size; +} + // --------------------------------------------------------------------------- }; // namespace android diff --git a/services/surfaceflinger/MessageQueue.h b/services/surfaceflinger/MessageQueue.h index 2317d81ab8e9..5ea197dc70c9 100644 --- a/services/surfaceflinger/MessageQueue.h +++ b/services/surfaceflinger/MessageQueue.h @@ -25,10 +25,15 @@ #include <utils/Timers.h> #include <utils/Looper.h> +#include <gui/DisplayEventReceiver.h> + #include "Barrier.h" namespace android { +class IDisplayEventConnection; +class EventThread; + // --------------------------------------------------------------------------- class MessageBase : public MessageHandler @@ -55,11 +60,20 @@ private: class MessageQueue { sp<Looper> mLooper; - volatile int32_t mWorkPending; + sp<EventThread> mEventThread; + sp<IDisplayEventConnection> mEvents; + sp<BitTube> mEventTube; + int32_t mWorkPending; + + static int cb_eventReceiver(int fd, int events, void* data); + int eventReceiver(int fd, int events); + ssize_t getEvents(DisplayEventReceiver::Event* events, size_t count); + void scheduleWorkASAP(); public: MessageQueue(); ~MessageQueue(); + void setEventThread(const sp<EventThread>& events); void waitMessage(); status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime=0); diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 887aee7bf440..ff70ec327eb3 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -296,6 +296,7 @@ status_t SurfaceFlinger::readyToRun() // start the EventThread mEventThread = new EventThread(this); + mEventQueue.setEventThread(mEventThread); /* * We're now ready to accept clients... @@ -383,8 +384,7 @@ bool SurfaceFlinger::authenticateSurfaceTexture( // ---------------------------------------------------------------------------- sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection() { - sp<DisplayEventConnection> result(new DisplayEventConnection(mEventThread)); - return result; + return mEventThread->createEventConnection(); } // ---------------------------------------------------------------------------- @@ -432,7 +432,6 @@ bool SurfaceFlinger::threadLoop() } else { // pretend we did the post hw.compositionComplete(); - hw.waitForRefresh(); } return true; } diff --git a/services/surfaceflinger/SurfaceTextureLayer.cpp b/services/surfaceflinger/SurfaceTextureLayer.cpp index 259b93766a92..49e8e6393505 100644 --- a/services/surfaceflinger/SurfaceTextureLayer.cpp +++ b/services/surfaceflinger/SurfaceTextureLayer.cpp @@ -94,6 +94,10 @@ status_t SurfaceTextureLayer::connect(int api, *outTransform = orientation; } switch(api) { + case NATIVE_WINDOW_API_CPU: + // SurfaceTextureClient supports only 2 buffers for CPU connections + this->setBufferCountServer(2); + break; case NATIVE_WINDOW_API_MEDIA: case NATIVE_WINDOW_API_CAMERA: // Camera preview and videos are rate-limited on the producer diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java index 05e198f3dbdb..1a4b574ce5dc 100644 --- a/telephony/java/android/telephony/SignalStrength.java +++ b/telephony/java/android/telephony/SignalStrength.java @@ -47,6 +47,9 @@ public class SignalStrength implements Parcelable { "none", "poor", "moderate", "good", "great" }; + /** @hide */ + public static final int INVALID_SNR = 0x7FFFFFFF; + private int mGsmSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5 private int mGsmBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 private int mCdmaDbm; // This value is the RSSI value @@ -96,7 +99,7 @@ public class SignalStrength implements Parcelable { mLteSignalStrength = -1; mLteRsrp = -1; mLteRsrq = -1; - mLteRssnr = -1; + mLteRssnr = INVALID_SNR; mLteCqi = -1; isGsm = true; } @@ -136,7 +139,8 @@ public class SignalStrength implements Parcelable { int evdoDbm, int evdoEcio, int evdoSnr, boolean gsm) { this(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio, - evdoDbm, evdoEcio, evdoSnr, -1, -1, -1, -1, -1, gsm); + evdoDbm, evdoEcio, evdoSnr, -1, -1, + -1, INVALID_SNR, -1, gsm); } /** @@ -292,7 +296,7 @@ public class SignalStrength implements Parcelable { if ((mLteSignalStrength == -1) && (mLteRsrp == -1) && (mLteRsrq == -1) - && (mLteRssnr == -1) + && (mLteRssnr == INVALID_SNR) && (mLteCqi == -1)) { level = getGsmLevel(); } else { @@ -327,7 +331,7 @@ public class SignalStrength implements Parcelable { if ((mLteSignalStrength == -1) && (mLteRsrp == -1) && (mLteRsrq == -1) - && (mLteRssnr == -1) + && (mLteRssnr == INVALID_SNR) && (mLteCqi == -1)) { asuLevel = getGsmAsuLevel(); } else { @@ -363,7 +367,7 @@ public class SignalStrength implements Parcelable { if ((mLteSignalStrength == -1) && (mLteRsrp == -1) && (mLteRsrq == -1) - && (mLteRssnr == -1) + && (mLteRssnr == INVALID_SNR) && (mLteCqi == -1)) { dBm = getGsmDbm(); } else { @@ -566,6 +570,7 @@ public class SignalStrength implements Parcelable { */ public int getLteLevel() { int levelLteRsrp = 0; + int levelLteRssnr = 0; if (mLteRsrp == -1) levelLteRsrp = 0; else if (mLteRsrp >= -95) levelLteRsrp = SIGNAL_STRENGTH_GREAT; @@ -573,8 +578,23 @@ public class SignalStrength implements Parcelable { else if (mLteRsrp >= -115) levelLteRsrp = SIGNAL_STRENGTH_MODERATE; else levelLteRsrp = SIGNAL_STRENGTH_POOR; - if (DBG) log("Lte level: "+levelLteRsrp); - return levelLteRsrp; + if (mLteRssnr == INVALID_SNR) levelLteRssnr = 0; + else if (mLteRssnr >= 45) levelLteRssnr = SIGNAL_STRENGTH_GREAT; + else if (mLteRssnr >= 10) levelLteRssnr = SIGNAL_STRENGTH_GOOD; + else if (mLteRssnr >= -30) levelLteRssnr = SIGNAL_STRENGTH_MODERATE; + else levelLteRssnr = SIGNAL_STRENGTH_POOR; + + int level; + if (mLteRsrp == -1) + level = levelLteRssnr; + else if (mLteRssnr == INVALID_SNR) + level = levelLteRsrp; + else + level = (levelLteRssnr < levelLteRsrp) ? levelLteRssnr : levelLteRsrp; + + if (DBG) log("Lte rsrp level: "+levelLteRsrp + + " snr level: " + levelLteRssnr + " level: " + level); + return level; } /** diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java index 6b73cc5d4bf1..7da23d05e3d9 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java @@ -124,7 +124,9 @@ public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker { @Override protected void setSignalStrengthDefaultValues() { - mSignalStrength = new SignalStrength(99, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, false); + // TODO Make a constructor only has boolean gsm as parameter + mSignalStrength = new SignalStrength(99, -1, -1, -1, -1, -1, -1, + -1, -1, -1, SignalStrength.INVALID_SNR, -1, false); } @Override @@ -429,8 +431,13 @@ public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker { setSignalStrengthDefaultValues(); } else { int[] ints = (int[])ar.result; - int lteCqi = 99, lteRsrp = -1; - int lteRssi = 99; + + int lteRssi = -1; + int lteRsrp = -1; + int lteRsrq = -1; + int lteRssnr = SignalStrength.INVALID_SNR; + int lteCqi = -1; + int offset = 2; int cdmaDbm = (ints[offset] > 0) ? -ints[offset] : -120; int cdmaEcio = (ints[offset + 1] > 0) ? -ints[offset + 1] : -160; @@ -438,10 +445,13 @@ public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker { int evdoEcio = (ints[offset + 3] > 0) ? -ints[offset + 3] : -1; int evdoSnr = ((ints[offset + 4] > 0) && (ints[offset + 4] <= 8)) ? ints[offset + 4] : -1; + if (networkType == ServiceState.RADIO_TECHNOLOGY_LTE) { - lteRssi = (ints[offset + 5] >= 0) ? ints[offset + 5] : 99; - lteRsrp = (ints[offset + 6] < 0) ? ints[offset + 6] : -1; - lteCqi = (ints[offset + 7] >= 0) ? ints[offset + 7] : 99; + lteRssi = ints[offset+5]; + lteRsrp = ints[offset+6]; + lteRsrq = ints[offset+7]; + lteRssnr = ints[offset+8]; + lteCqi = ints[offset+9]; } if (networkType != ServiceState.RADIO_TECHNOLOGY_LTE) { @@ -449,7 +459,7 @@ public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker { evdoSnr, false); } else { mSignalStrength = new SignalStrength(99, -1, cdmaDbm, cdmaEcio, evdoRssi, evdoEcio, - evdoSnr, lteRssi, lteRsrp, -1, -1, lteCqi, true); + evdoSnr, lteRssi, lteRsrp, lteRsrq, lteRssnr, lteCqi, true); } } diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java index 92e16ce649f0..0ebeabedacfc 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java @@ -675,7 +675,9 @@ final class GsmServiceStateTracker extends ServiceStateTracker { } private void setSignalStrengthDefaultValues() { - mSignalStrength = new SignalStrength(99, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, true); + // TODO Make a constructor only has boolean gsm as parameter + mSignalStrength = new SignalStrength(99, -1, -1, -1, -1, -1, -1, + -1, -1, -1, SignalStrength.INVALID_SNR, -1, true); } /** @@ -1013,7 +1015,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker { int lteSignalStrength = -1; int lteRsrp = -1; int lteRsrq = -1; - int lteRssnr = -1; + int lteRssnr = SignalStrength.INVALID_SNR; int lteCqi = -1; if (ar.exception != null) { diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java index e2d2686e01b1..5b7627295902 100644 --- a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java +++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java @@ -512,7 +512,8 @@ public class PhoneNumberUtilsTest extends AndroidTestCase { public void testFormatNumber() { assertEquals("(650) 291-0000", PhoneNumberUtils.formatNumber("650 2910000", "US")); assertEquals("223-4567", PhoneNumberUtils.formatNumber("2234567", "US")); - assertEquals("(800) 466-4114", PhoneNumberUtils.formatNumber("800-GOOG-114", "US")); + // formatNumber doesn't format alpha numbers, but keep them as they are. + assertEquals("800-GOOG-114", PhoneNumberUtils.formatNumber("800-GOOG-114", "US")); } @SmallTest @@ -592,9 +593,12 @@ public class PhoneNumberUtilsTest extends AndroidTestCase { // addressing that, they are also classified as "potential" emergency numbers in the US. assertTrue(PhoneNumberUtils.isPotentialEmergencyNumber("91112345", "US")); assertTrue(PhoneNumberUtils.isPotentialEmergencyNumber("11212345", "US")); + // A valid mobile phone number from Singapore shouldn't be classified as an emergency number // in Singapore, as 911 is not an emergency number there. - assertFalse(PhoneNumberUtils.isPotentialEmergencyNumber("91121234", "SG")); + // This test fails on devices that have ecclist property preloaded with 911. + // assertFalse(PhoneNumberUtils.isPotentialEmergencyNumber("91121234", "SG")); + // A valid fixed-line phone number from Brazil shouldn't be classified as an emergency number // in Brazil, as 112 is not an emergency number there. assertFalse(PhoneNumberUtils.isPotentialEmergencyNumber("1121234567", "BR")); |