diff options
55 files changed, 659 insertions, 812 deletions
diff --git a/api/current.txt b/api/current.txt index 9610853..9eb2075 100644 --- a/api/current.txt +++ b/api/current.txt @@ -4244,6 +4244,7 @@ package android.bluetooth { method public int getProfileConnectionState(int); method public boolean getProfileProxy(android.content.Context, android.bluetooth.BluetoothProfile.ServiceListener, int); method public android.bluetooth.BluetoothDevice getRemoteDevice(java.lang.String); + method public android.bluetooth.BluetoothDevice getRemoteDevice(byte[]); method public int getScanMode(); method public int getState(); method public boolean isDiscovering(); diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 5f5ba50..e420bfd 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -399,6 +399,25 @@ public final class BluetoothAdapter { } /** + * Get a {@link BluetoothDevice} object for the given Bluetooth hardware + * address. + * <p>Valid Bluetooth hardware addresses must be 6 bytes. This method + * expects the address in network byte order (MSB first). + * <p>A {@link BluetoothDevice} will always be returned for a valid + * hardware address, even if this adapter has never seen that device. + * + * @param address Bluetooth MAC address (6 bytes) + * @throws IllegalArgumentException if address is invalid + */ + public BluetoothDevice getRemoteDevice(byte[] address) { + if (address == null || address.length != 6) { + throw new IllegalArgumentException("Bluetooth address must have 6 bytes"); + } + return new BluetoothDevice(String.format("%02X:%02X:%02X:%02X:%02X:%02X", + address[0], address[1], address[2], address[3], address[4], address[5])); + } + + /** * Return true if Bluetooth is currently enabled and ready for use. * <p>Equivalent to: * <code>getBluetoothState() == STATE_ON</code> @@ -1281,7 +1300,7 @@ public final class BluetoothAdapter { } /** - * Validate a Bluetooth address, such as "00:43:A8:23:10:F0" + * Validate a String Bluetooth address, such as "00:43:A8:23:10:F0" * <p>Alphabetic characters must be uppercase to be valid. * * @param address Bluetooth address as string diff --git a/core/java/android/inputmethodservice/ExtractEditText.java b/core/java/android/inputmethodservice/ExtractEditText.java index 72431f3..10c1195 100644 --- a/core/java/android/inputmethodservice/ExtractEditText.java +++ b/core/java/android/inputmethodservice/ExtractEditText.java @@ -158,25 +158,46 @@ public class ExtractEditText extends EditText { } /** - * Delete the range of text, supposedly valid + * {@inheritDoc} * @hide */ @Override protected void deleteText_internal(int start, int end) { - // Do not call the super method. This will change the source TextView instead, which - // will update the ExtractTextView. + // Do not call the super method. + // This will change the source TextView instead, which will update the ExtractTextView. mIME.onExtractedDeleteText(start, end); } /** - * Replaces the range of text [start, end[ by replacement text + * {@inheritDoc} * @hide */ @Override protected void replaceText_internal(int start, int end, CharSequence text) { - // Do not call the super method. This will change the source TextView instead, which - // will update the ExtractTextView. + // Do not call the super method. + // This will change the source TextView instead, which will update the ExtractTextView. mIME.onExtractedReplaceText(start, end, text); } + /** + * {@inheritDoc} + * @hide + */ + @Override + protected void setSpan_internal(Object span, int start, int end, int flags) { + // Do not call the super method. + // This will change the source TextView instead, which will update the ExtractTextView. + mIME.onExtractedSetSpan(span, start, end, flags); + } + + /** + * {@inheritDoc} + * @hide + */ + @Override + protected void setCursorPosition_internal(int start, int end) { + // Do not call the super method. + // This will change the source TextView instead, which will update the ExtractTextView. + mIME.onExtractedSelectionChanged(start, end); + } } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 02839db..53cdf21 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -2006,6 +2006,22 @@ public class InputMethodService extends AbstractInputMethodService { } /** + * @hide + */ + public void onExtractedSetSpan(Object span, int start, int end, int flags) { + InputConnection conn = getCurrentInputConnection(); + if (conn != null) { + if (!conn.setSelection(start, end)) return; + CharSequence text = conn.getSelectedText(InputConnection.GET_TEXT_WITH_STYLES); + if (text instanceof Spannable) { + ((Spannable) text).setSpan(span, 0, text.length(), flags); + conn.setComposingRegion(start, end); + conn.commitText(text, 1); + } + } + } + + /** * This is called when the user has clicked on the extracted text view, * when running in fullscreen mode. The default implementation hides * the candidates view when this happens, but only if the extracted text diff --git a/core/java/android/speech/tts/AudioPlaybackQueueItem.java b/core/java/android/speech/tts/AudioPlaybackQueueItem.java index 668b459..1a1fda8 100644 --- a/core/java/android/speech/tts/AudioPlaybackQueueItem.java +++ b/core/java/android/speech/tts/AudioPlaybackQueueItem.java @@ -15,27 +15,91 @@ */ package android.speech.tts; +import android.content.Context; +import android.media.MediaPlayer; +import android.net.Uri; +import android.os.ConditionVariable; import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher; import android.util.Log; class AudioPlaybackQueueItem extends PlaybackQueueItem { - private final BlockingMediaPlayer mPlayer; + private static final String TAG = "TTS.AudioQueueItem"; + + private final Context mContext; + private final Uri mUri; + private final int mStreamType; + + private final ConditionVariable mDone; + private MediaPlayer mPlayer; + private volatile boolean mFinished; AudioPlaybackQueueItem(UtteranceProgressDispatcher dispatcher, - Object callerIdentity, BlockingMediaPlayer player) { + Object callerIdentity, + Context context, Uri uri, int streamType) { super(dispatcher, callerIdentity); - mPlayer = player; + + mContext = context; + mUri = uri; + mStreamType = streamType; + + mDone = new ConditionVariable(); + mPlayer = null; + mFinished = false; } @Override public void run() { - getDispatcher().dispatchOnStart(); - // TODO: This can be avoided. Will be fixed later in this CL. - mPlayer.startAndWait(); - getDispatcher().dispatchOnDone(); + final UtteranceProgressDispatcher dispatcher = getDispatcher(); + + dispatcher.dispatchOnStart(); + mPlayer = MediaPlayer.create(mContext, mUri); + if (mPlayer == null) { + dispatcher.dispatchOnError(); + return; + } + + try { + mPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { + @Override + public boolean onError(MediaPlayer mp, int what, int extra) { + Log.w(TAG, "Audio playback error: " + what + ", " + extra); + mDone.open(); + return true; + } + }); + mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { + @Override + public void onCompletion(MediaPlayer mp) { + mFinished = true; + mDone.open(); + } + }); + mPlayer.setAudioStreamType(mStreamType); + mPlayer.start(); + mDone.block(); + finish(); + } catch (IllegalArgumentException ex) { + Log.w(TAG, "MediaPlayer failed", ex); + mDone.open(); + } + + if (mFinished) { + dispatcher.dispatchOnDone(); + } else { + dispatcher.dispatchOnError(); + } + } + + private void finish() { + try { + mPlayer.stop(); + } catch (IllegalStateException ex) { + // Do nothing, the player is already stopped + } + mPlayer.release(); } @Override void stop(boolean isError) { - mPlayer.stop(); + mDone.open(); } } diff --git a/core/java/android/speech/tts/BlockingMediaPlayer.java b/core/java/android/speech/tts/BlockingMediaPlayer.java deleted file mode 100644 index 1ccc6e4..0000000 --- a/core/java/android/speech/tts/BlockingMediaPlayer.java +++ /dev/null @@ -1,145 +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. - */ -package android.speech.tts; - -import android.content.Context; -import android.media.MediaPlayer; -import android.net.Uri; -import android.os.ConditionVariable; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.util.Log; - -/** - * A media player that allows blocking to wait for it to finish. - */ -class BlockingMediaPlayer { - - private static final String TAG = "BlockMediaPlayer"; - - private static final String MEDIA_PLAYER_THREAD_NAME = "TTS-MediaPlayer"; - - private final Context mContext; - private final Uri mUri; - private final int mStreamType; - private final ConditionVariable mDone; - // Only accessed on the Handler thread - private MediaPlayer mPlayer; - private volatile boolean mFinished; - - /** - * Creates a new blocking media player. - * Creating a blocking media player is a cheap operation. - * - * @param context - * @param uri - * @param streamType - */ - public BlockingMediaPlayer(Context context, Uri uri, int streamType) { - mContext = context; - mUri = uri; - mStreamType = streamType; - mDone = new ConditionVariable(); - } - - /** - * Starts playback and waits for it to finish. - * Can be called from any thread. - * - * @return {@code true} if the playback finished normally, {@code false} if the playback - * failed or {@link #stop} was called before the playback finished. - */ - public boolean startAndWait() { - HandlerThread thread = new HandlerThread(MEDIA_PLAYER_THREAD_NAME); - thread.start(); - Handler handler = new Handler(thread.getLooper()); - mFinished = false; - handler.post(new Runnable() { - @Override - public void run() { - startPlaying(); - } - }); - mDone.block(); - handler.post(new Runnable() { - @Override - public void run() { - finish(); - // No new messages should get posted to the handler thread after this - Looper.myLooper().quit(); - } - }); - return mFinished; - } - - /** - * Stops playback. Can be called multiple times. - * Can be called from any thread. - */ - public void stop() { - mDone.open(); - } - - /** - * Starts playback. - * Called on the handler thread. - */ - private void startPlaying() { - mPlayer = MediaPlayer.create(mContext, mUri); - if (mPlayer == null) { - Log.w(TAG, "Failed to play " + mUri); - mDone.open(); - return; - } - try { - mPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { - @Override - public boolean onError(MediaPlayer mp, int what, int extra) { - Log.w(TAG, "Audio playback error: " + what + ", " + extra); - mDone.open(); - return true; - } - }); - mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { - @Override - public void onCompletion(MediaPlayer mp) { - mFinished = true; - mDone.open(); - } - }); - mPlayer.setAudioStreamType(mStreamType); - mPlayer.start(); - } catch (IllegalArgumentException ex) { - Log.w(TAG, "MediaPlayer failed", ex); - mDone.open(); - } - } - - /** - * Stops playback and release the media player. - * Called on the handler thread. - */ - private void finish() { - try { - mPlayer.stop(); - } catch (IllegalStateException ex) { - // Do nothing, the player is already stopped - } - mPlayer.release(); - } - -}
\ No newline at end of file diff --git a/core/java/android/speech/tts/MessageParams.java b/core/java/android/speech/tts/MessageParams.java deleted file mode 100644 index f83b793..0000000 --- a/core/java/android/speech/tts/MessageParams.java +++ /dev/null @@ -1,47 +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. - */ -package android.speech.tts; - -import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher; - -abstract class MessageParams { - static final int TYPE_SYNTHESIS = 1; - static final int TYPE_AUDIO = 2; - static final int TYPE_SILENCE = 3; - - private final UtteranceProgressDispatcher mDispatcher; - private final Object mCallerIdentity; - - MessageParams(UtteranceProgressDispatcher dispatcher, Object callerIdentity) { - mDispatcher = dispatcher; - mCallerIdentity = callerIdentity; - } - - UtteranceProgressDispatcher getDispatcher() { - return mDispatcher; - } - - Object getCallerIdentity() { - return mCallerIdentity; - } - - @Override - public String toString() { - return "MessageParams[" + hashCode() + "]"; - } - - abstract int getType(); -} diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index ba8485a..4c1a0af 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -694,12 +694,12 @@ public abstract class TextToSpeechService extends Service { } private class AudioSpeechItem extends SpeechItem { - private final BlockingMediaPlayer mPlayer; - + private final AudioPlaybackQueueItem mItem; public AudioSpeechItem(Object callerIdentity, int callerUid, int callerPid, Bundle params, Uri uri) { super(callerIdentity, callerUid, callerPid, params); - mPlayer = new BlockingMediaPlayer(TextToSpeechService.this, uri, getStreamType()); + mItem = new AudioPlaybackQueueItem(this, getCallerIdentity(), + TextToSpeechService.this, uri, getStreamType()); } @Override @@ -709,8 +709,7 @@ public abstract class TextToSpeechService extends Service { @Override protected int playImpl() { - mAudioPlaybackHandler.enqueue(new AudioPlaybackQueueItem( - this, getCallerIdentity(), mPlayer)); + mAudioPlaybackHandler.enqueue(mItem); return TextToSpeech.SUCCESS; } diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java index 231f913..b708750 100644 --- a/core/java/android/text/SpannableStringBuilder.java +++ b/core/java/android/text/SpannableStringBuilder.java @@ -863,6 +863,17 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable return new String(buf); } + /** + * Return a String containing a copy of the chars in this buffer, limited to the + * [start, end[ range. + * @hide + */ + public String substring(int start, int end) { + char[] buf = new char[end - start]; + getChars(start, end, buf, 0); + return new String(buf); + } + private TextWatcher[] sendTextWillChange(int start, int before, int after) { TextWatcher[] recip = getSpans(start, start + before, TextWatcher.class); int n = recip.length; diff --git a/core/java/android/text/method/WordIterator.java b/core/java/android/text/method/WordIterator.java index 239d9e8..11226a9 100644 --- a/core/java/android/text/method/WordIterator.java +++ b/core/java/android/text/method/WordIterator.java @@ -18,6 +18,7 @@ package android.text.method; import android.text.Selection; +import android.text.SpannableStringBuilder; import java.text.BreakIterator; import java.util.Locale; @@ -58,7 +59,11 @@ public class WordIterator implements Selection.PositionIterator { mOffsetShift = Math.max(0, start - WINDOW_WIDTH); final int windowEnd = Math.min(charSequence.length(), end + WINDOW_WIDTH); - mString = charSequence.toString().substring(mOffsetShift, windowEnd); + if (charSequence instanceof SpannableStringBuilder) { + mString = ((SpannableStringBuilder) charSequence).substring(mOffsetShift, windowEnd); + } else { + mString = charSequence.subSequence(mOffsetShift, windowEnd).toString(); + } mIterator.setText(mString); } diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java index ed2af10..0f26a34 100644 --- a/core/java/android/text/style/SuggestionSpan.java +++ b/core/java/android/text/style/SuggestionSpan.java @@ -92,11 +92,6 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { private float mAutoCorrectionUnderlineThickness; private int mAutoCorrectionUnderlineColor; - /* - * TODO: If switching IME is required, needs to add parameters for ids of InputMethodInfo - * and InputMethodSubtype. - */ - /** * @param context Context for the application * @param suggestions Suggestions for the string under the span @@ -146,6 +141,16 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { } private void initStyle(Context context) { + if (context == null) { + mMisspelledUnderlineThickness = 0; + mEasyCorrectUnderlineThickness = 0; + mAutoCorrectionUnderlineThickness = 0; + mMisspelledUnderlineColor = Color.BLACK; + mEasyCorrectUnderlineColor = Color.BLACK; + mAutoCorrectionUnderlineColor = Color.BLACK; + return; + } + int defStyle = com.android.internal.R.attr.textAppearanceMisspelledSuggestion; TypedArray typedArray = context.obtainStyledAttributes( null, com.android.internal.R.styleable.SuggestionSpan, defStyle, 0); @@ -169,7 +174,6 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { com.android.internal.R.styleable.SuggestionSpan_textUnderlineThickness, 0); mAutoCorrectionUnderlineColor = typedArray.getColor( com.android.internal.R.styleable.SuggestionSpan_textUnderlineColor, Color.BLACK); - } public SuggestionSpan(Parcel src) { diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 443acf6..3f793bb 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -441,6 +441,8 @@ public abstract class HardwareRenderer { } boolean mDirtyRegionsEnabled; + boolean mUpdateDirtyRegions; + final boolean mVsyncDisabled; final int mGlVersion; @@ -675,6 +677,12 @@ public abstract class HardwareRenderer { initCaches(); + enableDirtyRegions(); + + return mEglContext.getGL(); + } + + private void enableDirtyRegions() { // If mDirtyRegions is set, this means we have an EGL configuration // with EGL_SWAP_BEHAVIOR_PRESERVED_BIT set if (sDirtyRegions) { @@ -690,8 +698,6 @@ public abstract class HardwareRenderer { // configuration (see RENDER_DIRTY_REGIONS) mDirtyRegionsEnabled = GLES20Canvas.isBackBufferPreserved(); } - - return mEglContext.getGL(); } abstract void initCaches(); @@ -745,6 +751,9 @@ public abstract class HardwareRenderer { if (!createSurface(holder)) { return; } + + mUpdateDirtyRegions = true; + if (mCanvas != null) { setEnabled(true); } @@ -943,6 +952,10 @@ public abstract class HardwareRenderer { fallback(true); return SURFACE_STATE_ERROR; } else { + if (mUpdateDirtyRegions) { + enableDirtyRegions(); + mUpdateDirtyRegions = false; + } return SURFACE_STATE_UPDATED; } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 7a9d82c..72966ef 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -3257,8 +3257,9 @@ public final class ViewRootImpl extends Handler implements ViewParent, } // If the Control modifier is held, try to interpret the key as a shortcut. - if (event.getAction() == KeyEvent.ACTION_UP + if (event.getAction() == KeyEvent.ACTION_DOWN && event.isCtrlPressed() + && event.getRepeatCount() == 0 && !KeyEvent.isModifierKey(event.getKeyCode())) { if (mView.dispatchKeyShortcutEvent(event)) { finishInputEvent(q, true); diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 40cb248..a284a17 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -2496,11 +2496,12 @@ public class WebView extends AbsoluteLayout } /** - * Return the reading level scale of the WebView + * Compute the reading level scale of the WebView + * @param scale The current scale. * @return The reading level scale. */ - /*package*/ float getReadingLevelScale() { - return mZoomManager.getReadingLevelScale(); + /*package*/ float computeReadingLevelScale(float scale) { + return mZoomManager.computeReadingLevelScale(scale); } /** diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 0f749bc..14da23e 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -2514,7 +2514,7 @@ public final class WebViewCore { if (mSettings.isNarrowColumnLayout()) { // In case of automatic text reflow in fixed view port mode. mInitialViewState.mTextWrapScale = - mWebView.getReadingLevelScale(); + mWebView.computeReadingLevelScale(data.mScale); } } else { // Scale is given such as when page is restored, use it. diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java index 14bdc42..8ffba64 100644 --- a/core/java/android/webkit/ZoomManager.java +++ b/core/java/android/webkit/ZoomManager.java @@ -316,7 +316,12 @@ class ZoomManager { * Returns the zoom scale used for reading text on a double-tap. */ public final float getReadingLevelScale() { - return mDisplayDensity * mDoubleTapZoomFactor; + return computeScaleWithLimits(computeReadingLevelScale(getZoomOverviewScale())); + } + + /* package */ final float computeReadingLevelScale(float scale) { + return Math.max(mDisplayDensity * mDoubleTapZoomFactor, + scale + MIN_DOUBLE_TAP_SCALE_INCREMENT); } public final float getInvDefaultScale() { @@ -678,7 +683,7 @@ class ZoomManager { } zoomToOverview(); } else { - zoomToReadingLevelOrMore(); + zoomToReadingLevel(); } } @@ -709,9 +714,8 @@ class ZoomManager { !mWebView.getSettings().getUseFixedViewport()); } - private void zoomToReadingLevelOrMore() { - final float zoomScale = Math.max(getReadingLevelScale(), - mActualScale + MIN_DOUBLE_TAP_SCALE_INCREMENT); + private void zoomToReadingLevel() { + final float readingScale = getReadingLevelScale(); int left = mWebView.nativeGetBlockLeftEdge(mAnchorX, mAnchorY, mActualScale); if (left != WebView.NO_LEFTEDGE) { @@ -721,13 +725,13 @@ class ZoomManager { // Re-calculate the zoom center so that the new scroll x will be // on the left edge. if (viewLeft > 0) { - mZoomCenterX = viewLeft * zoomScale / (zoomScale - mActualScale); + mZoomCenterX = viewLeft * readingScale / (readingScale - mActualScale); } else { mWebView.scrollBy(viewLeft, 0); mZoomCenterX = 0; } } - startZoomAnimation(zoomScale, + startZoomAnimation(readingScale, !mWebView.getSettings().getUseFixedViewport()); } diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index 9d541e0..7d0f98e 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -774,6 +774,7 @@ public class NumberPicker extends LinearLayout { mBeginEditOnUpEvent = false; mAdjustScrollerOnUpEvent = true; if (mSelectorWheelState == SELECTOR_WHEEL_STATE_LARGE) { + mSelectorWheelPaint.setAlpha(SELECTOR_WHEEL_BRIGHT_ALPHA); boolean scrollersFinished = mFlingScroller.isFinished() && mAdjustScroller.isFinished(); if (!scrollersFinished) { @@ -1608,23 +1609,11 @@ public class NumberPicker extends LinearLayout { */ private void fling(int velocityY) { mPreviousScrollerY = 0; - Scroller flingScroller = mFlingScroller; - if (mWrapSelectorWheel) { - if (velocityY > 0) { - flingScroller.fling(0, 0, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE); - } else { - flingScroller.fling(0, Integer.MAX_VALUE, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE); - } + if (velocityY > 0) { + mFlingScroller.fling(0, 0, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE); } else { - if (velocityY > 0) { - int maxY = mTextSize * (mValue - mMinValue); - flingScroller.fling(0, 0, 0, velocityY, 0, 0, 0, maxY); - } else { - int startY = mTextSize * (mMaxValue - mValue); - int maxY = startY; - flingScroller.fling(0, startY, 0, velocityY, 0, 0, 0, maxY); - } + mFlingScroller.fling(0, Integer.MAX_VALUE, 0, velocityY, 0, 0, 0, Integer.MAX_VALUE); } invalidate(); diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java index 4bd7165..31da5b5 100644 --- a/core/java/android/widget/SpellChecker.java +++ b/core/java/android/widget/SpellChecker.java @@ -19,6 +19,7 @@ package android.widget; import android.content.Context; import android.text.Editable; import android.text.Selection; +import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.method.WordIterator; import android.text.style.SpellCheckSpan; @@ -220,7 +221,6 @@ public class SpellChecker implements SpellCheckerSessionListener { TextInfo[] textInfos = new TextInfo[mLength]; int textInfosCount = 0; - final String text = editable.toString(); for (int i = 0; i < mLength; i++) { final SpellCheckSpan spellCheckSpan = mSpellCheckSpans[i]; if (spellCheckSpan.isSpellCheckInProgress()) continue; @@ -230,7 +230,9 @@ public class SpellChecker implements SpellCheckerSessionListener { // Do not check this word if the user is currently editing it if (start >= 0 && end > start && (selectionEnd < start || selectionStart > end)) { - final String word = text.substring(start, end); + final String word = (editable instanceof SpannableStringBuilder) ? + ((SpannableStringBuilder) editable).substring(start, end) : + editable.subSequence(start, end).toString(); spellCheckSpan.setSpellCheckInProgress(true); textInfos[textInfosCount++] = new TextInfo(word, mCookie, mIds[i]); } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index babd8e7..5b73a74 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -5517,7 +5517,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * call performClick(), but that won't do anything in * this case.) */ - if (hasOnClickListeners()) { + if (!hasOnClickListeners()) { if (mMovement != null && mText instanceof Editable && mLayout != null && onCheckIsTextEditor()) { InputMethodManager imm = InputMethodManager.peekInstance(); @@ -5554,7 +5554,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * call performClick(), but that won't do anything in * this case.) */ - if (hasOnClickListeners()) { + if (!hasOnClickListeners()) { View v = focusSearch(FOCUS_DOWN); if (v != null) { @@ -9835,7 +9835,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener highlightTextDifferences(mSuggestionInfos[i], spanUnionStart, spanUnionEnd); } - // Add to dictionary item is there a span with the misspelled flag + // Add to dictionary item if there is a span with the misspelled flag if (misspelledSpan != null) { final int misspelledStart = spannable.getSpanStart(misspelledSpan); final int misspelledEnd = spannable.getSpanEnd(misspelledSpan); @@ -9921,7 +9921,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int spanStart = editable.getSpanStart(suggestionInfo.suggestionSpan); final int spanEnd = editable.getSpanEnd(suggestionInfo.suggestionSpan); - if (spanStart < 0 || spanEnd < 0) { + if (spanStart < 0 || spanEnd <= spanStart) { // Span has been removed hide(); return; @@ -9989,14 +9989,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // way to assign them a valid range after replacement if (suggestionSpansStarts[i] <= spanStart && suggestionSpansEnds[i] >= spanEnd) { - // TODO The ExtractEditText should restore these spans in the original text - editable.setSpan(suggestionSpans[i], suggestionSpansStarts[i], + setSpan_internal(suggestionSpans[i], suggestionSpansStarts[i], suggestionSpansEnds[i] + lengthDifference, suggestionSpansFlags[i]); } } // Move cursor at the end of the replaced word - Selection.setSelection(editable, spanEnd + lengthDifference); + final int newCursorPosition = spanEnd + lengthDifference; + setCursorPosition_internal(newCursorPosition, newCursorPosition); } hide(); @@ -11469,6 +11469,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener ((Editable) mText).replace(start, end, text); } + /** + * Sets a span on the specified range of text + * @hide + */ + protected void setSpan_internal(Object span, int start, int end, int flags) { + ((Editable) mText).setSpan(span, start, end, flags); + } + + /** + * Moves the cursor to the specified offset position in text + * @hide + */ + protected void setCursorPosition_internal(int start, int end) { + Selection.setSelection(((Editable) mText), start, end); + } + @ViewDebug.ExportedProperty(category = "text") private CharSequence mText; private CharSequence mTransformed; diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp index 7724646..feba1e6 100644 --- a/core/jni/android/graphics/Canvas.cpp +++ b/core/jni/android/graphics/Canvas.cpp @@ -732,11 +732,7 @@ public: jcharArray text, int index, int count, jfloat x, jfloat y, int flags, SkPaint* paint) { jchar* textArray = env->GetCharArrayElements(text, NULL); -#if RTL_USE_HARFBUZZ drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, flags, paint); -#else - TextLayout::drawText(paint, textArray + index, count, flags, x, y, canvas); -#endif env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); } @@ -745,11 +741,7 @@ public: int start, int end, jfloat x, jfloat y, int flags, SkPaint* paint) { const jchar* textArray = env->GetStringChars(text, NULL); -#if RTL_USE_HARFBUZZ drawTextWithGlyphs(canvas, textArray, start, end, x, y, flags, paint); -#else - TextLayout::drawText(paint, textArray + start, end - start, flags, x, y, canvas); -#endif env->ReleaseStringChars(text, textArray); } @@ -770,12 +762,14 @@ public: value = TextLayoutCache::getInstance().getValue(paint, textArray, start, count, contextCount, flags); if (value == NULL) { - LOGE("Cannot get TextLayoutCache value"); + LOGE("Cannot get TextLayoutCache value for text = '%s'", + String8(textArray + start, count).string()); return ; } #else - value = new TextLayoutCacheValue(); - value->computeValues(paint, textArray, start, count, contextCount, flags); + value = new TextLayoutCacheValue(contextCount); + TextLayoutEngine::getInstance().computeValues(value.get(), paint, + reinterpret_cast<const UChar*>(textArray), start, count, contextCount, flags); #endif doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(), x, y, flags, paint); } @@ -802,13 +796,8 @@ public: jfloat x, jfloat y, int dirFlags, SkPaint* paint) { jchar* chars = env->GetCharArrayElements(text, NULL); -#if RTL_USE_HARFBUZZ drawTextWithGlyphs(canvas, chars + contextIndex, index - contextIndex, count, contextCount, x, y, dirFlags, paint); -#else - TextLayout::drawTextRun(paint, chars + contextIndex, index - contextIndex, - count, contextCount, dirFlags, x, y, canvas); -#endif env->ReleaseCharArrayElements(text, chars, JNI_ABORT); } @@ -820,13 +809,8 @@ public: jint count = end - start; jint contextCount = contextEnd - contextStart; const jchar* chars = env->GetStringChars(text, NULL); -#if RTL_USE_HARFBUZZ drawTextWithGlyphs(canvas, chars + contextStart, start - contextStart, count, contextCount, x, y, dirFlags, paint); -#else - TextLayout::drawTextRun(paint, chars + contextStart, start - contextStart, - count, contextCount, dirFlags, x, y, canvas); -#endif env->ReleaseStringChars(text, chars); } diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index 883940b..19f53d7 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -350,14 +350,10 @@ public: SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint); const jchar* textArray = env->GetCharArrayElements(text, NULL); jfloat result = 0; -#if RTL_USE_HARFBUZZ + TextLayout::getTextRunAdvances(paint, textArray, index, count, textLength, paint->getFlags(), NULL /* dont need all advances */, &result); -#else - // we double count, since measureText wants a byteLength - SkScalar width = paint->measureText(textArray + index, count << 1); - result = SkScalarToFloat(width); -#endif + env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT); return result; } @@ -380,13 +376,9 @@ public: SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint); jfloat width = 0; -#if RTL_USE_HARFBUZZ TextLayout::getTextRunAdvances(paint, textArray, start, count, textLength, paint->getFlags(), NULL /* dont need all advances */, &width); -#else - width = SkScalarToFloat(paint->measureText(textArray + start, count << 1)); -#endif env->ReleaseStringChars(text, textArray); return width; } @@ -404,12 +396,9 @@ public: SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint); jfloat width = 0; -#if RTL_USE_HARFBUZZ TextLayout::getTextRunAdvances(paint, textArray, 0, textLength, textLength, paint->getFlags(), NULL /* dont need all advances */, &width); -#else - width = SkScalarToFloat(paint->measureText(textArray, textLength << 1)); -#endif + env->ReleaseStringChars(text, textArray); return width; } @@ -434,17 +423,9 @@ public: AutoJavaFloatArray autoWidths(env, widths, count); jfloat* widthsArray = autoWidths.ptr(); -#if RTL_USE_HARFBUZZ TextLayout::getTextRunAdvances(paint, text, 0, count, count, paint->getFlags(), widthsArray, NULL /* dont need totalAdvance */); -#else - SkScalar* scalarArray = (SkScalar*)widthsArray; - count = paint->getTextWidths(text, count << 1, scalarArray); - for (int i = 0; i < count; i++) { - widthsArray[i] = SkScalarToFloat(scalarArray[i]); - } -#endif return count; } @@ -597,54 +578,11 @@ public: static jint doTextRunCursor(JNIEnv *env, SkPaint* paint, const jchar *text, jint start, jint count, jint flags, jint offset, jint opt) { -#if RTL_USE_HARFBUZZ jfloat scalarArray[count]; TextLayout::getTextRunAdvances(paint, text, start, count, count, flags, scalarArray, NULL /* dont need totalAdvance */); -#else - SkScalar scalarArray[count]; - jchar buffer[count]; - - // this is where we'd call harfbuzz - // for now we just use ushape.c and widths returned from skia - - int widths; - if (flags & 0x1) { // rtl, call arabic shaping in case - UErrorCode status = U_ZERO_ERROR; - // Use fixed length since we need to keep start and count valid - u_shapeArabic(text + start, count, buffer, count, - U_SHAPE_LENGTH_FIXED_SPACES_NEAR | U_SHAPE_TEXT_DIRECTION_LOGICAL | - U_SHAPE_LETTERS_SHAPE | U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status); - // we shouldn't fail unless there's an out of memory condition, - // in which case we're hosed anyway - for (int i = 0; i < count; ++i) { - if (buffer[i] == 0xffff) { - buffer[i] = 0x200b; // zero-width-space for skia - } - } - widths = paint->getTextWidths(buffer, count << 1, scalarArray); - } else { - widths = paint->getTextWidths(text + start, count << 1, scalarArray); - } - if (widths < count) { - // Skia operates on code points, not code units, so surrogate pairs return only one - // value. Expand the result so we have one value per UTF-16 code unit. - - // Note, skia's getTextWidth gets confused if it encounters a surrogate pair, - // leaving the remaining widths zero. Not nice. - const jchar *chars = text + start; - for (int i = count, p = widths - 1; --i > p;) { - if (chars[i] >= 0xdc00 && chars[i] < 0xe000 && - chars[i-1] >= 0xd800 && chars[i-1] < 0xdc00) { - scalarArray[i] = 0; - } else { - scalarArray[i] = scalarArray[--p]; - } - } - } -#endif jint pos = offset - start; switch (opt) { case AFTER: diff --git a/core/jni/android/graphics/RtlProperties.h b/core/jni/android/graphics/RtlProperties.h index a41c91b..d43745f 100644 --- a/core/jni/android/graphics/RtlProperties.h +++ b/core/jni/android/graphics/RtlProperties.h @@ -45,9 +45,6 @@ static RtlDebugLevel readRtlDebugLevel() { return kRtlDebugDisabled; } -// Define if we want to use Harfbuzz (1) or not (0) -#define RTL_USE_HARFBUZZ 1 - // Define if we want (1) to have Advances debug values or not (0) #define DEBUG_ADVANCES 0 diff --git a/core/jni/android/graphics/TextLayout.cpp b/core/jni/android/graphics/TextLayout.cpp index e1398e9..bc30ace 100644 --- a/core/jni/android/graphics/TextLayout.cpp +++ b/core/jni/android/graphics/TextLayout.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#define LOG_TAG "TextLayout" + #include "TextLayout.h" #include "TextLayoutCache.h" @@ -46,211 +48,35 @@ bool TextLayout::needsLayout(const jchar* text, jint len, jint bidiFlags) { return false; } -/** - * Character-based Arabic shaping. - * - * We'll use harfbuzz and glyph-based shaping instead once we're set up for it. - * - * @context the text context - * @start the start of the text to render - * @count the length of the text to render, start + count must be <= contextCount - * @contextCount the length of the context - * @shaped where to put the shaped text, must have capacity for count uchars - * @return the length of the shaped text, or -1 if error - */ -int TextLayout::shapeRtlText(const jchar* context, jsize start, jsize count, jsize contextCount, - jchar* shaped, UErrorCode& status) { - SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> tempBuffer(contextCount); - jchar* buffer = tempBuffer.get(); - - // Use fixed length since we need to keep start and count valid - u_shapeArabic(context, contextCount, buffer, contextCount, - U_SHAPE_LENGTH_FIXED_SPACES_NEAR | - U_SHAPE_TEXT_DIRECTION_LOGICAL | U_SHAPE_LETTERS_SHAPE | - U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status); - - if (U_SUCCESS(status)) { - // trim out UNICODE_NOT_A_CHAR following ligatures, if any - int end = 0; - for (int i = start, e = start + count; i < e; ++i) { - if (buffer[i] != UNICODE_NOT_A_CHAR) { - buffer[end++] = buffer[i]; - } - } - count = end; - // ALOG(LOG_INFO, "CSRTL", "start %d count %d ccount %d\n", start, count, contextCount); - ubidi_writeReverse(buffer, count, shaped, count, UBIDI_DO_MIRRORING | UBIDI_OUTPUT_REVERSE - | UBIDI_KEEP_BASE_COMBINING, &status); - if (U_SUCCESS(status)) { - return count; - } - } - return -1; -} - -/** - * Basic character-based layout supporting rtl and arabic shaping. - * Runs bidi on the text and generates a reordered, shaped line in buffer, returning - * the length. - * @text the text - * @len the length of the text in uchars - * @dir receives the resolved paragraph direction - * @buffer the buffer to receive the reordered, shaped line. Must have capacity of - * at least len jchars. - * @flags line bidi flags - * @return the length of the reordered, shaped line, or -1 if error - */ -jint TextLayout::layoutLine(const jchar* text, jint len, jint flags, int& dir, jchar* buffer, - UErrorCode& status) { - static const int RTL_OPTS = UBIDI_DO_MIRRORING | UBIDI_KEEP_BASE_COMBINING | - UBIDI_REMOVE_BIDI_CONTROLS | UBIDI_OUTPUT_REVERSE; - - UBiDiLevel bidiReq = 0; - switch (flags) { - case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level - case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level - case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break; - case kBidi_Default_RTL: bidiReq = UBIDI_DEFAULT_RTL; break; - case kBidi_Force_LTR: memcpy(buffer, text, len * sizeof(jchar)); return len; - case kBidi_Force_RTL: return shapeRtlText(text, 0, len, len, buffer, status); - } - - int32_t result = -1; - - UBiDi* bidi = ubidi_open(); - if (bidi) { - ubidi_setPara(bidi, text, len, bidiReq, NULL, &status); - if (U_SUCCESS(status)) { - dir = ubidi_getParaLevel(bidi) & 0x1; // 0 if ltr, 1 if rtl - - int rc = ubidi_countRuns(bidi, &status); - if (U_SUCCESS(status)) { - // ALOG(LOG_INFO, "LAYOUT", "para bidiReq=%d dir=%d rc=%d\n", bidiReq, dir, rc); - - int32_t slen = 0; - for (int i = 0; i < rc; ++i) { - int32_t start; - int32_t length; - UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &start, &length); - - if (runDir == UBIDI_RTL) { - slen += shapeRtlText(text + start, 0, length, length, buffer + slen, status); - } else { - memcpy(buffer + slen, text + start, length * sizeof(jchar)); - slen += length; - } - } - if (U_SUCCESS(status)) { - result = slen; - } - } - } - ubidi_close(bidi); - } - - return result; -} - -bool TextLayout::prepareText(SkPaint* paint, const jchar* text, jsize len, jint bidiFlags, - const jchar** outText, int32_t* outBytes, jchar** outBuffer) { - const jchar *workText = text; - jchar *buffer = NULL; - int dir = kDirection_LTR; - if (needsLayout(text, len, bidiFlags)) { - buffer =(jchar *) malloc(len * sizeof(jchar)); - if (!buffer) { - return false; - } - UErrorCode status = U_ZERO_ERROR; - len = layoutLine(text, len, bidiFlags, dir, buffer, status); // might change len, dir - if (!U_SUCCESS(status)) { - ALOG(LOG_WARN, "LAYOUT", "drawText error %d\n", status); - free(buffer); - return false; // can't render - } - workText = buffer; // use the shaped text - } - - bool trimLeft = false; - bool trimRight = false; - - SkPaint::Align horiz = paint->getTextAlign(); - switch (horiz) { - case SkPaint::kLeft_Align: trimLeft = dir & kDirection_Mask; break; - case SkPaint::kCenter_Align: trimLeft = trimRight = true; break; - case SkPaint::kRight_Align: trimRight = !(dir & kDirection_Mask); - default: break; - } - const jchar* workLimit = workText + len; - - if (trimLeft) { - while (workText < workLimit && *workText == ' ') { - ++workText; - } - } - if (trimRight) { - while (workLimit > workText && *(workLimit - 1) == ' ') { - --workLimit; - } - } - - *outBytes = (workLimit - workText) << 1; - *outText = workText; - *outBuffer = buffer; - - return true; -} - // Draws or gets the path of a paragraph of text on a single line, running bidi and shaping. // This will draw if canvas is not null, otherwise path must be non-null and it will create // a path representing the text that would have been drawn. void TextLayout::handleText(SkPaint *paint, const jchar* text, jsize len, jint bidiFlags, jfloat x, jfloat y,SkCanvas *canvas, SkPath *path) { - const jchar *workText; - jchar *buffer = NULL; - int32_t workBytes; - if (prepareText(paint, text, len, bidiFlags, &workText, &workBytes, &buffer)) { - SkScalar x_ = SkFloatToScalar(x); - SkScalar y_ = SkFloatToScalar(y); - if (canvas) { - canvas->drawText(workText, workBytes, x_, y_, *paint); - } else { - paint->getTextPath(workText, workBytes, x_, y_, path); - } - free(buffer); - } -} - -bool TextLayout::prepareRtlTextRun(const jchar* context, jsize start, jsize& count, - jsize contextCount, jchar* shaped) { - UErrorCode status = U_ZERO_ERROR; - count = shapeRtlText(context, start, count, contextCount, shaped, status); - if (U_SUCCESS(status)) { - return true; + sp<TextLayoutCacheValue> value; +#if USE_TEXT_LAYOUT_CACHE + // Return advances from the cache. Compute them if needed + value = TextLayoutCache::getInstance().getValue(paint, text, 0, len, + len, bidiFlags); +#else + value = new TextLayoutCacheValue(len); + TextLayoutEngine::getInstance().computeValues(value.get(), paint, + reinterpret_cast<const UChar*>(text), 0, len, len, bidiFlags); +#endif + if (value == NULL) { + LOGE("Cannot get TextLayoutCache value for text = '%s'", + String8(text, len).string()); + return ; + } + SkScalar x_ = SkFloatToScalar(x); + SkScalar y_ = SkFloatToScalar(y); + if (canvas) { + canvas->drawText(value->getGlyphs(), value->getGlyphsCount() * 2, x_, y_, *paint); } else { - LOGW("drawTextRun error %d\n", status); + paint->getTextPath(value->getGlyphs(), value->getGlyphsCount() * 2, x_, y_, path); } - return false; } -void TextLayout::drawTextRun(SkPaint* paint, const jchar* chars, - jint start, jint count, jint contextCount, - int dirFlags, jfloat x, jfloat y, SkCanvas* canvas) { - - SkScalar x_ = SkFloatToScalar(x); - SkScalar y_ = SkFloatToScalar(y); - - uint8_t rtl = dirFlags & 0x1; - if (rtl) { - SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> buffer(contextCount); - if (prepareRtlTextRun(chars, start, count, contextCount, buffer.get())) { - canvas->drawText(buffer.get(), count << 1, x_, y_, *paint); - } - } else { - canvas->drawText(chars + start, count << 1, x_, y_, *paint); - } - } - void TextLayout::getTextRunAdvances(SkPaint* paint, const jchar* chars, jint start, jint count, jint contextCount, jint dirFlags, jfloat* resultAdvances, jfloat* resultTotalAdvance) { @@ -260,16 +86,20 @@ void TextLayout::getTextRunAdvances(SkPaint* paint, const jchar* chars, jint sta value = TextLayoutCache::getInstance().getValue(paint, chars, start, count, contextCount, dirFlags); #else - value = new TextLayoutCacheValue(); - value->computeValues(paint, chars, start, count, contextCount, dirFlags); + value = new TextLayoutCacheValue(contextCount); + TextLayoutEngine::getInstance().computeValues(value.get(), paint, + reinterpret_cast<const UChar*>(chars), start, count, contextCount, dirFlags); #endif - if (value != NULL) { - if (resultAdvances) { - memcpy(resultAdvances, value->getAdvances(), value->getAdvancesCount() * sizeof(jfloat)); - } - if (resultTotalAdvance) { - *resultTotalAdvance = value->getTotalAdvance(); - } + if (value == NULL) { + LOGE("Cannot get TextLayoutCache value for text = '%s'", + String8(chars + start, count).string()); + return ; + } + if (resultAdvances) { + memcpy(resultAdvances, value->getAdvances(), value->getAdvancesCount() * sizeof(jfloat)); + } + if (resultTotalAdvance) { + *resultTotalAdvance = value->getTotalAdvance(); } } @@ -281,12 +111,6 @@ void TextLayout::getTextRunAdvancesICU(SkPaint* paint, const jchar* chars, jint resultAdvances, &resultTotalAdvance); } -// Draws a paragraph of text on a single line, running bidi and shaping -void TextLayout::drawText(SkPaint* paint, const jchar* text, jsize len, - int bidiFlags, jfloat x, jfloat y, SkCanvas* canvas) { - handleText(paint, text, len, bidiFlags, x, y, canvas, NULL); -} - void TextLayout::getTextPath(SkPaint *paint, const jchar *text, jsize len, jint bidiFlags, jfloat x, jfloat y, SkPath *path) { handleText(paint, text, len, bidiFlags, x, y, NULL, path); @@ -305,14 +129,30 @@ void TextLayout::drawTextOnPath(SkPaint* paint, const jchar* text, int count, return; } - SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> buffer(count); - - int dir = kDirection_LTR; - UErrorCode status = U_ZERO_ERROR; - count = layoutLine(text, count, bidiFlags, dir, buffer.get(), status); - if (U_SUCCESS(status)) { - canvas->drawTextOnPathHV(buffer.get(), count << 1, *path, h_, v_, *paint); + sp<TextLayoutCacheValue> value; +#if USE_TEXT_LAYOUT_CACHE + value = TextLayoutCache::getInstance().getValue(paint, text, 0, count, + count, bidiFlags); +#else + value = new TextLayoutCacheValue(count); + TextLayoutEngine::getInstance().computeValues(value.get(), paint, + reinterpret_cast<const UChar*>(text), 0, count, count, bidiFlags); +#endif + if (value == NULL) { + LOGE("Cannot get TextLayoutCache value for text = '%s'", + String8(text, count).string()); + return ; } + + // Save old text encoding + SkPaint::TextEncoding oldEncoding = paint->getTextEncoding(); + // Define Glyph encoding + paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); + + canvas->drawTextOnPathHV(value->getGlyphs(), value->getGlyphsCount() * 2, *path, h_, v_, *paint); + + // Get back old encoding + paint->setTextEncoding(oldEncoding); } void TextLayout::computeAdvancesWithICU(SkPaint* paint, const UChar* chars, diff --git a/core/jni/android/graphics/TextLayout.h b/core/jni/android/graphics/TextLayout.h index 9df3829..1dabe32 100644 --- a/core/jni/android/graphics/TextLayout.h +++ b/core/jni/android/graphics/TextLayout.h @@ -62,13 +62,6 @@ enum { class TextLayout { public: - /* - * Draws a unidirectional run of text. - */ - static void drawTextRun(SkPaint* paint, const jchar* chars, - jint start, jint count, jint contextCount, - int dirFlags, jfloat x, jfloat y, SkCanvas* canvas); - static void getTextRunAdvances(SkPaint* paint, const jchar* chars, jint start, jint count, jint contextCount, jint dirFlags, jfloat* resultAdvances, jfloat* resultTotalAdvance); @@ -77,29 +70,16 @@ public: jint count, jint contextCount, jint dirFlags, jfloat* resultAdvances, jfloat& resultTotalAdvance); - static void drawText(SkPaint* paint, const jchar* text, jsize len, - jint bidiFlags, jfloat x, jfloat y, SkCanvas* canvas); - static void getTextPath(SkPaint* paint, const jchar* text, jsize len, jint bidiFlags, jfloat x, jfloat y, SkPath* path); static void drawTextOnPath(SkPaint* paint, const jchar* text, jsize len, int bidiFlags, jfloat hOffset, jfloat vOffset, SkPath* path, SkCanvas* canvas); - - static bool prepareText(SkPaint* paint, const jchar* text, jsize len, jint bidiFlags, - const jchar** outText, int32_t* outBytes, jchar** outBuffer); - - static bool prepareRtlTextRun(const jchar* context, jsize start, jsize& count, - jsize contextCount, jchar* shaped); - private: static bool needsLayout(const jchar* text, jint len, jint bidiFlags); - static int shapeRtlText(const jchar* context, jsize start, jsize count, jsize contextCount, - jchar* shaped, UErrorCode& status); - static jint layoutLine(const jchar* text, jint len, jint flags, int &dir, jchar* buffer, - UErrorCode &status); + static void handleText(SkPaint* paint, const jchar* text, jsize len, int bidiFlags, jfloat x, jfloat y, SkCanvas* canvas, SkPath* path); diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp index f1c3101..81bf4d5 100644 --- a/core/jni/android/graphics/TextLayoutCache.cpp +++ b/core/jni/android/graphics/TextLayoutCache.cpp @@ -34,10 +34,11 @@ namespace android { #if USE_TEXT_LAYOUT_CACHE ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutCache); - ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutEngine); #endif + ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutEngine); + //-------------------------------------------------------------------------------------------------- TextLayoutCache::TextLayoutCache() : @@ -549,8 +550,8 @@ void TextLayoutEngine::computeRunValues(SkPaint* paint, const UChar* chars, LOGD("Shaping Script Run with"); LOGD(" -- isRTL = %d", isRTL); LOGD(" -- HB script = %d", mShaperItem.item.script); - LOGD(" -- startFontRun = %d", startScriptRun); - LOGD(" -- endFontRun = %d", endScriptRun); + LOGD(" -- startFontRun = %d", int(startScriptRun)); + LOGD(" -- endFontRun = %d", int(endScriptRun)); LOGD(" -- countFontRun = %d", countScriptRun); LOGD(" -- run = '%s'", String8(chars + startScriptRun, countScriptRun).string()); LOGD(" -- string = '%s'", String8(chars, count).string()); @@ -572,12 +573,12 @@ void TextLayoutEngine::computeRunValues(SkPaint* paint, const UChar* chars, if (isRTL) { endScriptRun = startScriptRun; #if DEBUG_GLYPHS - LOGD("Updated endScriptRun = %d", endScriptRun); + LOGD("Updated endScriptRun = %d", int(endScriptRun)); #endif } else { startScriptRun = endScriptRun; #if DEBUG_GLYPHS - LOGD("Updated startScriptRun = %d", startScriptRun); + LOGD("Updated startScriptRun = %d", int(startScriptRun)); #endif } diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index 9c7b108..5860658 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -44,7 +44,7 @@ #include <SkiaColorFilter.h> #include <Rect.h> -#include <TextLayout.h> +#include <TextLayoutCache.h> namespace android { @@ -487,66 +487,45 @@ static void android_view_GLES20Canvas_setupShadow(JNIEnv* env, jobject clazz, static void renderText(OpenGLRenderer* renderer, const jchar* text, int count, jfloat x, jfloat y, int flags, SkPaint* paint) { -#if RTL_USE_HARFBUZZ sp<TextLayoutCacheValue> value; #if USE_TEXT_LAYOUT_CACHE value = TextLayoutCache::getInstance().getValue(paint, text, 0, count, count, flags); if (value == NULL) { - LOGE("Cannot get TextLayoutCache value"); - return ; + LOGE("Cannot get TextLayoutCache value for text = '%s'", + String8(text, count).string()); + return; } #else - value = new TextLayoutCacheValue(); - value->computeValues(paint, text, 0, count, count, flags); + value = new TextLayoutCacheValue(count); + TextLayoutEngine::getInstance().computeValues(value.get(), paint, + text, 0, count, count, flags); #endif const jchar* glyphs = value->getGlyphs(); size_t glyphsCount = value->getGlyphsCount(); int bytesCount = glyphsCount * sizeof(jchar); renderer->drawText((const char*) glyphs, bytesCount, glyphsCount, x, y, paint); -#else - const jchar *workText; - jchar* buffer = NULL; - int32_t workBytes; - if (TextLayout::prepareText(paint, text, count, flags, &workText, &workBytes, &buffer)) { - renderer->drawText((const char*) workText, workBytes, count, x, y, paint); - free(buffer); - } -#endif } static void renderTextRun(OpenGLRenderer* renderer, const jchar* text, jint start, jint count, jint contextCount, jfloat x, jfloat y, int flags, SkPaint* paint) { -#if RTL_USE_HARFBUZZ sp<TextLayoutCacheValue> value; #if USE_TEXT_LAYOUT_CACHE value = TextLayoutCache::getInstance().getValue(paint, text, start, count, contextCount, flags); if (value == NULL) { - LOGE("Cannot get TextLayoutCache value"); - return ; + LOGE("Cannot get TextLayoutCache value for text = '%s'", + String8(text + start, count).string()); + return; } #else - value = new TextLayoutCacheValue(); - value->computeValues(paint, text, start, count, contextCount, flags); + value = new TextLayoutCacheValue(count); + TextLayoutEngine::getInstance().computeValues(value.get(), paint, + text, start, count, contextCount, flags); #endif const jchar* glyphs = value->getGlyphs(); size_t glyphsCount = value->getGlyphsCount(); int bytesCount = glyphsCount * sizeof(jchar); renderer->drawText((const char*) glyphs, bytesCount, glyphsCount, x, y, paint); -#else - uint8_t rtl = flags & 0x1; - if (rtl) { - SkAutoSTMalloc<80, jchar> buffer(contextCount); - jchar* shaped = buffer.get(); - if (TextLayout::prepareRtlTextRun(text, start, count, contextCount, shaped)) { - renderer->drawText((const char*) shaped, count << 1, count, x, y, paint); - } else { - LOGW("drawTextRun error"); - } - } else { - renderer->drawText((const char*) (text + start), count << 1, count, x, y, paint); - } -#endif } static void android_view_GLES20Canvas_drawTextArray(JNIEnv* env, jobject clazz, diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 6f115b3..80aef21 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1992,11 +1992,11 @@ <string name="save_password_label">Confirm</string> <!-- Toast for double-tap --> - <string name="double_tap_toast">Tip: Double-touch to zoom in and out.</string> + <string name="double_tap_toast">Tip: Double-tap to zoom in and out.</string> <!-- Text to show in the auto complete drop down list on a text view when the WebView can auto fill the entire form, and the user has configured an AutoFill profile [CHAR-LIMIT=8] --> <string name="autofill_this_form">Autofill</string> - <!-- Text to show in the auto complete drop down list on a text view when the WebView can auto fill the entire form but the user has not configured an AutoFill profile [CHAR-LIMIT=16] --> + <!-- Text to show in the auto complete drop down list on a text view when the WebView can auto fill the entire form but the user has not configured an AutoFill profile [CHAR-LIMIT=19] --> <string name="setup_autofill">Set up Autofill</string> <!-- String used to separate FirstName and LastName when writing out a local name diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index b18d88f..fe5388b 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -810,7 +810,7 @@ please see themes_device_defaults.xml. <!-- Special theme for the recent apps dialog, to allow customization with overlays. --> - <style name="Theme.Dialog.RecentApplications"> + <style name="Theme.Dialog.RecentApplications" parent="Theme.DeviceDefault.Dialog"> <item name="windowFrame">@null</item> <item name="windowBackground">@android:color/transparent</item> <item name="android:windowAnimationStyle">@android:style/Animation.RecentApplications</item> diff --git a/include/utils/GenerationCache.h b/include/utils/GenerationCache.h index 83cda86..da85a9a 100644 --- a/include/utils/GenerationCache.h +++ b/include/utils/GenerationCache.h @@ -88,11 +88,13 @@ private: void attachToCache(const sp<Entry<K, V> >& entry); void detachFromCache(const sp<Entry<K, V> >& entry); + + const V mNullValue; }; // class GenerationCache template<typename K, typename V> GenerationCache<K, V>::GenerationCache(uint32_t maxCapacity): mMaxCapacity(maxCapacity), - mListener(NULL) { + mListener(NULL), mNullValue(NULL) { }; template<typename K, typename V> @@ -154,7 +156,7 @@ const V& GenerationCache<K, V>::get(const K& key) { return entry->value; } - return NULL; + return mNullValue; } template<typename K, typename V> diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp index e72adc4..a3f2bf6 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp @@ -38,7 +38,8 @@ NuPlayer::RTSPSource::RTSPSource( mFlags(0), mState(DISCONNECTED), mFinalResult(OK), - mDisconnectReplyID(0) { + mDisconnectReplyID(0), + mSeekGeneration(0) { if (headers) { mExtraHeaders = *headers; @@ -146,14 +147,21 @@ status_t NuPlayer::RTSPSource::getDuration(int64_t *durationUs) { } status_t NuPlayer::RTSPSource::seekTo(int64_t seekTimeUs) { + sp<AMessage> msg = new AMessage(kWhatPerformSeek, mReflector->id()); + msg->setInt32("generation", ++mSeekGeneration); + msg->setInt64("timeUs", seekTimeUs); + msg->post(200000ll); + + return OK; +} + +void NuPlayer::RTSPSource::performSeek(int64_t seekTimeUs) { if (mState != CONNECTED) { - return UNKNOWN_ERROR; + return; } mState = SEEKING; mHandler->seek(seekTimeUs); - - return OK; } bool NuPlayer::RTSPSource::isSeekable() { @@ -168,6 +176,20 @@ void NuPlayer::RTSPSource::onMessageReceived(const sp<AMessage> &msg) { mDisconnectReplyID = replyID; finishDisconnectIfPossible(); return; + } else if (msg->what() == kWhatPerformSeek) { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + + if (generation != mSeekGeneration) { + // obsolete. + return; + } + + int64_t seekTimeUs; + CHECK(msg->findInt64("timeUs", &seekTimeUs)); + + performSeek(seekTimeUs); + return; } CHECK_EQ(msg->what(), (int)kWhatNotify); @@ -208,21 +230,32 @@ void NuPlayer::RTSPSource::onMessageReceived(const sp<AMessage> &msg) { break; } - const TrackInfo &info = mTracks.editItemAt(trackIndex); - sp<AnotherPacketSource> source = info.mSource; + TrackInfo *info = &mTracks.editItemAt(trackIndex); + + sp<AnotherPacketSource> source = info->mSource; if (source != NULL) { -#if 1 uint32_t rtpTime; CHECK(accessUnit->meta()->findInt32("rtp-time", (int32_t *)&rtpTime)); + if (!info->mNPTMappingValid) { + // This is a live stream, we didn't receive any normal + // playtime mapping. Assume the first packets correspond + // to time 0. + + LOGV("This is a live stream, assuming time = 0"); + + info->mRTPTime = rtpTime; + info->mNormalPlaytimeUs = 0ll; + info->mNPTMappingValid = true; + } + int64_t nptUs = - ((double)rtpTime - (double)info.mRTPTime) - / info.mTimeScale + ((double)rtpTime - (double)info->mRTPTime) + / info->mTimeScale * 1000000ll - + info.mNormalPlaytimeUs; + + info->mNormalPlaytimeUs; accessUnit->meta()->setInt64("timeUs", nptUs); -#endif source->queueAccessUnit(accessUnit); } @@ -278,6 +311,7 @@ void NuPlayer::RTSPSource::onMessageReceived(const sp<AMessage> &msg) { TrackInfo *info = &mTracks.editItemAt(trackIndex); info->mRTPTime = rtpTime; info->mNormalPlaytimeUs = nptUs; + info->mNPTMappingValid = true; break; } @@ -305,6 +339,7 @@ void NuPlayer::RTSPSource::onConnected() { info.mTimeScale = timeScale; info.mRTPTime = 0; info.mNormalPlaytimeUs = 0ll; + info.mNPTMappingValid = false; if ((isAudio && mAudioTrack == NULL) || (isVideo && mVideoTrack == NULL)) { diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.h b/media/libmediaplayerservice/nuplayer/RTSPSource.h index 66eab72..59d06ad 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.h +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.h @@ -56,6 +56,7 @@ private: enum { kWhatNotify = 'noti', kWhatDisconnect = 'disc', + kWhatPerformSeek = 'seek', }; enum State { @@ -76,6 +77,7 @@ private: int32_t mTimeScale; uint32_t mRTPTime; int64_t mNormalPlaytimeUs; + bool mNPTMappingValid; }; AString mURL; @@ -95,12 +97,16 @@ private: sp<AnotherPacketSource> mAudioTrack; sp<AnotherPacketSource> mVideoTrack; + int32_t mSeekGeneration; + sp<AnotherPacketSource> getSource(bool audio); void onConnected(); void onDisconnected(const sp<AMessage> &msg); void finishDisconnectIfPossible(); + void performSeek(int64_t seekTimeUs); + DISALLOW_EVIL_CONSTRUCTORS(RTSPSource); }; diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 964cb1f..8405264 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -2091,7 +2091,7 @@ status_t AwesomePlayer::finishSetDataSource_l() { String8 mimeType; float confidence; sp<AMessage> dummy; - bool success = SniffDRM(dataSource, &mimeType, &confidence, &dummy); + bool success = SniffWVM(dataSource, &mimeType, &confidence, &dummy); if (!success || strcasecmp( @@ -2099,6 +2099,8 @@ status_t AwesomePlayer::finishSetDataSource_l() { return ERROR_UNSUPPORTED; } + dataSource->DrmInitialization(); + mWVMExtractor = new WVMExtractor(dataSource); mWVMExtractor->setAdaptiveStreamingMode(true); extractor = mWVMExtractor; diff --git a/media/libstagefright/DRMExtractor.cpp b/media/libstagefright/DRMExtractor.cpp index 1f3d581..afc4a80 100644 --- a/media/libstagefright/DRMExtractor.cpp +++ b/media/libstagefright/DRMExtractor.cpp @@ -282,13 +282,13 @@ bool SniffDRM( if (decryptHandle != NULL) { if (decryptHandle->decryptApiType == DecryptApiType::CONTAINER_BASED) { *mimeType = String8("drm+container_based+") + decryptHandle->mimeType; + *confidence = 10.0f; } else if (decryptHandle->decryptApiType == DecryptApiType::ELEMENTARY_STREAM_BASED) { *mimeType = String8("drm+es_based+") + decryptHandle->mimeType; - } else if (decryptHandle->decryptApiType == DecryptApiType::WV_BASED) { - *mimeType = MEDIA_MIMETYPE_CONTAINER_WVM; - LOGW("SniffWVM: found match\n"); + *confidence = 10.0f; + } else { + return false; } - *confidence = 10.0f; return true; } diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp index e471f73..d0a7880 100644 --- a/media/libstagefright/DataSource.cpp +++ b/media/libstagefright/DataSource.cpp @@ -32,6 +32,7 @@ #include "include/DRMExtractor.h" #include "include/FLACExtractor.h" #include "include/AACExtractor.h" +#include "include/WVMExtractor.h" #include "matroska/MatroskaExtractor.h" @@ -120,6 +121,7 @@ void DataSource::RegisterDefaultSniffers() { RegisterSniffer(SniffAAC); RegisterSniffer(SniffAVI); RegisterSniffer(SniffMPEG2PS); + RegisterSniffer(SniffWVM); char value[PROPERTY_VALUE_MAX]; if (property_get("drm.service.enabled", value, NULL) diff --git a/media/libstagefright/WVMExtractor.cpp b/media/libstagefright/WVMExtractor.cpp index 26eda0c..79dedca 100644 --- a/media/libstagefright/WVMExtractor.cpp +++ b/media/libstagefright/WVMExtractor.cpp @@ -45,17 +45,12 @@ namespace android { static Mutex gWVMutex; WVMExtractor::WVMExtractor(const sp<DataSource> &source) - : mDataSource(source) { - { - Mutex::Autolock autoLock(gWVMutex); - if (gVendorLibHandle == NULL) { - gVendorLibHandle = dlopen("libwvm.so", RTLD_NOW); - } + : mDataSource(source) +{ + Mutex::Autolock autoLock(gWVMutex); - if (gVendorLibHandle == NULL) { - LOGE("Failed to open libwvm.so"); - return; - } + if (!getVendorLibHandle()) { + return; } typedef WVMLoadableExtractor *(*GetInstanceFunc)(sp<DataSource>); @@ -71,6 +66,19 @@ WVMExtractor::WVMExtractor(const sp<DataSource> &source) } } +bool WVMExtractor::getVendorLibHandle() +{ + if (gVendorLibHandle == NULL) { + gVendorLibHandle = dlopen("libwvm.so", RTLD_NOW); + } + + if (gVendorLibHandle == NULL) { + LOGE("Failed to open libwvm.so"); + } + + return gVendorLibHandle != NULL; +} + WVMExtractor::~WVMExtractor() { } @@ -113,5 +121,33 @@ void WVMExtractor::setAdaptiveStreamingMode(bool adaptive) { } } +bool SniffWVM( + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *) { + + Mutex::Autolock autoLock(gWVMutex); + + if (!WVMExtractor::getVendorLibHandle()) { + return false; + } + + typedef WVMLoadableExtractor *(*SnifferFunc)(sp<DataSource>); + SnifferFunc snifferFunc = + (SnifferFunc) dlsym(gVendorLibHandle, + "_ZN7android15IsWidevineMediaENS_2spINS_10DataSourceEEE"); + + if (snifferFunc) { + if ((*snifferFunc)(source)) { + *mimeType = MEDIA_MIMETYPE_CONTAINER_WVM; + *confidence = 10.0f; + return true; + } + } else { + LOGE("IsWidevineMedia not found in libwvm.so"); + } + + return false; +} + } //namespace android diff --git a/media/libstagefright/foundation/ABitReader.cpp b/media/libstagefright/foundation/ABitReader.cpp index f07dd4f..5499c32 100644 --- a/media/libstagefright/foundation/ABitReader.cpp +++ b/media/libstagefright/foundation/ABitReader.cpp @@ -79,7 +79,13 @@ void ABitReader::skipBits(size_t n) { } void ABitReader::putBits(uint32_t x, size_t n) { - CHECK_LE(mNumBitsLeft + n, 32u); + CHECK_LE(n, 32u); + + while (mNumBitsLeft + n > 32) { + mNumBitsLeft -= 8; + --mData; + ++mSize; + } mReservoir = (mReservoir >> n) | (x << (32 - n)); mNumBitsLeft += n; diff --git a/media/libstagefright/include/WVMExtractor.h b/media/libstagefright/include/WVMExtractor.h index deecd25..9f763f9 100644 --- a/media/libstagefright/include/WVMExtractor.h +++ b/media/libstagefright/include/WVMExtractor.h @@ -23,6 +23,8 @@ namespace android { +struct AMessage; +class String8; class DataSource; class WVMLoadableExtractor : public MediaExtractor { @@ -58,6 +60,8 @@ public: // is used. void setAdaptiveStreamingMode(bool adaptive); + static bool getVendorLibHandle(); + protected: virtual ~WVMExtractor(); @@ -69,6 +73,10 @@ private: WVMExtractor &operator=(const WVMExtractor &); }; +bool SniffWVM( + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *); + } // namespace android #endif // DRM_EXTRACTOR_H_ diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index dd049c2..21ef298 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -1100,6 +1100,8 @@ struct MyHandler : public AHandler { float npt1, npt2; if (!ASessionDescription::parseNTPRange(val.c_str(), &npt1, &npt2)) { // This is a live stream and therefore not seekable. + + LOGI("This is a live stream"); return; } @@ -1386,12 +1388,14 @@ private: msg->setInt32("what", kWhatConnected); msg->post(); - for (size_t i = 0; i < mTracks.size(); ++i) { - TrackInfo *info = &mTracks.editItemAt(i); + if (mSeekable) { + for (size_t i = 0; i < mTracks.size(); ++i) { + TrackInfo *info = &mTracks.editItemAt(i); - postNormalPlayTimeMapping( - i, - info->mNormalPlayTimeRTP, info->mNormalPlayTimeUs); + postNormalPlayTimeMapping( + i, + info->mNormalPlayTimeRTP, info->mNormalPlayTimeUs); + } } mFirstAccessUnit = false; diff --git a/nfc-extras/tests/src/com/android/nfc_extras/BasicNfcEeTest.java b/nfc-extras/tests/src/com/android/nfc_extras/tests/BasicNfcEeTest.java index e1025aa..389dfbe 100644 --- a/nfc-extras/tests/src/com/android/nfc_extras/BasicNfcEeTest.java +++ b/nfc-extras/tests/src/com/android/nfc_extras/tests/BasicNfcEeTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.nfc_extras; +package com.android.nfc_extras.tests; import android.content.Context; import android.nfc.NfcAdapter; @@ -48,7 +48,7 @@ public class BasicNfcEeTest extends InstrumentationTestCase { @Override protected void setUp() throws Exception { super.setUp(); - mContext = getInstrumentation().getContext(); + mContext = getInstrumentation().getTargetContext(); mAdapterExtras = NfcAdapterExtras.get(NfcAdapter.getDefaultAdapter(mContext)); mEe = mAdapterExtras.getEmbeddedExecutionEnvironment(); } diff --git a/opengl/libs/ETC1/etc1.cpp b/opengl/libs/ETC1/etc1.cpp index 5ed2c3c..97d1085 100644 --- a/opengl/libs/ETC1/etc1.cpp +++ b/opengl/libs/ETC1/etc1.cpp @@ -149,13 +149,13 @@ inline int divideBy255(int d) { static inline int convert8To4(int b) { int c = b & 0xff; - return divideBy255(b * 15); + return divideBy255(c * 15); } static inline int convert8To5(int b) { int c = b & 0xff; - return divideBy255(b * 31); + return divideBy255(c * 31); } static diff --git a/policy/src/com/android/internal/policy/impl/IconUtilities.java b/policy/src/com/android/internal/policy/impl/IconUtilities.java index 4564f90..e997355 100644 --- a/policy/src/com/android/internal/policy/impl/IconUtilities.java +++ b/policy/src/com/android/internal/policy/impl/IconUtilities.java @@ -38,6 +38,8 @@ import android.text.StaticLayout; import android.text.TextPaint; import android.util.DisplayMetrics; import android.util.Log; +import android.util.TypedValue; +import android.view.ContextThemeWrapper; import android.content.res.Resources; import android.content.Context; @@ -74,9 +76,13 @@ final class IconUtilities { mIconTextureWidth = mIconTextureHeight = mIconWidth + (int)(blurPx*2); mBlurPaint.setMaskFilter(new BlurMaskFilter(blurPx, BlurMaskFilter.Blur.NORMAL)); - mGlowColorPressedPaint.setColor(0xffffc300); + + TypedValue value = new TypedValue(); + mGlowColorPressedPaint.setColor(context.getTheme().resolveAttribute( + android.R.attr.colorPressedHighlight, value, true) ? value.data : 0xffffc300); mGlowColorPressedPaint.setMaskFilter(TableMaskFilter.CreateClipTable(0, 30)); - mGlowColorFocusedPaint.setColor(0xffff8e00); + mGlowColorFocusedPaint.setColor(context.getTheme().resolveAttribute( + android.R.attr.colorFocusedHighlight, value, true) ? value.data : 0xffff8e00); mGlowColorFocusedPaint.setMaskFilter(TableMaskFilter.CreateClipTable(0, 30)); ColorMatrix cm = new ColorMatrix(); diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java index 67a6855..088146b 100644 --- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java +++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java @@ -52,6 +52,7 @@ import android.os.Handler; import android.os.Message; import android.os.IBinder; import android.os.Parcelable; +import android.os.PowerManager; import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; @@ -101,8 +102,8 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler private View mLockScreen; private View mUnlockScreen; - private volatile boolean mScreenOn = false; - private volatile boolean mWindowFocused = false; + private boolean mScreenOn; + private boolean mWindowFocused = false; private boolean mEnableFallback = false; // assume no fallback UI until we know better private boolean mShowLockBeforeUnlock = false; @@ -311,6 +312,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler mWindowController = controller; mHasOverlay = false; mPluggedIn = mUpdateMonitor.isDevicePluggedIn(); + mScreenOn = ((PowerManager)context.getSystemService(Context.POWER_SERVICE)).isScreenOn(); mUpdateMonitor.registerInfoCallback(this); diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index af86ae9..535f039 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -1816,22 +1816,42 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { @Override public boolean dispatchKeyShortcutEvent(KeyEvent ev) { - // Perform the shortcut (mPreparedPanel can be null since - // global shortcuts (such as search) don't rely on a - // prepared panel or menu). - boolean handled = performPanelShortcut(mPreparedPanel, ev.getKeyCode(), ev, - Menu.FLAG_PERFORM_NO_CLOSE); - if (handled) { - if (mPreparedPanel != null) { - mPreparedPanel.isHandled = true; + // If the panel is already prepared, then perform the shortcut using it. + boolean handled; + if (mPreparedPanel != null) { + handled = performPanelShortcut(mPreparedPanel, ev.getKeyCode(), ev, + Menu.FLAG_PERFORM_NO_CLOSE); + if (handled) { + if (mPreparedPanel != null) { + mPreparedPanel.isHandled = true; + } + return true; } - return true; } // Shortcut not handled by the panel. Dispatch to the view hierarchy. final Callback cb = getCallback(); - return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchKeyShortcutEvent(ev) - : super.dispatchKeyShortcutEvent(ev); + handled = cb != null && !isDestroyed() && mFeatureId < 0 + ? cb.dispatchKeyShortcutEvent(ev) : super.dispatchKeyShortcutEvent(ev); + if (handled) { + return true; + } + + // If the panel is not prepared, then we may be trying to handle a shortcut key + // combination such as Control+C. Temporarily prepare the panel then mark it + // unprepared again when finished to ensure that the panel will again be prepared + // the next time it is shown for real. + if (mPreparedPanel == null) { + PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true); + preparePanel(st, ev); + handled = performPanelShortcut(st, ev.getKeyCode(), ev, + Menu.FLAG_PERFORM_NO_CLOSE); + st.isPrepared = false; + if (handled) { + return true; + } + } + return false; } @Override diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 92260d6..0a5a765 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -301,9 +301,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { GlobalActions mGlobalActions; volatile boolean mPowerKeyHandled; // accessed from input reader and handler thread boolean mPendingPowerKeyUpCanceled; - RecentApplicationsDialog mRecentAppsDialog; Handler mHandler; + static final int RECENT_APPS_BEHAVIOR_SHOW_OR_DISMISS = 0; + static final int RECENT_APPS_BEHAVIOR_EXIT_TOUCH_MODE_AND_SHOW = 1; + static final int RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH = 2; + + RecentApplicationsDialog mRecentAppsDialog; + int mRecentAppsDialogHeldModifiers; + private static final int LID_ABSENT = -1; private static final int LID_CLOSED = 0; private static final int LID_OPEN = 1; @@ -715,7 +721,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_DIALOG) { - showOrHideRecentAppsDialog(0, true /*dismissIfShown*/); + showOrHideRecentAppsDialog(RECENT_APPS_BEHAVIOR_SHOW_OR_DISMISS); } else if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_SYSTEM_UI) { try { mStatusBarService.toggleRecentApps(); @@ -726,10 +732,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { } /** - * Create (if necessary) and launch the recent apps dialog, or hide it if it is - * already shown. + * Create (if necessary) and show or dismiss the recent apps dialog according + * according to the requested behavior. */ - void showOrHideRecentAppsDialog(final int heldModifiers, final boolean dismissIfShown) { + void showOrHideRecentAppsDialog(final int behavior) { mHandler.post(new Runnable() { @Override public void run() { @@ -737,12 +743,33 @@ public class PhoneWindowManager implements WindowManagerPolicy { mRecentAppsDialog = new RecentApplicationsDialog(mContext); } if (mRecentAppsDialog.isShowing()) { - if (dismissIfShown) { - mRecentAppsDialog.dismiss(); + switch (behavior) { + case RECENT_APPS_BEHAVIOR_SHOW_OR_DISMISS: + mRecentAppsDialog.dismiss(); + break; + case RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH: + mRecentAppsDialog.dismissAndSwitch(); + break; + case RECENT_APPS_BEHAVIOR_EXIT_TOUCH_MODE_AND_SHOW: + default: + break; } } else { - mRecentAppsDialog.setHeldModifiers(heldModifiers); - mRecentAppsDialog.show(); + switch (behavior) { + case RECENT_APPS_BEHAVIOR_SHOW_OR_DISMISS: + mRecentAppsDialog.show(); + break; + case RECENT_APPS_BEHAVIOR_EXIT_TOUCH_MODE_AND_SHOW: + try { + mWindowManager.setInTouchMode(false); + } catch (RemoteException e) { + } + mRecentAppsDialog.show(); + break; + case RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH: + default: + break; + } } } }); @@ -1649,7 +1676,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { return 0; } else if (keyCode == KeyEvent.KEYCODE_APP_SWITCH) { if (down && repeatCount == 0) { - showOrHideRecentAppsDialog(0, true /*dismissIfShown*/); + showOrHideRecentAppsDialog(RECENT_APPS_BEHAVIOR_SHOW_OR_DISMISS); } return -1; } @@ -1685,6 +1712,26 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + // Invoke shortcuts using Meta. + if (down && repeatCount == 0 + && (metaState & KeyEvent.META_META_ON) != 0) { + final KeyCharacterMap kcm = event.getKeyCharacterMap(); + Intent shortcutIntent = mShortcutManager.getIntent(kcm, keyCode, + metaState & ~(KeyEvent.META_META_ON + | KeyEvent.META_META_LEFT_ON | KeyEvent.META_META_RIGHT_ON)); + if (shortcutIntent != null) { + shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + try { + mContext.startActivity(shortcutIntent); + } catch (ActivityNotFoundException ex) { + Slog.w(TAG, "Dropping shortcut key combination because " + + "the activity to which it is registered was not found: " + + "META+" + KeyEvent.keyCodeToString(keyCode), ex); + } + return -1; + } + } + // Handle application launch keys. if (down && repeatCount == 0) { String category = sApplicationLaunchKeyCategories.get(keyCode); @@ -1698,9 +1745,29 @@ public class PhoneWindowManager implements WindowManagerPolicy { + "the activity to which it is registered was not found: " + "keyCode=" + keyCode + ", category=" + category, ex); } + return -1; } } + // Display task switcher for ALT-TAB or Meta-TAB. + if (down && repeatCount == 0 && keyCode == KeyEvent.KEYCODE_TAB) { + if (mRecentAppsDialogHeldModifiers == 0) { + final int shiftlessModifiers = event.getModifiers() & ~KeyEvent.META_SHIFT_MASK; + if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON) + || KeyEvent.metaStateHasModifiers( + shiftlessModifiers, KeyEvent.META_META_ON)) { + mRecentAppsDialogHeldModifiers = shiftlessModifiers; + showOrHideRecentAppsDialog(RECENT_APPS_BEHAVIOR_EXIT_TOUCH_MODE_AND_SHOW); + return -1; + } + } + } else if (!down && mRecentAppsDialogHeldModifiers != 0 + && (metaState & mRecentAppsDialogHeldModifiers) == 0) { + mRecentAppsDialogHeldModifiers = 0; + showOrHideRecentAppsDialog(RECENT_APPS_BEHAVIOR_DISMISS_AND_SWITCH); + } + + // Let the application handle the key. return 0; } @@ -1722,39 +1789,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { final KeyCharacterMap kcm = event.getKeyCharacterMap(); final int keyCode = event.getKeyCode(); final int metaState = event.getMetaState(); - final boolean initialDown = event.getAction() == KeyEvent.ACTION_DOWN - && event.getRepeatCount() == 0; - - if (initialDown) { - // Invoke shortcuts using Meta as a fallback. - if ((metaState & KeyEvent.META_META_ON) != 0) { - Intent shortcutIntent = mShortcutManager.getIntent(kcm, keyCode, - metaState & ~(KeyEvent.META_META_ON - | KeyEvent.META_META_LEFT_ON | KeyEvent.META_META_RIGHT_ON)); - if (shortcutIntent != null) { - shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - try { - mContext.startActivity(shortcutIntent); - } catch (ActivityNotFoundException ex) { - Slog.w(TAG, "Dropping shortcut key combination because " - + "the activity to which it is registered was not found: " - + "META+" + KeyEvent.keyCodeToString(keyCode), ex); - } - return null; - } - } - - // Display task switcher for ALT-TAB or Meta-TAB. - if (keyCode == KeyEvent.KEYCODE_TAB) { - final int shiftlessModifiers = event.getModifiers() & ~KeyEvent.META_SHIFT_MASK; - if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON) - || KeyEvent.metaStateHasModifiers( - shiftlessModifiers, KeyEvent.META_META_ON)) { - showOrHideRecentAppsDialog(shiftlessModifiers, false /*dismissIfShown*/); - return null; - } - } - } // Check for fallback actions specified by the key character map. if (getFallbackAction(kcm, keyCode, metaState, mFallbackAction)) { diff --git a/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java b/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java index aa00fbd..b9903dd 100644 --- a/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java +++ b/policy/src/com/android/internal/policy/impl/RecentApplicationsDialog.java @@ -71,8 +71,6 @@ public class RecentApplicationsDialog extends Dialog implements OnClickListener } }; - private int mHeldModifiers; - public RecentApplicationsDialog(Context context) { super(context, com.android.internal.R.style.Theme_Dialog_RecentApplications); @@ -124,17 +122,6 @@ public class RecentApplicationsDialog extends Dialog implements OnClickListener } } - /** - * Sets the modifier keys that are being held to keep the dialog open, or 0 if none. - * Used to make the recent apps dialog automatically dismiss itself when the modifiers - * all go up. - * @param heldModifiers The held key modifiers, such as {@link KeyEvent#META_ALT_ON}. - * Should exclude shift. - */ - public void setHeldModifiers(int heldModifiers) { - mHeldModifiers = heldModifiers; - } - @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_TAB) { @@ -174,30 +161,27 @@ public class RecentApplicationsDialog extends Dialog implements OnClickListener return super.onKeyDown(keyCode, event); } - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - if (mHeldModifiers != 0 && (event.getModifiers() & mHeldModifiers) == 0) { - final int numIcons = mIcons.length; - RecentTag tag = null; - for (int i = 0; i < numIcons; i++) { - if (mIcons[i].getVisibility() != View.VISIBLE) { + /** + * Dismiss the dialog and switch to the selected application. + */ + public void dismissAndSwitch() { + final int numIcons = mIcons.length; + RecentTag tag = null; + for (int i = 0; i < numIcons; i++) { + if (mIcons[i].getVisibility() != View.VISIBLE) { + break; + } + if (i == 0 || mIcons[i].hasFocus()) { + tag = (RecentTag) mIcons[i].getTag(); + if (mIcons[i].hasFocus()) { break; } - if (i == 0 || mIcons[i].hasFocus()) { - tag = (RecentTag) mIcons[i].getTag(); - if (mIcons[i].hasFocus()) { - break; - } - } - } - if (tag != null) { - switchTo(tag); } - dismiss(); - return true; } - - return super.onKeyUp(keyCode, event); + if (tag != null) { + switchTo(tag); + } + dismiss(); } /** diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp index 382f4d9..5f31f05 100644 --- a/services/input/InputDispatcher.cpp +++ b/services/input/InputDispatcher.cpp @@ -4312,12 +4312,23 @@ bool InputDispatcher::InputState::trackKey(const KeyEntry* entry, mKeyMementos.removeAt(index); return true; } + /* FIXME: We can't just drop the key up event because that prevents creating + * popup windows that are automatically shown when a key is held and then + * dismissed when the key is released. The problem is that the popup will + * not have received the original key down, so the key up will be considered + * to be inconsistent with its observed state. We could perhaps handle this + * by synthesizing a key down but that will cause other problems. + * + * So for now, allow inconsistent key up events to be dispatched. + * #if DEBUG_OUTBOUND_EVENT_DETAILS LOGD("Dropping inconsistent key up event: deviceId=%d, source=%08x, " "keyCode=%d, scanCode=%d", entry->deviceId, entry->source, entry->keyCode, entry->scanCode); #endif return false; + */ + return true; } case AKEY_EVENT_ACTION_DOWN: { diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 16643ff..cb291ce 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -840,7 +840,7 @@ public class WifiService extends IWifiManager.Stub { * of WifiLock & device idle status unless wifi enabled status is toggled */ - mWifiStateMachine.setDriverStart(true); + mWifiStateMachine.setDriverStart(true, mEmergencyCallbackMode); mWifiStateMachine.reconnectCommand(); } @@ -854,7 +854,7 @@ public class WifiService extends IWifiManager.Stub { * TODO: if a stop is issued, wifi is brought up only by startWifi * unless wifi enabled status is toggled */ - mWifiStateMachine.setDriverStart(false); + mWifiStateMachine.setDriverStart(false, mEmergencyCallbackMode); } @@ -1074,11 +1074,11 @@ public class WifiService extends IWifiManager.Stub { mWifiStateMachine.setWifiEnabled(true); mWifiStateMachine.setScanOnlyMode( strongestLockMode == WifiManager.WIFI_MODE_SCAN_ONLY); - mWifiStateMachine.setDriverStart(true); + mWifiStateMachine.setDriverStart(true, mEmergencyCallbackMode); mWifiStateMachine.setHighPerfModeEnabled(strongestLockMode == WifiManager.WIFI_MODE_FULL_HIGH_PERF); } else { - mWifiStateMachine.setDriverStart(false); + mWifiStateMachine.setDriverStart(false, mEmergencyCallbackMode); } } else { mWifiStateMachine.setWifiEnabled(false); diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBench.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBench.java index 4ed42b4..0dceafe 100644 --- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBench.java +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBench.java @@ -95,8 +95,10 @@ public class RsBench extends Activity { switch (item.getItemId()) { case R.id.benchmark_all: mView.setBenchmarkMode(-1); + mView.suspendRendering(false); return true; case R.id.benchmark_one: + mView.suspendRendering(true); AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("Pick a Test"); builder.setItems(mView.getTestNames(), @@ -106,11 +108,13 @@ public class RsBench extends Activity { "Starting to benchmark: " + mView.getTestNames()[item], Toast.LENGTH_SHORT).show(); mView.setBenchmarkMode(item); + mView.suspendRendering(false); } }); builder.show(); return true; case R.id.debug_mode: + mView.suspendRendering(true); AlertDialog.Builder debugBuilder = new AlertDialog.Builder(this); debugBuilder.setTitle("Pick a Test"); debugBuilder.setItems(mView.getTestNames(), @@ -120,6 +124,7 @@ public class RsBench extends Activity { "Switching to: " + mView.getTestNames()[item], Toast.LENGTH_SHORT).show(); mView.setDebugMode(item); + mView.suspendRendering(false); } }); debugBuilder.show(); diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java index 8c67026..4ac7dd5 100644 --- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java @@ -266,6 +266,10 @@ public class RsBenchRS { mScript.invoke_setBenchmarkMode(benchNum); } + public void pause(boolean pause) { + mScript.set_gPauseRendering(pause); + } + private void initRS() { mScript = new ScriptC_rsbench(mRS, mRes, R.raw.rsbench); diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchView.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchView.java index 004e6bf..124071e 100644 --- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchView.java +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchView.java @@ -92,6 +92,10 @@ public class RsBenchView extends RSSurfaceView { mRender.setBenchmarkMode(benchNum); } + void suspendRendering(boolean pause) { + mRender.pause(pause); + } + void setDebugMode(int num) { mRender.setDebugMode(num); } diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs index cb15449..27e5b11 100644 --- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs @@ -51,6 +51,7 @@ typedef struct TestScripts_s { TestScripts *gTestScripts; bool gLoadComplete = false; +bool gPauseRendering = false; static float gDt = 0; @@ -257,6 +258,10 @@ int root(void) { return 1; } + if (gPauseRendering) { + rsgDrawText("Paused", 50, 50); + return 30; + } if (gIsDebugMode) { debug(); } else { diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java index d5b404e..7bb927b 100644 --- a/wifi/java/android/net/wifi/WifiInfo.java +++ b/wifi/java/android/net/wifi/WifiInfo.java @@ -70,7 +70,6 @@ public class WifiInfo implements Parcelable { private InetAddress mIpAddress; private String mMacAddress; - private boolean mExplicitConnect; WifiInfo() { mSSID = null; @@ -80,7 +79,6 @@ public class WifiInfo implements Parcelable { mRssi = -9999; mLinkSpeed = -1; mHiddenSSID = false; - mExplicitConnect = false; } /** @@ -98,7 +96,6 @@ public class WifiInfo implements Parcelable { mLinkSpeed = source.mLinkSpeed; mIpAddress = source.mIpAddress; mMacAddress = source.mMacAddress; - mExplicitConnect = source.mExplicitConnect; } } @@ -175,22 +172,6 @@ public class WifiInfo implements Parcelable { mNetworkId = id; } - - /** - * @hide - */ - public boolean isExplicitConnect() { - return mExplicitConnect; - } - - /** - * @hide - */ - public void setExplicitConnect(boolean explicitConnect) { - this.mExplicitConnect = explicitConnect; - } - - /** * Each configured network has a unique small integer ID, used to identify * the network when performing operations on the supplicant. This method @@ -279,8 +260,7 @@ public class WifiInfo implements Parcelable { append(mSupplicantState == null ? none : mSupplicantState). append(", RSSI: ").append(mRssi). append(", Link speed: ").append(mLinkSpeed). - append(", Net ID: ").append(mNetworkId). - append(", Explicit connect: ").append(mExplicitConnect); + append(", Net ID: ").append(mNetworkId); return sb.toString(); } @@ -304,7 +284,6 @@ public class WifiInfo implements Parcelable { dest.writeString(getSSID()); dest.writeString(mBSSID); dest.writeString(mMacAddress); - dest.writeByte(mExplicitConnect ? (byte)1 : (byte)0); mSupplicantState.writeToParcel(dest, flags); } @@ -324,7 +303,6 @@ public class WifiInfo implements Parcelable { info.setSSID(in.readString()); info.mBSSID = in.readString(); info.mMacAddress = in.readString(); - info.mExplicitConnect = in.readByte() == 1 ? true : false; info.mSupplicantState = SupplicantState.CREATOR.createFromParcel(in); return info; } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 40ac2a0..1a0e0da 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -413,6 +413,13 @@ public class WifiManager { private static final int MAX_RSSI = -55; /** + * Number of RSSI levels used in the framework to initiate + * {@link #RSSI_CHANGED_ACTION} broadcast + * @hide + */ + public static final int RSSI_LEVELS = 5; + + /** * Auto settings in the driver. The driver could choose to operate on both * 2.4 GHz and 5 GHz or make a dynamic decision on selecting the band. * @hide diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java index ae8f466..5437caa 100644 --- a/wifi/java/android/net/wifi/WifiStateMachine.java +++ b/wifi/java/android/net/wifi/WifiStateMachine.java @@ -368,6 +368,10 @@ public class WifiStateMachine extends StateMachine { private static final int SUCCESS = 1; private static final int FAILURE = -1; + /* Phone in emergency call back mode */ + private static final int IN_ECM_STATE = 1; + private static final int NOT_IN_ECM_STATE = 0; + /** * The maximum number of times we will retry a connection to an access point * for which we have failed in acquiring an IP address from DHCP. A value of @@ -533,11 +537,6 @@ public class WifiStateMachine extends StateMachine { private final WorkSource mLastRunningWifiUids = new WorkSource(); private final IBatteryStats mBatteryStats; - private boolean mNextWifiActionExplicit = false; - private int mLastExplicitNetworkId; - private long mLastNetworkChoiceTime; - private static final long EXPLICIT_CONNECT_ALLOWED_DELAY_MS = 2 * 60 * 1000; - public WifiStateMachine(Context context, String wlanInterface) { super(TAG); @@ -778,11 +777,11 @@ public class WifiStateMachine extends StateMachine { /** * TODO: doc */ - public void setDriverStart(boolean enable) { + public void setDriverStart(boolean enable, boolean ecm) { if (enable) { sendMessage(CMD_START_DRIVER); } else { - sendMessage(CMD_STOP_DRIVER); + sendMessage(obtainMessage(CMD_STOP_DRIVER, ecm ? IN_ECM_STATE : NOT_IN_ECM_STATE, 0)); } } @@ -1458,14 +1457,11 @@ public class WifiStateMachine extends StateMachine { * be displayed in the status bar, and only send the * broadcast if that much more coarse-grained number * changes. This cuts down greatly on the number of - * broadcasts, at the cost of not mWifiInforming others + * broadcasts, at the cost of not informing others * interested in RSSI of all the changes in signal * level. */ - // TODO: The second arg to the call below needs to be a symbol somewhere, but - // it's actually the size of an array of icons that's private - // to StatusBar Policy. - int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, 4); + int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, WifiManager.RSSI_LEVELS); if (newSignalLevel != mLastSignalLevel) { sendRssiChangeBroadcast(newRssi); } @@ -1640,7 +1636,6 @@ public class WifiStateMachine extends StateMachine { mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID); mWifiInfo.setRssi(MIN_RSSI); mWifiInfo.setLinkSpeed(-1); - mWifiInfo.setExplicitConnect(false); /* send event to CM & network change broadcast */ setNetworkDetailedState(DetailedState.DISCONNECTED); @@ -2576,16 +2571,25 @@ public class WifiStateMachine extends StateMachine { WifiNative.setBluetoothCoexistenceScanModeCommand(mBluetoothConnectionActive); break; case CMD_STOP_DRIVER: - /* Already doing a delayed stop */ - if (mInDelayedStop) { + int mode = message.arg1; + + /* Already doing a delayed stop && not in ecm state */ + if (mInDelayedStop && mode != IN_ECM_STATE) { if (DBG) log("Already in delayed stop"); break; } mInDelayedStop = true; mDelayedStopCounter++; if (DBG) log("Delayed stop message " + mDelayedStopCounter); - sendMessageDelayed(obtainMessage(CMD_DELAYED_STOP_DRIVER, mDelayedStopCounter, - 0), DELAYED_DRIVER_STOP_MS); + + if (mode == IN_ECM_STATE) { + /* send a shut down immediately */ + sendMessage(obtainMessage(CMD_DELAYED_STOP_DRIVER, mDelayedStopCounter, 0)); + } else { + /* send regular delayed shut down */ + sendMessageDelayed(obtainMessage(CMD_DELAYED_STOP_DRIVER, + mDelayedStopCounter, 0), DELAYED_DRIVER_STOP_MS); + } break; case CMD_START_DRIVER: if (mInDelayedStop) { @@ -2824,10 +2828,6 @@ public class WifiStateMachine extends StateMachine { mSupplicantStateTracker.sendMessage(CMD_CONNECT_NETWORK); WifiNative.reconnectCommand(); - mLastExplicitNetworkId = netId; - mLastNetworkChoiceTime = SystemClock.elapsedRealtime(); - mNextWifiActionExplicit = true; - if (DBG) log("Setting wifi connect explicit for netid " + netId); /* Expect a disconnection from the old connection */ transitionTo(mDisconnectingState); break; @@ -2849,13 +2849,6 @@ public class WifiStateMachine extends StateMachine { mWifiInfo.setSSID(fetchSSID()); mWifiInfo.setBSSID(mLastBssid); mWifiInfo.setNetworkId(mLastNetworkId); - if (mNextWifiActionExplicit && - mWifiInfo.getNetworkId() == mLastExplicitNetworkId && - SystemClock.elapsedRealtime() < mLastNetworkChoiceTime + - EXPLICIT_CONNECT_ALLOWED_DELAY_MS) { - mWifiInfo.setExplicitConnect(true); - } - mNextWifiActionExplicit = false; /* send event to CM & network change broadcast */ setNetworkDetailedState(DetailedState.OBTAINING_IPADDR); sendNetworkStateChangeBroadcast(mLastBssid); diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java index b27c60f..0ca3852 100644 --- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java +++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java @@ -1030,7 +1030,7 @@ public class WifiWatchdogStateMachine extends StateMachine { mHasConnectedWifiManager = true; } mWifiManager.disableNetwork(networkId, WifiConfiguration.DISABLED_DNS_FAILURE); - if (mShowDisabledNotification && mConnectionInfo.isExplicitConnect()) { + if (mShowDisabledNotification) { setDisabledNetworkNotificationVisible(true); } transitionTo(mNotConnectedState); |