diff options
Diffstat (limited to 'core')
23 files changed, 466 insertions, 489 deletions
diff --git a/core/java/android/content/XmlDocumentProvider.java b/core/java/android/content/XmlDocumentProvider.java deleted file mode 100644 index 76539c7..0000000 --- a/core/java/android/content/XmlDocumentProvider.java +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Copyright (C) 2010 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.content; - -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.client.methods.HttpGet; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlPullParserFactory; - -import android.content.ContentResolver.OpenResourceIdResult; -import android.database.Cursor; -import android.database.MatrixCursor; -import android.net.Uri; -import android.net.http.AndroidHttpClient; -import android.util.Log; -import android.widget.CursorAdapter; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.BitSet; -import java.util.Stack; -import java.util.regex.Pattern; - -/** - * @hide -- not yet ready to support, should be provided just as a static lib. - * - * A read-only content provider which extracts data out of an XML document. - * - * <p>A XPath-like selection pattern is used to select some nodes in the XML document. Each such - * node will create a row in the {@link Cursor} result.</p> - * - * Each row is then populated with columns that are also defined as XPath-like projections. These - * projections fetch attributes values or text in the matching row node or its children. - * - * <p>To add this provider in your application, you should add its declaration to your application - * manifest: - * <pre class="prettyprint"> - * <provider android:name="android.content.XmlDocumentProvider" android:authorities="xmldocument" /> - * </pre> - * </p> - * - * <h2>Node selection syntax</h2> - * The node selection syntax is made of the concatenation of an arbitrary number (at least one) of - * <code>/node_name</code> node selection patterns. - * - * <p>The <code>/root/child1/child2</code> pattern will for instance match all nodes named - * <code>child2</code> which are children of a node named <code>child1</code> which are themselves - * children of a root node named <code>root</code>.</p> - * - * Any <code>/</code> separator in the previous expression can be replaced by a <code>//</code> - * separator instead, which indicated a <i>descendant</i> instead of a child. - * - * <p>The <code>//node1//node2</code> pattern will for instance match all nodes named - * <code>node2</code> which are descendant of a node named <code>node1</code> located anywhere in - * the document hierarchy.</p> - * - * Node names can contain namespaces in the form <code>namespace:node</code>. - * - * <h2>Projection syntax</h2> - * For every selected node, the projection will then extract actual data from this node and its - * descendant. - * - * <p>Use a syntax similar to the selection syntax described above to select the text associated - * with a child of the selected node. The implicit root of this projection pattern is the selected - * node. <code>/</code> will hence refer to the text of the selected node, while - * <code>/child1</code> will fetch the text of its child named <code>child1</code> and - * <code>//child1</code> will match any <i>descendant</i> named <code>child1</code>. If several - * nodes match the projection pattern, their texts are appended as a result.</p> - * - * A projection can also fetch any node attribute by appending a <code>@attribute_name</code> - * pattern to the previously described syntax. <code>//child1@price</code> will for instance match - * the attribute <code>price</code> of any <code>child1</code> descendant. - * - * <p>If a projection does not match any node/attribute, its associated value will be an empty - * string.</p> - * - * <h2>Example</h2> - * Using the following XML document: - * <pre class="prettyprint"> - * <library> - * <book id="EH94"> - * <title>The Old Man and the Sea</title> - * <author>Ernest Hemingway</author> - * </book> - * <book id="XX10"> - * <title>The Arabian Nights: Tales of 1,001 Nights</title> - * </book> - * <no-id> - * <book> - * <title>Animal Farm</title> - * <author>George Orwell</author> - * </book> - * </no-id> - * </library> - * </pre> - * A selection pattern of <code>/library//book</code> will match the three book entries (while - * <code>/library/book</code> will only match the first two ones). - * - * <p>Defining the projections as <code>/title</code>, <code>/author</code> and <code>@id</code> - * will retrieve the associated data. Note that the author of the second book as well as the id of - * the third are empty strings. - */ -public class XmlDocumentProvider extends ContentProvider { - /* - * Ideas for improvement: - * - Expand XPath-like syntax to allow for [nb] child number selector - * - Address the starting . bug in AbstractCursor which prevents a true XPath syntax. - * - Provide an alternative to concatenation when several node match (list-like). - * - Support namespaces in attribute names. - * - Incremental Cursor creation, pagination - */ - private static final String LOG_TAG = "XmlDocumentProvider"; - private AndroidHttpClient mHttpClient; - - @Override - public boolean onCreate() { - return true; - } - - /** - * Query data from the XML document referenced in the URI. - * - * <p>The XML document can be a local resource or a file that will be downloaded from the - * Internet. In the latter case, your application needs to request the INTERNET permission in - * its manifest.</p> - * - * The URI will be of the form <code>content://xmldocument/?resource=R.xml.myFile</code> for a - * local resource. <code>xmldocument</code> should match the authority declared for this - * provider in your manifest. Internet documents are referenced using - * <code>content://xmldocument/?url=</code> followed by an encoded version of the URL of your - * document (see {@link Uri#encode(String)}). - * - * <p>The number of columns of the resulting Cursor is equal to the size of the projection - * array plus one, named <code>_id</code> which will contain a unique row id (allowing the - * Cursor to be used with a {@link CursorAdapter}). The other columns' names are the projection - * patterns.</p> - * - * @param uri The URI of your local resource or Internet document. - * @param projection A set of patterns that will be used to extract data from each selected - * node. See class documentation for pattern syntax. - * @param selection A selection pattern which will select the nodes that will create the - * Cursor's rows. See class documentation for pattern syntax. - * @param selectionArgs This parameter is ignored. - * @param sortOrder The row order in the resulting cursor is determined from the node order in - * the XML document. This parameter is ignored. - * @return A Cursor or null in case of error. - */ - @Override - public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, - String sortOrder) { - - XmlPullParser parser = null; - mHttpClient = null; - - final String url = uri.getQueryParameter("url"); - if (url != null) { - parser = getUriXmlPullParser(url); - } else { - final String resource = uri.getQueryParameter("resource"); - if (resource != null) { - Uri resourceUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + - getContext().getPackageName() + "/" + resource); - parser = getResourceXmlPullParser(resourceUri); - } - } - - if (parser != null) { - XMLCursor xmlCursor = new XMLCursor(selection, projection); - try { - xmlCursor.parseWith(parser); - return xmlCursor; - } catch (IOException e) { - Log.w(LOG_TAG, "I/O error while parsing XML " + uri, e); - } catch (XmlPullParserException e) { - Log.w(LOG_TAG, "Error while parsing XML " + uri, e); - } finally { - if (mHttpClient != null) { - mHttpClient.close(); - } - } - } - - return null; - } - - /** - * Creates an XmlPullParser for the provided URL. Can be overloaded to provide your own parser. - * @param url The URL of the XML document that is to be parsed. - * @return An XmlPullParser on this document. - */ - protected XmlPullParser getUriXmlPullParser(String url) { - XmlPullParser parser = null; - try { - XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); - factory.setNamespaceAware(true); - parser = factory.newPullParser(); - } catch (XmlPullParserException e) { - Log.e(LOG_TAG, "Unable to create XmlPullParser", e); - return null; - } - - InputStream inputStream = null; - try { - final HttpGet get = new HttpGet(url); - mHttpClient = AndroidHttpClient.newInstance("Android"); - HttpResponse response = mHttpClient.execute(get); - if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { - final HttpEntity entity = response.getEntity(); - if (entity != null) { - inputStream = entity.getContent(); - } - } - } catch (IOException e) { - Log.w(LOG_TAG, "Error while retrieving XML file " + url, e); - return null; - } - - try { - parser.setInput(inputStream, null); - } catch (XmlPullParserException e) { - Log.w(LOG_TAG, "Error while reading XML file from " + url, e); - return null; - } - - return parser; - } - - /** - * Creates an XmlPullParser for the provided local resource. Can be overloaded to provide your - * own parser. - * @param resourceUri A fully qualified resource name referencing a local XML resource. - * @return An XmlPullParser on this resource. - */ - protected XmlPullParser getResourceXmlPullParser(Uri resourceUri) { - OpenResourceIdResult resourceId; - try { - resourceId = getContext().getContentResolver().getResourceId(resourceUri); - return resourceId.r.getXml(resourceId.id); - } catch (FileNotFoundException e) { - Log.w(LOG_TAG, "XML resource not found: " + resourceUri.toString(), e); - return null; - } - } - - /** - * Returns "vnd.android.cursor.dir/xmldoc". - */ - @Override - public String getType(Uri uri) { - return "vnd.android.cursor.dir/xmldoc"; - } - - /** - * This ContentProvider is read-only. This method throws an UnsupportedOperationException. - **/ - @Override - public Uri insert(Uri uri, ContentValues values) { - throw new UnsupportedOperationException(); - } - - /** - * This ContentProvider is read-only. This method throws an UnsupportedOperationException. - **/ - @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { - throw new UnsupportedOperationException(); - } - - /** - * This ContentProvider is read-only. This method throws an UnsupportedOperationException. - **/ - @Override - public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { - throw new UnsupportedOperationException(); - } - - private static class XMLCursor extends MatrixCursor { - private final Pattern mSelectionPattern; - private Pattern[] mProjectionPatterns; - private String[] mAttributeNames; - private String[] mCurrentValues; - private BitSet[] mActiveTextDepthMask; - private final int mNumberOfProjections; - - public XMLCursor(String selection, String[] projections) { - super(projections); - // The first column in projections is used for the _ID - mNumberOfProjections = projections.length - 1; - mSelectionPattern = createPattern(selection); - createProjectionPattern(projections); - } - - private Pattern createPattern(String input) { - String pattern = input.replaceAll("//", "/(.*/|)").replaceAll("^/", "^/") + "$"; - return Pattern.compile(pattern); - } - - private void createProjectionPattern(String[] projections) { - mProjectionPatterns = new Pattern[mNumberOfProjections]; - mAttributeNames = new String[mNumberOfProjections]; - mActiveTextDepthMask = new BitSet[mNumberOfProjections]; - // Add a column to store _ID - mCurrentValues = new String[mNumberOfProjections + 1]; - - for (int i=0; i<mNumberOfProjections; i++) { - mActiveTextDepthMask[i] = new BitSet(); - String projection = projections[i + 1]; // +1 to skip the _ID column - int atIndex = projection.lastIndexOf('@', projection.length()); - if (atIndex >= 0) { - mAttributeNames[i] = projection.substring(atIndex+1); - projection = projection.substring(0, atIndex); - } else { - mAttributeNames[i] = null; - } - - // Conforms to XPath standard: reference to local context starts with a . - if (projection.charAt(0) == '.') { - projection = projection.substring(1); - } - mProjectionPatterns[i] = createPattern(projection); - } - } - - public void parseWith(XmlPullParser parser) throws IOException, XmlPullParserException { - StringBuilder path = new StringBuilder(); - Stack<Integer> pathLengthStack = new Stack<Integer>(); - - // There are two parsing mode: in root mode, rootPath is updated and nodes matching - // selectionPattern are searched for and currentNodeDepth is negative. - // When a node matching selectionPattern is found, currentNodeDepth is set to 0 and - // updated as children are parsed and projectionPatterns are searched in nodePath. - int currentNodeDepth = -1; - - // Index where local selected node path starts from in path - int currentNodePathStartIndex = 0; - - int eventType = parser.getEventType(); - while (eventType != XmlPullParser.END_DOCUMENT) { - - if (eventType == XmlPullParser.START_TAG) { - // Update path - pathLengthStack.push(path.length()); - path.append('/'); - String prefix = null; - try { - // getPrefix is not supported by local Xml resource parser - prefix = parser.getPrefix(); - } catch (RuntimeException e) { - prefix = null; - } - if (prefix != null) { - path.append(prefix); - path.append(':'); - } - path.append(parser.getName()); - - if (currentNodeDepth >= 0) { - currentNodeDepth++; - } else { - // A node matching selection is found: initialize child parsing mode - if (mSelectionPattern.matcher(path.toString()).matches()) { - currentNodeDepth = 0; - currentNodePathStartIndex = path.length(); - mCurrentValues[0] = Integer.toString(getCount()); // _ID - for (int i = 0; i < mNumberOfProjections; i++) { - // Reset values to default (empty string) - mCurrentValues[i + 1] = ""; - mActiveTextDepthMask[i].clear(); - } - } - } - - // This test has to be separated from the previous one as currentNodeDepth can - // be modified above (when a node matching selection is found). - if (currentNodeDepth >= 0) { - final String localNodePath = path.substring(currentNodePathStartIndex); - for (int i = 0; i < mNumberOfProjections; i++) { - if (mProjectionPatterns[i].matcher(localNodePath).matches()) { - String attribute = mAttributeNames[i]; - if (attribute != null) { - mCurrentValues[i + 1] = - parser.getAttributeValue(null, attribute); - } else { - mActiveTextDepthMask[i].set(currentNodeDepth, true); - } - } - } - } - - } else if (eventType == XmlPullParser.END_TAG) { - // Pop last node from path - final int length = pathLengthStack.pop(); - path.setLength(length); - - if (currentNodeDepth >= 0) { - if (currentNodeDepth == 0) { - // Leaving a selection matching node: add a new row with results - addRow(mCurrentValues); - } else { - for (int i = 0; i < mNumberOfProjections; i++) { - mActiveTextDepthMask[i].set(currentNodeDepth, false); - } - } - currentNodeDepth--; - } - - } else if ((eventType == XmlPullParser.TEXT) && (!parser.isWhitespace())) { - for (int i = 0; i < mNumberOfProjections; i++) { - if ((currentNodeDepth >= 0) && - (mActiveTextDepthMask[i].get(currentNodeDepth))) { - mCurrentValues[i + 1] += parser.getText(); - } - } - } - - eventType = parser.next(); - } - } - } -} diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java index c7603ee..8ef4295 100644 --- a/core/java/android/speech/tts/AudioPlaybackHandler.java +++ b/core/java/android/speech/tts/AudioPlaybackHandler.java @@ -384,11 +384,16 @@ class AudioPlaybackHandler { } count += written; } + + param.mLogger.onPlaybackStart(); } private void handleSynthesisDone(MessageParams msg) { final SynthesisMessageParams params = (SynthesisMessageParams) msg; handleSynthesisDone(params); + // This call is delayed more than it should be, but we are + // certain at this point that we have all the data we want. + params.mLogger.onWriteData(); } // Flush all remaining data to the audio track, stop it and release @@ -416,6 +421,8 @@ class AudioPlaybackHandler { final SynthesisMessageParams params = (SynthesisMessageParams) msg; if (DBG) Log.d(TAG, "completeAudioAvailable(" + params + ")"); + params.mLogger.onPlaybackStart(); + // Channel config and bytes per frame are checked before // this message is sent. int channelConfig = AudioPlaybackHandler.getChannelConfig(params.mChannelCount); diff --git a/core/java/android/speech/tts/EventLogTags.logtags b/core/java/android/speech/tts/EventLogTags.logtags new file mode 100644 index 0000000..1a9f5fe --- /dev/null +++ b/core/java/android/speech/tts/EventLogTags.logtags @@ -0,0 +1,6 @@ +# See system/core/logcat/event.logtags for a description of the format of this file. + +option java_package android.speech.tts; + +76001 tts_speak_success (engine|3),(caller|3),(length|1),(locale|3),(rate|1),(pitch|1),(engine_latency|2|3),(engine_total|2|3),(audio_latency|2|3) +76002 tts_speak_failure (engine|3),(caller|3),(length|1),(locale|3),(rate|1),(pitch|1) diff --git a/core/java/android/speech/tts/EventLogger.java b/core/java/android/speech/tts/EventLogger.java new file mode 100644 index 0000000..63b954b --- /dev/null +++ b/core/java/android/speech/tts/EventLogger.java @@ -0,0 +1,175 @@ +/* + * 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.os.SystemClock; +import android.text.TextUtils; + +/** + * Writes data about a given speech synthesis request to the event logs. + * The data that is logged includes the calling app, length of the utterance, + * speech rate / pitch and the latency and overall time taken. + * + * Note that {@link EventLogger#onStopped()} and {@link EventLogger#onError()} + * might be called from any thread, but on {@link EventLogger#onPlaybackStart()} and + * {@link EventLogger#onComplete()} must be called from a single thread + * (usually the audio playback thread} + */ +class EventLogger { + private final SynthesisRequest mRequest; + private final String mCallingApp; + private final String mServiceApp; + private final long mReceivedTime; + private long mPlaybackStartTime = -1; + private volatile long mRequestProcessingStartTime = -1; + private volatile long mEngineStartTime = -1; + private volatile long mEngineCompleteTime = -1; + + private volatile boolean mError = false; + private volatile boolean mStopped = false; + private boolean mLogWritten = false; + + EventLogger(SynthesisRequest request, String callingApp, + String serviceApp) { + mRequest = request; + mCallingApp = callingApp; + mServiceApp = serviceApp; + mReceivedTime = SystemClock.elapsedRealtime(); + } + + /** + * Notifies the logger that this request has been selected from + * the processing queue for processing. Engine latency / total time + * is measured from this baseline. + */ + public void onRequestProcessingStart() { + mRequestProcessingStartTime = SystemClock.elapsedRealtime(); + } + + /** + * Notifies the logger that a chunk of data has been received from + * the engine. Might be called multiple times. + */ + public void onEngineDataReceived() { + if (mEngineStartTime == -1) { + mEngineStartTime = SystemClock.elapsedRealtime(); + } + } + + /** + * Notifies the logger that the engine has finished processing data. + * Will be called exactly once. + */ + public void onEngineComplete() { + mEngineCompleteTime = SystemClock.elapsedRealtime(); + } + + /** + * Notifies the logger that audio playback has started for some section + * of the synthesis. This is normally some amount of time after the engine + * has synthesized data and varides depending on utterances and + * other audio currently in the queue. + */ + public void onPlaybackStart() { + // For now, keep track of only the first chunk of audio + // that was played. + if (mPlaybackStartTime == -1) { + mPlaybackStartTime = SystemClock.elapsedRealtime(); + } + } + + /** + * Notifies the logger that the current synthesis was stopped. + * Latency numbers are not reported for stopped syntheses. + */ + public void onStopped() { + mStopped = false; + } + + /** + * Notifies the logger that the current synthesis resulted in + * an error. This is logged using {@link EventLogTags#writeTtsSpeakFailure}. + */ + public void onError() { + mError = true; + } + + /** + * Notifies the logger that the current synthesis has completed. + * All available data is not logged. + */ + public void onWriteData() { + if (mLogWritten) { + return; + } else { + mLogWritten = true; + } + + long completionTime = SystemClock.elapsedRealtime(); + // onPlaybackStart() should normally always be called if an + // error does not occur. + if (mError || mPlaybackStartTime == -1 || mEngineCompleteTime == -1) { + EventLogTags.writeTtsSpeakFailure(mServiceApp, mCallingApp, + getUtteranceLength(), getLocaleString(), + mRequest.getSpeechRate(), mRequest.getPitch()); + return; + } + + // We don't report stopped syntheses because their overall + // total time spent will be innacurate (will not correlate with + // the length of the utterance). + if (mStopped) { + return; + } + + final long audioLatency = mPlaybackStartTime - mReceivedTime; + final long engineLatency = mEngineStartTime - mRequestProcessingStartTime; + final long engineTotal = mEngineCompleteTime - mRequestProcessingStartTime; + EventLogTags.writeTtsSpeakSuccess(mServiceApp, mCallingApp, + getUtteranceLength(), getLocaleString(), + mRequest.getSpeechRate(), mRequest.getPitch(), + engineLatency, engineTotal, audioLatency); + } + + /** + * @return the length of the utterance for the given synthesis, 0 + * if the utterance was {@code null}. + */ + private int getUtteranceLength() { + final String utterance = mRequest.getText(); + return utterance == null ? 0 : utterance.length(); + } + + /** + * Returns a formatted locale string from the synthesis params of the + * form lang-country-variant. + */ + private String getLocaleString() { + StringBuilder sb = new StringBuilder(mRequest.getLanguage()); + if (!TextUtils.isEmpty(mRequest.getCountry())) { + sb.append('-'); + sb.append(mRequest.getCountry()); + + if (!TextUtils.isEmpty(mRequest.getVariant())) { + sb.append('-'); + sb.append(mRequest.getVariant()); + } + } + + return sb.toString(); + } + +} diff --git a/core/java/android/speech/tts/PlaybackSynthesisCallback.java b/core/java/android/speech/tts/PlaybackSynthesisCallback.java index bdaa1b8..38030a6 100644 --- a/core/java/android/speech/tts/PlaybackSynthesisCallback.java +++ b/core/java/android/speech/tts/PlaybackSynthesisCallback.java @@ -65,29 +65,42 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { private final UtteranceCompletedDispatcher mDispatcher; private final String mCallingApp; + private final EventLogger mLogger; PlaybackSynthesisCallback(int streamType, float volume, float pan, AudioPlaybackHandler audioTrackHandler, UtteranceCompletedDispatcher dispatcher, - String callingApp) { + String callingApp, EventLogger logger) { mStreamType = streamType; mVolume = volume; mPan = pan; mAudioTrackHandler = audioTrackHandler; mDispatcher = dispatcher; mCallingApp = callingApp; + mLogger = logger; } @Override void stop() { if (DBG) Log.d(TAG, "stop()"); + // Note that mLogger.mError might be true too at this point. + mLogger.onStopped(); + synchronized (mStateLock) { - if (mToken == null || mStopped) { - Log.w(TAG, "stop() called twice, before start(), or after done()"); + if (mStopped) { + Log.w(TAG, "stop() called twice"); return; } - mAudioTrackHandler.stop(mToken); - mToken = null; + // mToken will be null if the engine encounters + // an error before it called start(). + if (mToken != null) { + mAudioTrackHandler.stop(mToken); + mToken = null; + } else { + // In all other cases, mAudioTrackHandler.stop() will + // result in onComplete being called. + mLogger.onWriteData(); + } mStopped = true; } } @@ -124,7 +137,7 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { } SynthesisMessageParams params = new SynthesisMessageParams( mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan, - mDispatcher, mCallingApp); + mDispatcher, mCallingApp, mLogger); mAudioTrackHandler.enqueueSynthesisStart(params); mToken = params; @@ -157,6 +170,8 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { mAudioTrackHandler.enqueueSynthesisDataAvailable(mToken); } + mLogger.onEngineDataReceived(); + return TextToSpeech.SUCCESS; } @@ -177,6 +192,7 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { } mAudioTrackHandler.enqueueSynthesisDone(mToken); + mLogger.onEngineComplete(); } return TextToSpeech.SUCCESS; } @@ -184,6 +200,9 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { @Override public void error() { if (DBG) Log.d(TAG, "error() [will call stop]"); + // Currently, this call will not be logged if error( ) is called + // before start. + mLogger.onError(); stop(); } @@ -208,7 +227,7 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { } SynthesisMessageParams params = new SynthesisMessageParams( mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan, - mDispatcher, mCallingApp); + mDispatcher, mCallingApp, mLogger); params.addBuffer(buffer, offset, length); mAudioTrackHandler.enqueueSynthesisCompleteDataAvailable(params); diff --git a/core/java/android/speech/tts/SynthesisMessageParams.java b/core/java/android/speech/tts/SynthesisMessageParams.java index 51f3d2e..caf02ef 100644 --- a/core/java/android/speech/tts/SynthesisMessageParams.java +++ b/core/java/android/speech/tts/SynthesisMessageParams.java @@ -30,6 +30,7 @@ final class SynthesisMessageParams extends MessageParams { final int mChannelCount; final float mVolume; final float mPan; + final EventLogger mLogger; public volatile AudioTrack mAudioTrack; @@ -38,7 +39,7 @@ final class SynthesisMessageParams extends MessageParams { SynthesisMessageParams(int streamType, int sampleRate, int audioFormat, int channelCount, float volume, float pan, UtteranceCompletedDispatcher dispatcher, - String callingApp) { + String callingApp, EventLogger logger) { super(dispatcher, callingApp); mStreamType = streamType; @@ -47,6 +48,7 @@ final class SynthesisMessageParams extends MessageParams { mChannelCount = channelCount; mVolume = volume; mPan = pan; + mLogger = logger; // initially null. mAudioTrack = null; diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index 7ea9373..010c155 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -82,8 +82,7 @@ public abstract class TextToSpeechService extends Service { private AudioPlaybackHandler mAudioPlaybackHandler; private CallbackMap mCallbacks; - - private int mDefaultAvailability = TextToSpeech.LANG_NOT_SUPPORTED; + private String mPackageName; @Override public void onCreate() { @@ -99,9 +98,10 @@ public abstract class TextToSpeechService extends Service { mCallbacks = new CallbackMap(); + mPackageName = getApplicationInfo().packageName; + // Load default language - mDefaultAvailability = onLoadLanguage(getDefaultLanguage(), - getDefaultCountry(), getDefaultVariant()); + onLoadLanguage(getDefaultLanguage(), getDefaultCountry(), getDefaultVariant()); } @Override @@ -457,12 +457,14 @@ public abstract class TextToSpeechService extends Service { // Non null after synthesis has started, and all accesses // guarded by 'this'. private AbstractSynthesisCallback mSynthesisCallback; + private final EventLogger mEventLogger; public SynthesisSpeechItem(String callingApp, Bundle params, String text) { super(callingApp, params); mText = text; mSynthesisRequest = new SynthesisRequest(mText, mParams); setRequestParams(mSynthesisRequest); + mEventLogger = new EventLogger(mSynthesisRequest, getCallingApp(), mPackageName); } public String getText() { @@ -485,6 +487,7 @@ public abstract class TextToSpeechService extends Service { @Override protected int playImpl() { AbstractSynthesisCallback synthesisCallback; + mEventLogger.onRequestProcessingStart(); synchronized (this) { mSynthesisCallback = createSynthesisCallback(); synthesisCallback = mSynthesisCallback; @@ -495,7 +498,7 @@ public abstract class TextToSpeechService extends Service { protected AbstractSynthesisCallback createSynthesisCallback() { return new PlaybackSynthesisCallback(getStreamType(), getVolume(), getPan(), - mAudioPlaybackHandler, this, getCallingApp()); + mAudioPlaybackHandler, this, getCallingApp(), mEventLogger); } private void setRequestParams(SynthesisRequest request) { diff --git a/core/java/android/text/method/AllCapsTransformationMethod.java b/core/java/android/text/method/AllCapsTransformationMethod.java new file mode 100644 index 0000000..f9920dd --- /dev/null +++ b/core/java/android/text/method/AllCapsTransformationMethod.java @@ -0,0 +1,59 @@ +/* + * 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.text.method; + +import android.content.Context; +import android.graphics.Rect; +import android.util.Log; +import android.view.View; + +import java.util.Locale; + +/** + * Transforms source text into an ALL CAPS string, locale-aware. + * + * @hide + */ +public class AllCapsTransformationMethod implements TransformationMethod2 { + private static final String TAG = "AllCapsTransformationMethod"; + + private boolean mEnabled; + private Locale mLocale; + + public AllCapsTransformationMethod(Context context) { + mLocale = context.getResources().getConfiguration().locale; + } + + @Override + public CharSequence getTransformation(CharSequence source, View view) { + if (mEnabled) { + return source != null ? source.toString().toUpperCase(mLocale) : null; + } + Log.w(TAG, "Caller did not enable length changes; not transforming text"); + return source; + } + + @Override + public void onFocusChanged(View view, CharSequence sourceText, boolean focused, int direction, + Rect previouslyFocusedRect) { + } + + @Override + public void setLengthChangesAllowed(boolean allowLengthChanges) { + mEnabled = allowLengthChanges; + } + +} diff --git a/core/java/android/text/method/TransformationMethod2.java b/core/java/android/text/method/TransformationMethod2.java new file mode 100644 index 0000000..ef00ecd --- /dev/null +++ b/core/java/android/text/method/TransformationMethod2.java @@ -0,0 +1,33 @@ +/* + * 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.text.method; + +/** + * TransformationMethod2 extends the TransformationMethod interface + * and adds the ability to relax restrictions of TransformationMethod. + * + * @hide + */ +public interface TransformationMethod2 extends TransformationMethod { + /** + * Relax the contract of TransformationMethod to allow length changes, + * or revert to the length-restricted behavior. + * + * @param allowLengthChanges true to allow the transformation to change the length + * of the input string. + */ + public void setLengthChangesAllowed(boolean allowLengthChanges); +} diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index b6c5522..4987e2f 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -246,8 +246,19 @@ class GLES20Canvas extends HardwareCanvas { return nIsBackBufferPreserved(); } - private static native boolean nIsBackBufferPreserved(); - + private static native boolean nIsBackBufferPreserved(); + + /** + * Disables v-sync. For performance testing only. + * + * @hide + */ + public static void disableVsync() { + nDisableVsync(); + } + + private static native void nDisableVsync(); + @Override void onPreDraw(Rect dirty) { if (dirty != null) { @@ -265,7 +276,7 @@ class GLES20Canvas extends HardwareCanvas { void onPostDraw() { nFinish(mRenderer); } - + private static native void nFinish(int renderer); @Override diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index bbfb4c1..188970c 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -57,6 +57,16 @@ public abstract class HardwareRenderer { * "false", to disable partial invalidates */ static final String RENDER_DIRTY_REGIONS_PROPERTY = "hwui.render_dirty_regions"; + + /** + * System property used to enable or disable vsync. + * The default value of this property is assumed to be false. + * + * Possible values: + * "true", to disable vsync + * "false", to enable vsync + */ + static final String DISABLE_VSYNC_PROPERTY = "hwui.disable_vsync"; /** * Turn on to draw dirty regions every other frame. @@ -303,6 +313,7 @@ public abstract class HardwareRenderer { boolean mDirtyRegions; final boolean mDirtyRegionsRequested; + final boolean mVsyncDisabled; final int mGlVersion; final boolean mTranslucent; @@ -314,10 +325,17 @@ public abstract class HardwareRenderer { GlRenderer(int glVersion, boolean translucent) { mGlVersion = glVersion; mTranslucent = translucent; + final String dirtyProperty = SystemProperties.get(RENDER_DIRTY_REGIONS_PROPERTY, "true"); //noinspection PointlessBooleanExpression,ConstantConditions mDirtyRegions = RENDER_DIRTY_REGIONS && "true".equalsIgnoreCase(dirtyProperty); mDirtyRegionsRequested = mDirtyRegions; + + final String vsyncProperty = SystemProperties.get(DISABLE_VSYNC_PROPERTY, "false"); + mVsyncDisabled = "true".equalsIgnoreCase(vsyncProperty); + if (mVsyncDisabled) { + Log.d(LOG_TAG, "Disabling v-sync"); + } } /** @@ -765,6 +783,14 @@ public abstract class HardwareRenderer { } @Override + void setup(int width, int height) { + super.setup(width, height); + if (mVsyncDisabled) { + GLES20Canvas.disableVsync(); + } + } + + @Override DisplayList createDisplayList(View v) { return new GLES20DisplayList(v); } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 41412de..e6fdb17 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -46,7 +46,6 @@ import com.android.internal.util.Predicate; import java.util.ArrayList; import java.util.HashSet; -import java.util.Locale; /** * <p> @@ -5019,7 +5018,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ @Override protected void resolveTextDirection() { - int resolvedTextDirection = TEXT_DIRECTION_UNDEFINED; + int resolvedTextDirection; switch(mTextDirection) { default: case TEXT_DIRECTION_INHERIT: diff --git a/core/java/android/webkit/L10nUtils.java b/core/java/android/webkit/L10nUtils.java index 4c42cde..f9d0067d 100644 --- a/core/java/android/webkit/L10nUtils.java +++ b/core/java/android/webkit/L10nUtils.java @@ -74,7 +74,8 @@ public class L10nUtils { com.android.internal.R.string.autofill_country_code_re, // IDS_AUTOFILL_COUNTRY_CODE_RE com.android.internal.R.string.autofill_area_code_notext_re, // IDS_AUTOFILL_AREA_CODE_NOTEXT_RE com.android.internal.R.string.autofill_phone_prefix_separator_re, // IDS_AUTOFILL_PHONE_PREFIX_SEPARATOR_RE - com.android.internal.R.string.autofill_phone_suffix_separator_re // IDS_AUTOFILL_PHONE_SUFFIX_SEPARATOR_RE + com.android.internal.R.string.autofill_phone_suffix_separator_re, // IDS_AUTOFILL_PHONE_SUFFIX_SEPARATOR_RE + com.android.internal.R.string.credit_card_number_preview_format // IDS_CREDIT_CARD_NUMBER_PREVIEW_FORMAT }; private static Context mApplicationContext; diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 4f97066..c652e55 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -2235,16 +2235,15 @@ public final class WebViewCore { // called by JNI private void updateViewport() { - // if updateViewport is called before first layout, wait until first - // layout to update the viewport. In the rare case, this is called after - // first layout, force an update as we have just parsed the viewport - // meta tag. - if (mBrowserFrame.firstLayoutDone()) { - setupViewport(true); - } + // Update viewport asap to make sure we get correct one. + setupViewport(true); } private void setupViewport(boolean updateViewState) { + if (mWebView == null || mSettings == null) { + // We've been destroyed or are being destroyed, return early + return; + } // set the viewport settings from WebKit setViewportSettingsFromNative(); @@ -2375,8 +2374,12 @@ public final class WebViewCore { (float) webViewWidth / mViewportWidth; } else { mInitialViewState.mTextWrapScale = adjust; - // 0 will trigger WebView to turn on zoom overview mode - mInitialViewState.mViewScale = 0; + if (mSettings.getUseWideViewPort()) { + // 0 will trigger WebView to turn on zoom overview mode + mInitialViewState.mViewScale = 0; + } else { + mInitialViewState.mViewScale = adjust; + } } } @@ -2407,7 +2410,7 @@ public final class WebViewCore { mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED); mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null, EventHub.VIEW_SIZE_CHANGED, data)); - } else if (mSettings.getUseWideViewPort()) { + } else { if (viewportWidth == 0) { // Trick to ensure VIEW_SIZE_CHANGED will be sent from WebView // to WebViewCore diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 772e8e9..1e63e26 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -16,6 +16,11 @@ package android.widget; +import com.android.internal.util.FastMath; +import com.android.internal.widget.EditableInputConnection; + +import org.xmlpull.v1.XmlPullParserException; + import android.R; import android.content.ClipData; import android.content.ClipData.Item; @@ -62,6 +67,7 @@ import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; import android.text.TextWatcher; +import android.text.method.AllCapsTransformationMethod; import android.text.method.ArrowKeyMovementMethod; import android.text.method.DateKeyListener; import android.text.method.DateTimeKeyListener; @@ -76,6 +82,7 @@ import android.text.method.SingleLineTransformationMethod; import android.text.method.TextKeyListener; import android.text.method.TimeKeyListener; import android.text.method.TransformationMethod; +import android.text.method.TransformationMethod2; import android.text.method.WordIterator; import android.text.style.ClickableSpan; import android.text.style.ParagraphStyle; @@ -125,11 +132,6 @@ import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.widget.RemoteViews.RemoteView; -import com.android.internal.util.FastMath; -import com.android.internal.widget.EditableInputConnection; - -import org.xmlpull.v1.XmlPullParserException; - import java.io.IOException; import java.lang.ref.WeakReference; import java.text.BreakIterator; @@ -424,6 +426,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int textSize = 15; int typefaceIndex = -1; int styleIndex = -1; + boolean allCaps = false; /* * Look the appearance up without checking first if it exists because @@ -471,6 +474,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case com.android.internal.R.styleable.TextAppearance_textStyle: styleIndex = appearance.getInt(attr, -1); break; + + case com.android.internal.R.styleable.TextAppearance_textAllCaps: + allCaps = appearance.getBoolean(attr, false); + break; } } @@ -822,6 +829,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case com.android.internal.R.styleable.TextView_suggestionsEnabled: mSuggestionsEnabled = a.getBoolean(attr, true); break; + + case com.android.internal.R.styleable.TextView_textAllCaps: + allCaps = a.getBoolean(attr, false); + break; } } a.recycle(); @@ -1004,6 +1015,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } setRawTextSize(textSize); + if (allCaps) { + setTransformationMethod(new AllCapsTransformationMethod(getContext())); + } + if (password || passwordInputType || webPasswordInputType || numberPasswordInputType) { setTransformationMethod(PasswordTransformationMethod.getInstance()); typefaceIndex = MONOSPACE; @@ -1331,6 +1346,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mTransformation = method; + if (method instanceof TransformationMethod2) { + TransformationMethod2 method2 = (TransformationMethod2) method; + mAllowTransformationLengthChange = !mTextIsSelectable && !(mText instanceof Editable); + method2.setLengthChangesAllowed(mAllowTransformationLengthChange); + } else { + mAllowTransformationLengthChange = false; + } + setText(mText); } @@ -1775,6 +1798,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener setTypefaceByIndex(typefaceIndex, styleIndex); + if (appearance.getBoolean(com.android.internal.R.styleable.TextAppearance_textAllCaps, + false)) { + setTransformationMethod(new AllCapsTransformationMethod(getContext())); + } + appearance.recycle(); } @@ -2823,14 +2851,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mBufferType = type; mText = text; - if (mTransformation == null) + if (mTransformation == null) { mTransformed = text; - else + } else { mTransformed = mTransformation.getTransformation(text, this); + } final int textLength = text.length(); - if (text instanceof Spannable) { + if (text instanceof Spannable && !mAllowTransformationLengthChange) { Spannable sp = (Spannable) text; // Remove any ChangeWatchers that might have come @@ -2852,7 +2881,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mTransformation != null) { sp.setSpan(mTransformation, 0, textLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE); - } if (mMovement != null) { @@ -6570,6 +6598,26 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * Sets the properties of this field to transform input to ALL CAPS + * display. This may use a "small caps" formatting if available. + * This setting will be ignored if this field is editable or selectable. + * + * This call replaces the current transformation method. Disabling this + * will not necessarily restore the previous behavior from before this + * was enabled. + * + * @see #setTransformationMethod(TransformationMethod) + * @attr ref android.R.styleable#TextView_textAllCaps + */ + public void setAllCaps(boolean allCaps) { + if (allCaps) { + setTransformationMethod(new AllCapsTransformationMethod(getContext())); + } else { + setTransformationMethod(null); + } + } + + /** * If true, sets the properties of this field (number of lines, horizontally scrolling, * transformation method) to be for a single-line input; if false, restores these to the default * conditions. @@ -10254,6 +10302,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private MovementMethod mMovement; private TransformationMethod mTransformation; + private boolean mAllowTransformationLengthChange; private ChangeWatcher mChangeWatcher; private ArrayList<TextWatcher> mListeners = null; diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java index 2e7ec58..b754d94 100644 --- a/core/java/com/android/internal/util/Protocol.java +++ b/core/java/com/android/internal/util/Protocol.java @@ -26,6 +26,9 @@ package com.android.internal.util; * codes with Message.what starting at Protocol.WIFI + 1 and less than or equal to Protocol.WIFI + * Protocol.MAX_MESSAGE * + * NOTE: After a value is created and source released a value shouldn't be changed to + * maintain backwards compatibility. + * * {@hide} */ public class Protocol { @@ -40,7 +43,7 @@ public class Protocol { public static final int BASE_DHCP = 0x00030000; public static final int BASE_DATA_CONNECTION = 0x00040000; public static final int BASE_DATA_CONNECTION_AC = 0x00041000; - public static final int BASE_DATA_CONNECTION_TRACKER = 0x00050000; + public static final int BASE_DATA_CONNECTION_TRACKER = 0x00042000; //TODO: define all used protocols } diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java index 3b497e4..09bebae 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuItemView.java +++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java @@ -154,12 +154,7 @@ public class ActionMenuItemView extends LinearLayout // populate accessibility description with title setContentDescription(title); - if (mShowTextAllCaps && title != null) { - mTextButton.setText(title.toString().toUpperCase( - getContext().getResources().getConfiguration().locale)); - } else { - mTextButton.setText(mTitle); - } + mTextButton.setText(mTitle); updateTextButtonVisibility(); } diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java index 2f7adf0..40e5e8a 100644 --- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java +++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java @@ -260,7 +260,6 @@ public class ScrollingTabContainerView extends HorizontalScrollView { if (mTextView == null) { TextView textView = new TextView(getContext(), null, com.android.internal.R.attr.actionBarTabTextStyle); - textView.setSingleLine(); textView.setEllipsize(TruncateAt.END); LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index 2106eb4..681f43f 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -115,6 +115,18 @@ static jboolean android_view_GLES20Canvas_isBackBufferPreserved(JNIEnv* env, job return error == EGL_SUCCESS && value == EGL_BUFFER_PRESERVED; } +static void android_view_GLES20Canvas_disableVsync(JNIEnv* env, jobject clazz) { + EGLDisplay display = eglGetCurrentDisplay(); + + eglGetError(); + eglSwapInterval(display, 0); + + EGLint error = eglGetError(); + if (error != EGL_SUCCESS) { + RENDERER_LOGD("Could not disable v-sync (%x)", error); + } +} + // ---------------------------------------------------------------------------- // Constructors // ---------------------------------------------------------------------------- @@ -621,7 +633,7 @@ static Layer* android_view_GLES20Canvas_createTextureLayer(JNIEnv* env, jobject if (layer) { jint* storage = env->GetIntArrayElements(layerInfo, NULL); - storage[0] = layer->texture; + storage[0] = layer->getTexture(); env->ReleaseIntArrayElements(layerInfo, storage, 0); } @@ -634,8 +646,8 @@ static Layer* android_view_GLES20Canvas_createLayer(JNIEnv* env, jobject clazz, if (layer) { jint* storage = env->GetIntArrayElements(layerInfo, NULL); - storage[0] = layer->width; - storage[1] = layer->height; + storage[0] = layer->getWidth(); + storage[1] = layer->getHeight(); env->ReleaseIntArrayElements(layerInfo, storage, 0); } @@ -647,8 +659,8 @@ static void android_view_GLES20Canvas_resizeLayer(JNIEnv* env, jobject clazz, LayerRenderer::resizeLayer(layer, width, height); jint* storage = env->GetIntArrayElements(layerInfo, NULL); - storage[0] = layer->width; - storage[1] = layer->height; + storage[0] = layer->getWidth(); + storage[1] = layer->getHeight(); env->ReleaseIntArrayElements(layerInfo, storage, 0); } @@ -722,6 +734,7 @@ static JNINativeMethod gMethods[] = { #ifdef USE_OPENGL_RENDERER { "nIsBackBufferPreserved", "()Z", (void*) android_view_GLES20Canvas_isBackBufferPreserved }, { "nPreserveBackBuffer", "()Z", (void*) android_view_GLES20Canvas_preserveBackBuffer }, + { "nDisableVsync", "()V", (void*) android_view_GLES20Canvas_disableVsync }, { "nCreateRenderer", "()I", (void*) android_view_GLES20Canvas_createRenderer }, { "nDestroyRenderer", "(I)V", (void*) android_view_GLES20Canvas_destroyRenderer }, diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 2cc8171..39de054 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2810,6 +2810,8 @@ <attr name="textColorHint" /> <!-- Color of the links. --> <attr name="textColorLink" /> + <!-- Present the text in ALL CAPS. This may use a small-caps form when available. --> + <attr name="textAllCaps" format="boolean" /> </declare-styleable> <declare-styleable name="TextSwitcher"> </declare-styleable> @@ -3074,6 +3076,8 @@ <attr name="textIsSelectable" /> <!-- Suggestions will be displayed when the user double taps on editable text. --> <attr name="suggestionsEnabled" /> + <!-- Present the text in ALL CAPS. This may use a small-caps form when available. --> + <attr name="textAllCaps" /> </declare-styleable> <!-- An <code>input-extras</code> is a container for extra data to supply to an input method. Contains diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 2de9cd3..3c23add 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1779,6 +1779,8 @@ <public type="attr" name="backgroundStacked" /> <public type="attr" name="backgroundSplit" /> + <public type="attr" name="textAllCaps" /> + <public type="style" name="TextAppearance.SuggestionHighlight" /> <public type="style" name="Theme.Holo.SplitActionBarWhenNarrow" /> <public type="style" name="Theme.Holo.Light.SplitActionBarWhenNarrow" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 50f8df7..97a8c0b 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2085,6 +2085,10 @@ <!-- Do not translate. Regex used by AutoFill. --> <string name="autofill_phone_suffix_separator_re">^-$</string> + <!-- Do not translate. Regex used by AutoFill. --> + <!-- Ex: ************1234 --> + <string name="credit_card_number_preview_format">$1</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_readHistoryBookmarks">read Browser\'s history and bookmarks</string> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 11016f4..8a8f81a 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1319,6 +1319,7 @@ <item name="android:textSize">12sp</item> <item name="android:textStyle">bold</item> <item name="android:textColor">?android:attr/actionMenuTextColor</item> + <item name="android:textAllCaps">true</item> </style> <style name="TextAppearance.Holo.Widget.ActionMode"> @@ -1857,6 +1858,7 @@ <item name="android:textColor">?android:attr/textColorPrimary</item> <item name="android:textSize">12sp</item> <item name="android:textStyle">bold</item> + <item name="android:textAllCaps">true</item> </style> <style name="Widget.Holo.ActionMode" parent="Widget.ActionMode"> |