diff options
Diffstat (limited to 'core')
47 files changed, 1094 insertions, 543 deletions
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index 473a2d1..f427e78 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -181,7 +181,8 @@ final class FragmentState implements Parcelable { * * While the Fragment API was introduced in * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, a version of the API - * is also available for use on older platforms. See the blog post + * at is also available for use on older platforms through + * {@link android.support.v4.app.FragmentActivity}. See the blog post * <a href="http://android-developers.blogspot.com/2011/03/fragments-for-all.html"> * Fragments For All</a> for more details. * diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index a8c9cba..1abb7de 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -51,6 +51,13 @@ import java.util.Arrays; * <p>For more information about using fragments, read the * <a href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a> developer guide.</p> * </div> + * + * While the FragmentManager API was introduced in + * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, a version of the API + * at is also available for use on older platforms through + * {@link android.support.v4.app.FragmentActivity}. See the blog post + * <a href="http://android-developers.blogspot.com/2011/03/fragments-for-all.html"> + * Fragments For All</a> for more details. */ public abstract class FragmentManager { /** diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java index 1b8a4f5..d83d2e6 100644 --- a/core/java/android/app/LoaderManager.java +++ b/core/java/android/app/LoaderManager.java @@ -36,7 +36,8 @@ import java.lang.reflect.Modifier; * * While the LoaderManager API was introduced in * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, a version of the API - * is also available for use on older platforms. See the blog post + * at is also available for use on older platforms through + * {@link android.support.v4.app.FragmentActivity}. See the blog post * <a href="http://android-developers.blogspot.com/2011/03/fragments-for-all.html"> * Fragments For All</a> for more details. * 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/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java index d71a8d6..b609c26 100644 --- a/core/java/android/content/BroadcastReceiver.java +++ b/core/java/android/content/BroadcastReceiver.java @@ -28,11 +28,21 @@ import android.util.Slog; /** * Base class for code that will receive intents sent by sendBroadcast(). - * You can either dynamically register an instance of this class with + * + * <p>If you don't need to send broadcasts across applications, consider using + * this class with {@link android.support.v4.content.LocalBroadcastManager} instead + * of the more general facilities described below. This will give you a much + * more efficient implementation (no cross-process communication needed) and allow + * you to avoid thinking about any security issues related to other applications + * being able to receive or send your broadcasts. + * + * <p>You can either dynamically register an instance of this class with * {@link Context#registerReceiver Context.registerReceiver()} * or statically publish an implementation through the * {@link android.R.styleable#AndroidManifestReceiver <receiver>} - * tag in your <code>AndroidManifest.xml</code>. <em><strong>Note:</strong></em> + * tag in your <code>AndroidManifest.xml</code>. + * + * <p><em><strong>Note:</strong></em> * If registering a receiver in your * {@link android.app.Activity#onResume() Activity.onResume()} * implementation, you should unregister it in @@ -86,8 +96,8 @@ import android.util.Slog; * * <p>Topics covered here: * <ol> + * <li><a href="#Security">Security</a> * <li><a href="#ReceiverLifecycle">Receiver Lifecycle</a> - * <li><a href="#Permissions">Permissions</a> * <li><a href="#ProcessLifecycle">Process Lifecycle</a> * </ol> * @@ -98,32 +108,39 @@ import android.util.Slog; * developer guide.</p> * </div> * - * <a name="ReceiverLifecycle"></a> - * <h3>Receiver Lifecycle</h3> - * - * <p>A BroadcastReceiver object is only valid for the duration of the call - * to {@link #onReceive}. Once your code returns from this function, - * the system considers the object to be finished and no longer active. - * - * <p>This has important repercussions to what you can do in an - * {@link #onReceive} implementation: anything that requires asynchronous - * operation is not available, because you will need to return from the - * function to handle the asynchronous operation, but at that point the - * BroadcastReceiver is no longer active and thus the system is free to kill - * its process before the asynchronous operation completes. - * - * <p>In particular, you may <i>not</i> show a dialog or bind to a service from - * within a BroadcastReceiver. For the former, you should instead use the - * {@link android.app.NotificationManager} API. For the latter, you can - * use {@link android.content.Context#startService Context.startService()} to - * send a command to the service. - * - * <a name="Permissions"></a> - * <h3>Permissions</h3> - * + * <a name="Security"></a> + * <h3>Security</h3> + * + * <p>Receivers used with the {@link Context} APIs are by their nature a + * cross-application facility, so you must consider how other applications + * may be able to abuse your use of them. Some things to consider are: + * + * <ul> + * <li><p>The Intent namespace is global. Make sure that Intent action names and + * other strings are written in a namespace you own, or else you may inadvertantly + * conflict with other applications. + * <li><p>When you use {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)}, + * <em>any</em> application may send broadcasts to that registered receiver. You can + * control who can send broadcasts to it through permissions described below. + * <li><p>When you publish a receiver in your application's manifest and specify + * intent-filters for it, any other application can send broadcasts to it regardless + * of the filters you specify. To prevent others from sending to it, make it + * unavailable to them with <code>android:exported="false"</code>. + * <li><p>When you use {@link Context#sendBroadcast(Intent)} or related methods, + * normally any other application can receive these broadcasts. You can control who + * can receive such broadcasts through permissions described below. Alternatively, + * starting with {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}, you + * can also safely restrict the broadcast to a single application with + * {@link Intent#setPackage(String) Intent.setPackage} + * </ul> + * + * <p>None of these issues exist when using + * {@link android.support.v4.content.LocalBroadcastManager}, since intents + * broadcast it never go outside of the current process. + * * <p>Access permissions can be enforced by either the sender or receiver - * of an Intent. - * + * of a broadcast. + * * <p>To enforce a permission when sending, you supply a non-null * <var>permission</var> argument to * {@link Context#sendBroadcast(Intent, String)} or @@ -133,7 +150,7 @@ import android.util.Slog; * {@link android.R.styleable#AndroidManifestUsesPermission <uses-permission>} * tag in their <code>AndroidManifest.xml</code>) will be able to receive * the broadcast. - * + * * <p>To enforce a permission when receiving, you supply a non-null * <var>permission</var> when registering your receiver -- either when calling * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter, String, android.os.Handler)} @@ -144,10 +161,30 @@ import android.util.Slog; * {@link android.R.styleable#AndroidManifestUsesPermission <uses-permission>} * tag in their <code>AndroidManifest.xml</code>) will be able to send an * Intent to the receiver. - * + * * <p>See the <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a> * document for more information on permissions and security in general. + * + * <a name="ReceiverLifecycle"></a> + * <h3>Receiver Lifecycle</h3> + * + * <p>A BroadcastReceiver object is only valid for the duration of the call + * to {@link #onReceive}. Once your code returns from this function, + * the system considers the object to be finished and no longer active. + * + * <p>This has important repercussions to what you can do in an + * {@link #onReceive} implementation: anything that requires asynchronous + * operation is not available, because you will need to return from the + * function to handle the asynchronous operation, but at that point the + * BroadcastReceiver is no longer active and thus the system is free to kill + * its process before the asynchronous operation completes. * + * <p>In particular, you may <i>not</i> show a dialog or bind to a service from + * within a BroadcastReceiver. For the former, you should instead use the + * {@link android.app.NotificationManager} API. For the latter, you can + * use {@link android.content.Context#startService Context.startService()} to + * send a command to the service. + * * <a name="ProcessLifecycle"></a> * <h3>Process Lifecycle</h3> * diff --git a/core/java/android/content/Loader.java b/core/java/android/content/Loader.java index 2d2a90d..ac05682 100644 --- a/core/java/android/content/Loader.java +++ b/core/java/android/content/Loader.java @@ -58,6 +58,13 @@ public class Loader<D> { boolean mReset = true; boolean mContentChanged = false; + /** + * An implementation of a ContentObserver that takes care of connecting + * it to the Loader to have the loader re-load its data when the observer + * is told it has changed. You do not normally need to use this yourself; + * it is used for you by {@link CursorLoader} to take care of executing + * an update when the cursor's backing data changes. + */ public final class ForceLoadContentObserver extends ContentObserver { public ForceLoadContentObserver() { super(new Handler()); @@ -74,6 +81,14 @@ public class Loader<D> { } } + /** + * Interface that is implemented to discover when a Loader has finished + * loading its data. You do not normally need to implement this yourself; + * it is used in the implementation of {@link android.app.LoaderManager} + * to find out when a Loader it is managing has completed so that this can + * be reported to its client. This interface should only be used if a + * Loader is not being used in conjunction with LoaderManager. + */ public interface OnLoadCompleteListener<D> { /** * Called on the thread that created the Loader when the load is complete. 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/server/BluetoothAdapterStateMachine.java b/core/java/android/server/BluetoothAdapterStateMachine.java index f4a390e..ed59b03 100644 --- a/core/java/android/server/BluetoothAdapterStateMachine.java +++ b/core/java/android/server/BluetoothAdapterStateMachine.java @@ -360,13 +360,13 @@ final class BluetoothAdapterStateMachine extends StateMachine { boolean retValue = HANDLED; switch(message.what) { case USER_TURN_ON: + broadcastState(BluetoothAdapter.STATE_TURNING_ON); if ((Boolean) message.obj) { persistSwitchSetting(true); } // let it fall to TURN_ON_CONTINUE: //$FALL-THROUGH$ case TURN_ON_CONTINUE: - broadcastState(BluetoothAdapter.STATE_TURNING_ON); mBluetoothService.switchConnectable(true); transitionTo(mSwitching); break; diff --git a/core/java/android/service/textservice/SpellCheckerService.java b/core/java/android/service/textservice/SpellCheckerService.java index 5282e61..2b8a458 100644 --- a/core/java/android/service/textservice/SpellCheckerService.java +++ b/core/java/android/service/textservice/SpellCheckerService.java @@ -24,6 +24,7 @@ import android.app.Service; import android.content.Intent; import android.os.Bundle; import android.os.IBinder; +import android.os.Process; import android.os.RemoteException; import android.util.Log; import android.view.textservice.SuggestionsInfo; @@ -206,11 +207,15 @@ public abstract class SpellCheckerService extends Service { @Override public void onGetSuggestionsMultiple( TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { + int pri = Process.getThreadPriority(Process.myTid()); try { + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); mListener.onGetSuggestions( mSession.onGetSuggestionsMultiple( textInfos, suggestionsLimit, sequentialWords)); } catch (RemoteException e) { + } finally { + Process.setThreadPriority(pri); } } @@ -226,13 +231,25 @@ public abstract class SpellCheckerService extends Service { @Override public void onCancel() { - mSession.onCancel(); + int pri = Process.getThreadPriority(Process.myTid()); + try { + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + mSession.onCancel(); + } finally { + Process.setThreadPriority(pri); + } } @Override public void onClose() { - mSession.onClose(); - mListener = null; + int pri = Process.getThreadPriority(Process.myTid()); + try { + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + mSession.onClose(); + } finally { + Process.setThreadPriority(pri); + mListener = null; + } } public String getLocale() { 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/TextLine.java b/core/java/android/text/TextLine.java index b73d900..1e8a2f7 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -59,6 +59,12 @@ class TextLine { private boolean mCharsValid; private Spanned mSpanned; private final TextPaint mWorkPaint = new TextPaint(); + private final SpanSet<MetricAffectingSpan> mMetricAffectingSpanSpanSet = + new SpanSet<MetricAffectingSpan>(MetricAffectingSpan.class); + private final SpanSet<CharacterStyle> mCharacterStyleSpanSet = + new SpanSet<CharacterStyle>(CharacterStyle.class); + private final SpanSet<ReplacementSpan> mReplacementSpanSpanSet = + new SpanSet<ReplacementSpan>(ReplacementSpan.class); private static final TextLine[] sCached = new TextLine[3]; @@ -96,6 +102,11 @@ class TextLine { tl.mText = null; tl.mPaint = null; tl.mDirections = null; + + tl.mMetricAffectingSpanSpanSet.recycle(); + tl.mCharacterStyleSpanSet.recycle(); + tl.mReplacementSpanSpanSet.recycle(); + synchronized(sCached) { for (int i = 0; i < sCached.length; ++i) { if (sCached[i] == null) { @@ -119,7 +130,6 @@ class TextLine { * @param hasTabs true if the line might contain tabs or emoji * @param tabStops the tabStops. Can be null. */ - @SuppressWarnings("null") void set(TextPaint paint, CharSequence text, int start, int limit, int dir, Directions directions, boolean hasTabs, TabStops tabStops) { mPaint = paint; @@ -135,12 +145,10 @@ class TextLine { mSpanned = null; boolean hasReplacement = false; - SpanSet<ReplacementSpan> replacementSpans = null; if (text instanceof Spanned) { mSpanned = (Spanned) text; - replacementSpans = new SpanSet<ReplacementSpan>(mSpanned, start, limit, - ReplacementSpan.class); - hasReplacement = replacementSpans.numberOfSpans > 0; + mReplacementSpanSpanSet.init(mSpanned, start, limit); + hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0; } mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT; @@ -158,9 +166,8 @@ class TextLine { // zero-width characters. char[] chars = mChars; for (int i = start, inext; i < limit; i = inext) { - // replacementSpans cannot be null if hasReplacement is true - inext = replacementSpans.getNextTransition(i, limit); - if (replacementSpans.hasSpansIntersecting(i, inext)) { + inext = mReplacementSpanSpanSet.getNextTransition(i, limit); + if (mReplacementSpanSpanSet.hasSpansIntersecting(i, inext)) { // transition into a span chars[i - start] = '\ufffc'; for (int j = i - start + 1, e = inext - start; j < e; ++j) { @@ -854,23 +861,32 @@ class TextLine { } private static class SpanSet<E> { - final int numberOfSpans; - final E[] spans; - final int[] spanStarts; - final int[] spanEnds; - final int[] spanFlags; + int numberOfSpans; + E[] spans; + int[] spanStarts; + int[] spanEnds; + int[] spanFlags; + final Class<? extends E> classType; + + SpanSet(Class<? extends E> type) { + classType = type; + numberOfSpans = 0; + } @SuppressWarnings("unchecked") - SpanSet(Spanned spanned, int start, int limit, Class<? extends E> type) { - final E[] allSpans = spanned.getSpans(start, limit, type); + public void init(Spanned spanned, int start, int limit) { + final E[] allSpans = spanned.getSpans(start, limit, classType); final int length = allSpans.length; - // These arrays may end up being too large because of empty spans - spans = (E[]) Array.newInstance(type, length); - spanStarts = new int[length]; - spanEnds = new int[length]; - spanFlags = new int[length]; - int count = 0; + if (length > 0 && (spans == null || spans.length < length)) { + // These arrays may end up being too large because of empty spans + spans = (E[]) Array.newInstance(classType, length); + spanStarts = new int[length]; + spanEnds = new int[length]; + spanFlags = new int[length]; + } + + numberOfSpans = 0; for (int i = 0; i < length; i++) { final E span = allSpans[i]; @@ -879,34 +895,14 @@ class TextLine { if (spanStart == spanEnd) continue; final int spanFlag = spanned.getSpanFlags(span); - final int priority = spanFlag & Spanned.SPAN_PRIORITY; - if (priority != 0 && count != 0) { - int j; - - for (j = 0; j < count; j++) { - final int otherPriority = spanFlags[j] & Spanned.SPAN_PRIORITY; - if (priority > otherPriority) break; - } - System.arraycopy(spans, j, spans, j + 1, count - j); - System.arraycopy(spanStarts, j, spanStarts, j + 1, count - j); - System.arraycopy(spanEnds, j, spanEnds, j + 1, count - j); - System.arraycopy(spanFlags, j, spanFlags, j + 1, count - j); + spans[numberOfSpans] = span; + spanStarts[numberOfSpans] = spanStart; + spanEnds[numberOfSpans] = spanEnd; + spanFlags[numberOfSpans] = spanFlag; - spans[j] = span; - spanStarts[j] = spanStart; - spanEnds[j] = spanEnd; - spanFlags[j] = spanFlag; - } else { - spans[i] = span; - spanStarts[i] = spanStart; - spanEnds[i] = spanEnd; - spanFlags[i] = spanFlag; - } - - count++; + numberOfSpans++; } - numberOfSpans = count; } public boolean hasSpansIntersecting(int start, int end) { @@ -927,6 +923,13 @@ class TextLine { } return limit; } + + public void recycle() { + // The spans array is guaranteed to be not null when numberOfSpans is > 0 + for (int i = 0; i < numberOfSpans; i++) { + spans[i] = null; // prevent a leak: no reference kept when TextLine is recycled + } + } } /** @@ -970,10 +973,8 @@ class TextLine { y, bottom, fmi, needWidth || mlimit < measureLimit); } - final SpanSet<MetricAffectingSpan> metricAffectingSpans = new SpanSet<MetricAffectingSpan>( - mSpanned, mStart + start, mStart + limit, MetricAffectingSpan.class); - final SpanSet<CharacterStyle> characterStyleSpans = new SpanSet<CharacterStyle>( - mSpanned, mStart + start, mStart + limit, CharacterStyle.class); + mMetricAffectingSpanSpanSet.init(mSpanned, mStart + start, mStart + limit); + mCharacterStyleSpanSet.init(mSpanned, mStart + start, mStart + limit); // Shaping needs to take into account context up to metric boundaries, // but rendering needs to take into account character style boundaries. @@ -985,17 +986,18 @@ class TextLine { TextPaint wp = mWorkPaint; wp.set(mPaint); - inext = metricAffectingSpans.getNextTransition(mStart + i, mStart + limit) - mStart; + inext = mMetricAffectingSpanSpanSet.getNextTransition(mStart + i, mStart + limit) - + mStart; int mlimit = Math.min(inext, measureLimit); ReplacementSpan replacement = null; - for (int j = 0; j < metricAffectingSpans.numberOfSpans; j++) { + for (int j = 0; j < mMetricAffectingSpanSpanSet.numberOfSpans; j++) { // Both intervals [spanStarts..spanEnds] and [mStart + i..mStart + mlimit] are NOT // empty by construction. This special case in getSpans() explains the >= & <= tests - if ((metricAffectingSpans.spanStarts[j] >= mStart + mlimit) || - (metricAffectingSpans.spanEnds[j] <= mStart + i)) continue; - MetricAffectingSpan span = metricAffectingSpans.spans[j]; + if ((mMetricAffectingSpanSpanSet.spanStarts[j] >= mStart + mlimit) || + (mMetricAffectingSpanSpanSet.spanEnds[j] <= mStart + i)) continue; + MetricAffectingSpan span = mMetricAffectingSpanSpanSet.spans[j]; if (span instanceof ReplacementSpan) { replacement = (ReplacementSpan)span; } else { @@ -1016,16 +1018,16 @@ class TextLine { y, bottom, fmi, needWidth || mlimit < measureLimit); } else { for (int j = i, jnext; j < mlimit; j = jnext) { - jnext = characterStyleSpans.getNextTransition(mStart + j, mStart + mlimit) - + jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + mlimit) - mStart; wp.set(mPaint); - for (int k = 0; k < characterStyleSpans.numberOfSpans; k++) { + for (int k = 0; k < mCharacterStyleSpanSet.numberOfSpans; k++) { // Intentionally using >= and <= as explained above - if ((characterStyleSpans.spanStarts[k] >= mStart + jnext) || - (characterStyleSpans.spanEnds[k] <= mStart + j)) continue; + if ((mCharacterStyleSpanSet.spanStarts[k] >= mStart + jnext) || + (mCharacterStyleSpanSet.spanEnds[k] <= mStart + j)) continue; - CharacterStyle span = characterStyleSpans.spans[k]; + CharacterStyle span = mCharacterStyleSpanSet.spans[k]; span.updateDrawState(wp); } diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java index 5ae65df..121c6f2 100644 --- a/core/java/android/text/format/Formatter.java +++ b/core/java/android/text/format/Formatter.java @@ -21,7 +21,7 @@ import android.net.NetworkUtils; /** * Utility class to aid in formatting common values that are not covered - * by {@link java.util.Formatter} + * by the {@link java.util.Formatter} class in {@link java.util} */ public final class Formatter { diff --git a/core/java/android/text/format/package.html b/core/java/android/text/format/package.html new file mode 100644 index 0000000..b9e6a44 --- /dev/null +++ b/core/java/android/text/format/package.html @@ -0,0 +1,7 @@ +<HTML> +<BODY> +This package contains alternative classes for some text formatting classes +defined in {@link java.util} and {@link java.text}. It also contains additional text formatting +classes for situations not covered by {@link java.util} or {@link java.text}. +</BODY> +</HTML> 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/ViewGroup.java b/core/java/android/view/ViewGroup.java index 1102a47..0d34b90 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -4190,10 +4190,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * {@inheritDoc} */ public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) { - // The View is not attached to a window, 'visible' does not make sense, return false - if (mAttachInfo == null) return false; - - final RectF rect = mAttachInfo.mTmpTransformRect; + // It doesn't make a whole lot of sense to call this on a view that isn't attached, + // but for some simple tests it can be useful. If we don't have attach info this + // will allocate memory. + final RectF rect = mAttachInfo != null ? mAttachInfo.mTmpTransformRect : new RectF(); rect.set(r); if (!child.hasIdentityMatrix()) { @@ -4207,7 +4207,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (offset != null) { if (!child.hasIdentityMatrix()) { - float[] position = mAttachInfo.mTmpTransformLocation; + float[] position = mAttachInfo != null ? mAttachInfo.mTmpTransformLocation + : new float[2]; position[0] = offset.x; position[1] = offset.y; child.getMatrix().mapPoints(position); 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/view/VolumePanel.java b/core/java/android/view/VolumePanel.java index 48fe0df..24a3066 100644 --- a/core/java/android/view/VolumePanel.java +++ b/core/java/android/view/VolumePanel.java @@ -400,7 +400,7 @@ public class VolumePanel extends Handler implements OnSeekBarChangeListener, Vie if (LOGD) Log.d(TAG, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")"); if ((flags & AudioManager.FLAG_SHOW_UI) != 0) { - if (mActiveStreamType == -1) { + if (mActiveStreamType != streamType) { reorderSliders(streamType); } onShowVolumeChanged(streamType, flags); diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index d0841dd..a99ac03 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -743,6 +743,9 @@ public abstract class Window { public void setFlags(int flags, int mask) { final WindowManager.LayoutParams attrs = getAttributes(); attrs.flags = (attrs.flags&~mask) | (flags&mask); + if ((mask&WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY) != 0) { + attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SET_NEEDS_MENU_KEY; + } mForcedWindowFlags |= mask; if (mCallback != null) { mCallback.onWindowAttributesChanged(attrs); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index e74fec6..c0eb65b 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -824,6 +824,16 @@ public interface WindowManager extends ViewManager { public static final int PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS = 0x00000004; /** + * This is set for a window that has explicitly specified its + * FLAG_NEEDS_MENU_KEY, so we know the value on this window is the + * appropriate one to use. If this is not set, we should look at + * windows behind it to determine the appropriate value. + * + * @hide + */ + public static final int PRIVATE_FLAG_SET_NEEDS_MENU_KEY = 0x00000008; + + /** * Control flags that are private to the platform. * @hide */ diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 7d729c6..c7cd662 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -238,6 +238,14 @@ public interface WindowManagerPolicy { public WindowManager.LayoutParams getAttrs(); /** + * Return whether this window needs the menu key shown. Must be called + * with window lock held, because it may need to traverse down through + * window list to determine the result. + * @param bottom The bottom-most window to consider when determining this. + */ + public boolean getNeedsMenuLw(WindowState bottom); + + /** * Retrieve the current system UI visibility flags associated with * this window. */ diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java index 1d66cbe..f6418ce 100644 --- a/core/java/android/view/textservice/SpellCheckerSession.java +++ b/core/java/android/view/textservice/SpellCheckerSession.java @@ -21,8 +21,11 @@ import com.android.internal.textservice.ISpellCheckerSessionListener; import com.android.internal.textservice.ITextServicesManager; import com.android.internal.textservice.ITextServicesSessionListener; +import android.os.Binder; import android.os.Handler; +import android.os.HandlerThread; import android.os.Message; +import android.os.Process; import android.os.RemoteException; import android.util.Log; import android.view.textservice.SpellCheckerInfo; @@ -226,6 +229,8 @@ public class SpellCheckerSession { private boolean mOpened; private ISpellCheckerSession mISpellCheckerSession; + private HandlerThread mThread; + private Handler mAsyncHandler; public SpellCheckerSessionListenerImpl(Handler handler) { mOpened = false; @@ -237,6 +242,7 @@ public class SpellCheckerSession { public final TextInfo[] mTextInfos; public final int mSuggestionsLimit; public final boolean mSequentialWords; + public ISpellCheckerSession mSession; public SpellCheckerParams(int what, TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) { mWhat = what; @@ -246,30 +252,100 @@ public class SpellCheckerSession { } } - private void processTask(SpellCheckerParams scp) { - switch (scp.mWhat) { - case TASK_CANCEL: - processCancel(); - break; - case TASK_GET_SUGGESTIONS_MULTIPLE: - processGetSuggestionsMultiple(scp); - break; - case TASK_CLOSE: - processClose(); - break; - case TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE: - processGetSuggestionsMultipleForSentence(scp); - break; + private void processTask(ISpellCheckerSession session, SpellCheckerParams scp, + boolean async) { + if (async || mAsyncHandler == null) { + switch (scp.mWhat) { + case TASK_CANCEL: + if (DBG) { + Log.w(TAG, "Cancel spell checker tasks."); + } + try { + session.onCancel(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to cancel " + e); + } + break; + case TASK_GET_SUGGESTIONS_MULTIPLE: + if (DBG) { + Log.w(TAG, "Get suggestions from the spell checker."); + } + try { + session.onGetSuggestionsMultiple(scp.mTextInfos, + scp.mSuggestionsLimit, scp.mSequentialWords); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get suggestions " + e); + } + break; + case TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE: + if (DBG) { + Log.w(TAG, "Get suggestions from the spell checker."); + } + if (scp.mTextInfos.length != 1) { + throw new IllegalArgumentException(); + } + try { + session.onGetSuggestionsMultipleForSentence( + scp.mTextInfos, scp.mSuggestionsLimit); + } catch (RemoteException e) { + Log.e(TAG, "Failed to get suggestions " + e); + } + break; + case TASK_CLOSE: + if (DBG) { + Log.w(TAG, "Close spell checker tasks."); + } + try { + session.onClose(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to close " + e); + } + break; + } + } else { + // The interface is to a local object, so need to execute it + // asynchronously. + scp.mSession = session; + mAsyncHandler.sendMessage(Message.obtain(mAsyncHandler, 1, scp)); + } + + if (scp.mWhat == TASK_CLOSE) { + // If we are closing, we want to clean up our state now even + // if it is pending as an async operation. + synchronized (this) { + mISpellCheckerSession = null; + mHandler = null; + if (mThread != null) { + mThread.quit(); + } + mThread = null; + mAsyncHandler = null; + } } } public synchronized void onServiceConnected(ISpellCheckerSession session) { - mISpellCheckerSession = session; - mOpened = true; + synchronized (this) { + mISpellCheckerSession = session; + if (session.asBinder() instanceof Binder && mThread == null) { + // If this is a local object, we need to do our own threading + // to make sure we handle it asynchronously. + mThread = new HandlerThread("SpellCheckerSession", + Process.THREAD_PRIORITY_BACKGROUND); + mThread.start(); + mAsyncHandler = new Handler(mThread.getLooper()) { + @Override public void handleMessage(Message msg) { + SpellCheckerParams scp = (SpellCheckerParams)msg.obj; + processTask(scp.mSession, scp, true); + } + }; + } + mOpened = true; + } if (DBG) Log.d(TAG, "onServiceConnected - Success"); while (!mPendingTasks.isEmpty()) { - processTask(mPendingTasks.poll()); + processTask(session, mPendingTasks.poll(), false); } } @@ -310,105 +386,43 @@ public class SpellCheckerSession { return mOpened && mISpellCheckerSession == null; } - public boolean checkOpenConnection() { - if (mISpellCheckerSession != null) { - return true; - } - Log.e(TAG, "not connected to the spellchecker service."); - return false; - } - private void processOrEnqueueTask(SpellCheckerParams scp) { if (DBG) { Log.d(TAG, "process or enqueue task: " + mISpellCheckerSession); } - SpellCheckerParams closeTask = null; - if (mISpellCheckerSession == null) { - if (scp.mWhat == TASK_CANCEL) { - while (!mPendingTasks.isEmpty()) { - final SpellCheckerParams tmp = mPendingTasks.poll(); - if (tmp.mWhat == TASK_CLOSE) { - // Only one close task should be processed, while we need to remove all - // close tasks from the queue - closeTask = tmp; + ISpellCheckerSession session; + synchronized (this) { + session = mISpellCheckerSession; + if (session == null) { + SpellCheckerParams closeTask = null; + if (scp.mWhat == TASK_CANCEL) { + while (!mPendingTasks.isEmpty()) { + final SpellCheckerParams tmp = mPendingTasks.poll(); + if (tmp.mWhat == TASK_CLOSE) { + // Only one close task should be processed, while we need to remove all + // close tasks from the queue + closeTask = tmp; + } } } + mPendingTasks.offer(scp); + if (closeTask != null) { + mPendingTasks.offer(closeTask); + } + return; } - mPendingTasks.offer(scp); - if (closeTask != null) { - mPendingTasks.offer(closeTask); - } - } else { - processTask(scp); - } - } - - private void processCancel() { - if (!checkOpenConnection()) { - return; - } - if (DBG) { - Log.w(TAG, "Cancel spell checker tasks."); - } - try { - mISpellCheckerSession.onCancel(); - } catch (RemoteException e) { - Log.e(TAG, "Failed to cancel " + e); - } - } - - private void processClose() { - if (!checkOpenConnection()) { - return; - } - if (DBG) { - Log.w(TAG, "Close spell checker tasks."); - } - try { - mISpellCheckerSession.onClose(); - mISpellCheckerSession = null; - mHandler = null; - } catch (RemoteException e) { - Log.e(TAG, "Failed to close " + e); - } - } - - private void processGetSuggestionsMultiple(SpellCheckerParams scp) { - if (!checkOpenConnection()) { - return; - } - if (DBG) { - Log.w(TAG, "Get suggestions from the spell checker."); - } - try { - mISpellCheckerSession.onGetSuggestionsMultiple( - scp.mTextInfos, scp.mSuggestionsLimit, scp.mSequentialWords); - } catch (RemoteException e) { - Log.e(TAG, "Failed to get suggestions " + e); - } - } - - private void processGetSuggestionsMultipleForSentence(SpellCheckerParams scp) { - if (!checkOpenConnection()) { - return; - } - if (DBG) { - Log.w(TAG, "Get suggestions from the spell checker."); - } - if (scp.mTextInfos.length != 1) { - throw new IllegalArgumentException(); - } - try { - mISpellCheckerSession.onGetSuggestionsMultipleForSentence( - scp.mTextInfos, scp.mSuggestionsLimit); - } catch (RemoteException e) { - Log.e(TAG, "Failed to get suggestions " + e); } + processTask(session, scp, false); } @Override public void onGetSuggestions(SuggestionsInfo[] results) { - mHandler.sendMessage(Message.obtain(mHandler, MSG_ON_GET_SUGGESTION_MULTIPLE, results)); + synchronized (this) { + if (mHandler != null) { + mHandler.sendMessage(Message.obtain(mHandler, + MSG_ON_GET_SUGGESTION_MULTIPLE, results)); + } + } } @Override diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index c194559..0da867f 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -410,6 +410,7 @@ class BrowserFrame extends Handler { mCommitted = false; // remove pending draw to block update until mFirstLayoutDone is // set to true in didFirstLayout() + mWebViewCore.clearContent(); mWebViewCore.removeMessages(WebViewCore.EventHub.WEBKIT_DRAW); } } @@ -1180,11 +1181,19 @@ class BrowserFrame extends Handler { @Override public void proceed() { SslCertLookupTable.getInstance().setIsAllowed(sslError); - nativeSslCertErrorProceed(handle); + post(new Runnable() { + public void run() { + nativeSslCertErrorProceed(handle); + } + }); } @Override public void cancel() { - nativeSslCertErrorCancel(handle, certError); + post(new Runnable() { + public void run() { + nativeSslCertErrorCancel(handle, certError); + } + }); } }; mCallbackProxy.onReceivedSslError(handler, sslError); diff --git a/core/java/android/webkit/ClientCertRequestHandler.java b/core/java/android/webkit/ClientCertRequestHandler.java index 3a71e7e..f862613 100644 --- a/core/java/android/webkit/ClientCertRequestHandler.java +++ b/core/java/android/webkit/ClientCertRequestHandler.java @@ -16,6 +16,7 @@ package android.webkit; +import android.os.Handler; import java.security.PrivateKey; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; @@ -29,7 +30,7 @@ import org.apache.harmony.xnet.provider.jsse.NativeCrypto; * * @hide */ -public final class ClientCertRequestHandler { +public final class ClientCertRequestHandler extends Handler { private final BrowserFrame mBrowserFrame; private final int mHandle; @@ -49,23 +50,35 @@ public final class ClientCertRequestHandler { * Proceed with the specified private key and client certificate chain. */ public void proceed(PrivateKey privateKey, X509Certificate[] chain) { - byte[] privateKeyBytes = privateKey.getEncoded(); - byte[][] chainBytes; + final byte[] privateKeyBytes = privateKey.getEncoded(); + final byte[][] chainBytes; try { chainBytes = NativeCrypto.encodeCertificates(chain); + mTable.Allow(mHostAndPort, privateKeyBytes, chainBytes); + post(new Runnable() { + public void run() { + mBrowserFrame.nativeSslClientCert(mHandle, privateKeyBytes, chainBytes); + } + }); } catch (CertificateEncodingException e) { - mBrowserFrame.nativeSslClientCert(mHandle, null, null); - return; + post(new Runnable() { + public void run() { + mBrowserFrame.nativeSslClientCert(mHandle, null, null); + return; + } + }); } - mTable.Allow(mHostAndPort, privateKeyBytes, chainBytes); - mBrowserFrame.nativeSslClientCert(mHandle, privateKeyBytes, chainBytes); } /** * Igore the request for now, the user may be prompted again. */ public void ignore() { - mBrowserFrame.nativeSslClientCert(mHandle, null, null); + post(new Runnable() { + public void run() { + mBrowserFrame.nativeSslClientCert(mHandle, null, null); + } + }); } /** @@ -73,6 +86,10 @@ public final class ClientCertRequestHandler { */ public void cancel() { mTable.Deny(mHostAndPort); - mBrowserFrame.nativeSslClientCert(mHandle, null, null); + post(new Runnable() { + public void run() { + mBrowserFrame.nativeSslClientCert(mHandle, null, null); + } + }); } } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index a284a17..ac8693d 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -4538,7 +4538,7 @@ public class WebView extends AbsoluteLayout boolean isPictureAfterFirstLayout, boolean registerPageSwapCallback) { if (mNativeClass == 0) return; - nativeSetBaseLayer(layer, invalRegion, showVisualIndicator, + nativeSetBaseLayer(mNativeClass, layer, invalRegion, showVisualIndicator, isPictureAfterFirstLayout, registerPageSwapCallback); if (mHTML5VideoViewProxy != null) { mHTML5VideoViewProxy.setBaseLayer(layer); @@ -9493,7 +9493,12 @@ public class WebView extends AbsoluteLayout /** @hide call pageSwapCallback upon next page swap */ protected void registerPageSwapCallback() { - nativeRegisterPageSwapCallback(); + nativeRegisterPageSwapCallback(mNativeClass); + } + + /** @hide discard all textures from tiles */ + protected void discardAllTextures() { + nativeDiscardAllTextures(); } /** @@ -9643,7 +9648,8 @@ public class WebView extends AbsoluteLayout private native void nativeSetFindIsEmpty(); private native void nativeSetFindIsUp(boolean isUp); private native void nativeSetHeightCanMeasure(boolean measure); - private native void nativeSetBaseLayer(int layer, Region invalRegion, + private native void nativeSetBaseLayer(int nativeInstance, + int layer, Region invalRegion, boolean showVisualIndicator, boolean isPictureAfterFirstLayout, boolean registerPageSwapCallback); private native int nativeGetBaseLayer(); @@ -9657,7 +9663,8 @@ public class WebView extends AbsoluteLayout private native void nativeStopGL(); private native Rect nativeSubtractLayers(Rect content); private native int nativeTextGeneration(); - private native void nativeRegisterPageSwapCallback(); + private native void nativeRegisterPageSwapCallback(int nativeInstance); + private native void nativeDiscardAllTextures(); private native void nativeTileProfilingStart(); private native float nativeTileProfilingStop(); private native void nativeTileProfilingClear(); diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 14da23e..d99e264 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -497,6 +497,13 @@ public final class WebViewCore { message.sendToTarget(); } + /** + * Clear the picture set. To be called only on the WebCore thread. + */ + /* package */ void clearContent() { + nativeClearContent(); + } + //------------------------------------------------------------------------- // JNI methods //------------------------------------------------------------------------- @@ -1560,7 +1567,7 @@ public final class WebViewCore { // Clear the view so that onDraw() will draw nothing // but white background // (See public method WebView.clearView) - nativeClearContent(); + clearContent(); break; case MESSAGE_RELAY: @@ -2865,6 +2872,6 @@ public final class WebViewCore { private native ArrayList<Rect> nativeGetTouchHighlightRects(int x, int y, int slop); - private native void nativeAutoFillForm(int queryId); - private native void nativeScrollLayer(int layer, Rect rect); + private native void nativeAutoFillForm(int queryId); + private native void nativeScrollLayer(int layer, Rect rect); } 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..a106159 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; @@ -44,14 +45,15 @@ public class SpellChecker implements SpellCheckerSessionListener { // No more than this number of words will be parsed on each iteration to ensure a minimum // lock of the UI thread - public static final int MAX_NUMBER_OF_WORDS = 10; + public static final int MAX_NUMBER_OF_WORDS = 50; - // Safe estimate, will ensure that the interval below usually does not have to be updated - public static final int AVERAGE_WORD_LENGTH = 10; + // Rough estimate, such that the word iterator interval usually does not need to be shifted + public static final int AVERAGE_WORD_LENGTH = 7; // When parsing, use a character window of that size. Will be shifted if needed public static final int WORD_ITERATOR_INTERVAL = AVERAGE_WORD_LENGTH * MAX_NUMBER_OF_WORDS; + // Pause between each spell check to keep the UI smooth private final static int SPELL_PAUSE_DURATION = 400; // milliseconds private final TextView mTextView; @@ -74,6 +76,14 @@ public class SpellChecker implements SpellCheckerSessionListener { private Locale mCurrentLocale; + // Shared by all SpellParsers. Cannot be shared with TextView since it may be used + // concurrently due to the asynchronous nature of onGetSuggestions. + private WordIterator mWordIterator; + + private TextServicesManager mTextServicesManager; + + private Runnable mSpellRunnable; + public SpellChecker(TextView textView) { mTextView = textView; @@ -87,19 +97,19 @@ public class SpellChecker implements SpellCheckerSessionListener { mCookie = hashCode(); } - private void setLocale(Locale locale) { + private void resetSession() { closeSession(); - final TextServicesManager textServicesManager = (TextServicesManager) - mTextView.getContext().getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE); - if (!textServicesManager.isSpellCheckerEnabled()) { + + mTextServicesManager = (TextServicesManager) mTextView.getContext(). + getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE); + if (!mTextServicesManager.isSpellCheckerEnabled()) { mSpellCheckerSession = null; } else { - mSpellCheckerSession = textServicesManager.newSpellCheckerSession( + mSpellCheckerSession = mTextServicesManager.newSpellCheckerSession( null /* Bundle not currently used by the textServicesManager */, - locale, this, + mCurrentLocale, this, false /* means any available languages from current spell checker */); } - mCurrentLocale = locale; // Restore SpellCheckSpans in pool for (int i = 0; i < mLength; i++) { @@ -108,9 +118,19 @@ public class SpellChecker implements SpellCheckerSessionListener { } mLength = 0; - mSpellParsers = new SpellParser[0]; + // Remove existing misspelled SuggestionSpans + mTextView.removeMisspelledSpans((Editable) mTextView.getText()); + } - // This class is the global listener for locale change: warn other locale-aware objects + private void setLocale(Locale locale) { + mCurrentLocale = locale; + + resetSession(); + + // Change SpellParsers' wordIterator locale + mWordIterator = new WordIterator(locale); + + // This class is the listener for locale change: warn other locale-aware objects mTextView.onLocaleChanged(); } @@ -127,14 +147,14 @@ public class SpellChecker implements SpellCheckerSessionListener { mSpellCheckerSession.close(); } - stopAllSpellParsers(); - } - - private void stopAllSpellParsers() { final int length = mSpellParsers.length; for (int i = 0; i < length; i++) { mSpellParsers[i].stop(); } + + if (mSpellRunnable != null) { + mTextView.removeCallbacks(mSpellRunnable); + } } private int nextSpellCheckSpanIndex() { @@ -184,6 +204,12 @@ public class SpellChecker implements SpellCheckerSessionListener { // Re-check the entire text start = 0; end = mTextView.getText().length(); + } else { + final boolean spellCheckerActivated = mTextServicesManager.isSpellCheckerEnabled(); + if (isSessionActive() != spellCheckerActivated) { + // Spell checker has been turned of or off since last spellCheck + resetSession(); + } } if (!isSessionActive()) return; @@ -192,7 +218,7 @@ public class SpellChecker implements SpellCheckerSessionListener { final int length = mSpellParsers.length; for (int i = 0; i < length; i++) { final SpellParser spellParser = mSpellParsers[i]; - if (!spellParser.isParsing()) { + if (spellParser.isFinished()) { spellParser.init(start, end); spellParser.parse(); return; @@ -220,7 +246,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 +255,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]); } @@ -283,18 +310,29 @@ public class SpellChecker implements SpellCheckerSessionListener { } } - mTextView.postDelayed(new Runnable() { - @Override - public void run() { - final int length = mSpellParsers.length; - for (int i = 0; i < length; i++) { - final SpellParser spellParser = mSpellParsers[i]; - if (spellParser.isParsing()) { - spellParser.parse(); + scheduleNewSpellCheck(); + } + + private void scheduleNewSpellCheck() { + if (mSpellRunnable == null) { + mSpellRunnable = new Runnable() { + @Override + public void run() { + final int length = mSpellParsers.length; + for (int i = 0; i < length; i++) { + final SpellParser spellParser = mSpellParsers[i]; + if (!spellParser.isFinished()) { + spellParser.parse(); + break; // run one spell parser at a time to bound running time + } } } - } - }, SPELL_PAUSE_DURATION); + }; + } else { + mTextView.removeCallbacks(mSpellRunnable); + } + + mTextView.postDelayed(mSpellRunnable, SPELL_PAUSE_DURATION); } private void createMisspelledSuggestionSpan(Editable editable, SuggestionsInfo suggestionsInfo, @@ -359,7 +397,7 @@ public class SpellChecker implements SpellCheckerSessionListener { SuggestionSpan.FLAG_EASY_CORRECT | SuggestionSpan.FLAG_MISSPELLED); editable.setSpan(suggestionSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - mTextView.invalidateRegion(start, end); + mTextView.invalidateRegion(start, end, false /* No cursor involved */); } private class SpellParser { @@ -369,12 +407,12 @@ public class SpellChecker implements SpellCheckerSessionListener { setRangeSpan((Editable) mTextView.getText(), start, end); } - public void stop() { - removeRangeSpan((Editable) mTextView.getText()); + public boolean isFinished() { + return ((Editable) mTextView.getText()).getSpanStart(mRange) < 0; } - public boolean isParsing() { - return ((Editable) mTextView.getText()).getSpanStart(mRange) >= 0; + public void stop() { + removeRangeSpan((Editable) mTextView.getText()); } private void setRangeSpan(Editable editable, int start, int end) { @@ -391,20 +429,19 @@ public class SpellChecker implements SpellCheckerSessionListener { final int start = editable.getSpanStart(mRange); final int end = editable.getSpanEnd(mRange); - final WordIterator wordIterator = mTextView.getWordIterator(); int wordIteratorWindowEnd = Math.min(end, start + WORD_ITERATOR_INTERVAL); - wordIterator.setCharSequence(editable, start, wordIteratorWindowEnd); + mWordIterator.setCharSequence(editable, start, wordIteratorWindowEnd); // Move back to the beginning of the current word, if any - int wordStart = wordIterator.preceding(start); + int wordStart = mWordIterator.preceding(start); int wordEnd; if (wordStart == BreakIterator.DONE) { - wordEnd = wordIterator.following(start); + wordEnd = mWordIterator.following(start); if (wordEnd != BreakIterator.DONE) { - wordStart = wordIterator.getBeginning(wordEnd); + wordStart = mWordIterator.getBeginning(wordEnd); } } else { - wordEnd = wordIterator.getEnd(wordStart); + wordEnd = mWordIterator.getEnd(wordStart); } if (wordEnd == BreakIterator.DONE) { removeRangeSpan(editable); @@ -470,15 +507,15 @@ public class SpellChecker implements SpellCheckerSessionListener { // iterate word by word int originalWordEnd = wordEnd; - wordEnd = wordIterator.following(wordEnd); + wordEnd = mWordIterator.following(wordEnd); if ((wordIteratorWindowEnd < end) && (wordEnd == BreakIterator.DONE || wordEnd >= wordIteratorWindowEnd)) { wordIteratorWindowEnd = Math.min(end, originalWordEnd + WORD_ITERATOR_INTERVAL); - wordIterator.setCharSequence(editable, originalWordEnd, wordIteratorWindowEnd); - wordEnd = wordIterator.following(originalWordEnd); + mWordIterator.setCharSequence(editable, originalWordEnd, wordIteratorWindowEnd); + wordEnd = mWordIterator.following(originalWordEnd); } if (wordEnd == BreakIterator.DONE) break; - wordStart = wordIterator.getBeginning(wordEnd); + wordStart = mWordIterator.getBeginning(wordEnd); if (wordStart == BreakIterator.DONE) { break; } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index babd8e7..a178087 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -339,7 +339,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private int mCursorDrawableRes; private final Drawable[] mCursorDrawable = new Drawable[2]; - private int mCursorCount; // Actual current number of used mCursorDrawable: 0, 1 or 2 + private int mCursorCount; // Actual current number of used mCursorDrawable: 0, 1 or 2 (split) private Drawable mSelectHandleLeft; private Drawable mSelectHandleRight; @@ -3210,7 +3210,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } boolean needEditableForNotification = false; - boolean startSpellCheck = false; if (mListeners != null && mListeners.size() != 0) { needEditableForNotification = true; @@ -3222,7 +3221,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener setFilters(t, mFilters); InputMethodManager imm = InputMethodManager.peekInstance(); if (imm != null) imm.restartInput(this); - startSpellCheck = true; } else if (type == BufferType.SPANNABLE || mMovement != null) { text = mSpannableFactory.newSpannable(text); } else if (!(text instanceof CharWrapper)) { @@ -3311,11 +3309,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener sendOnTextChanged(text, 0, oldlen, textLength); onTextChanged(text, 0, oldlen, textLength); - if (startSpellCheck && mSpellChecker != null) { - // This view has to have been previously attached for mSpellChecker to exist - updateSpellCheckSpans(0, textLength); - } - if (needEditableForNotification) { sendAfterTextChanged((Editable) text); } @@ -4329,7 +4322,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (a >= 0 || b >= 0 || c >= 0) { int start = Math.min(Math.min(a, b), c); int end = Math.max(Math.max(a, b), c); - invalidateRegion(start, end); + invalidateRegion(start, end, true /* Also invalidates blinking cursor */); } } @@ -4338,7 +4331,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * * @hide */ - void invalidateRegion(int start, int end) { + void invalidateRegion(int start, int end, boolean invalidateCursor) { if (mLayout == null) { invalidate(); } else { @@ -4364,11 +4357,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int bottom = mLayout.getLineBottom(lineEnd); + if (invalidateCursor) { + for (int i = 0; i < mCursorCount; i++) { + Rect bounds = mCursorDrawable[i].getBounds(); + top = Math.min(top, bounds.top); + bottom = Math.max(bottom, bounds.bottom); + } + } + final int compoundPaddingLeft = getCompoundPaddingLeft(); final int verticalPadding = getExtendedPaddingTop() + getVerticalOffset(true); int left, right; - if (lineStart == lineEnd) { + if (lineStart == lineEnd && !invalidateCursor) { left = (int) mLayout.getPrimaryHorizontal(start); right = (int) (mLayout.getPrimaryHorizontal(end) + 1.0); left += compoundPaddingLeft; @@ -4482,8 +4483,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Resolve drawables as the layout direction has been resolved resolveDrawables(); - - updateSpellCheckSpans(0, mText.length()); + + updateSpellCheckSpans(0, mText.length(), true /* create the spell checker if needed */); } @Override @@ -5517,7 +5518,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 +5555,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) { @@ -7636,7 +7637,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - updateSpellCheckSpans(start, start + after); + updateSpellCheckSpans(start, start + after, false); // Hide the controllers as soon as text is modified (typing, procedural...) // We do not hide the span controllers, since they can be added when a new text is @@ -7794,17 +7795,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - if (newStart < 0 && what instanceof SpellCheckSpan) { - getSpellChecker().removeSpellCheckSpan((SpellCheckSpan) what); + if (mSpellChecker != null && newStart < 0 && what instanceof SpellCheckSpan) { + mSpellChecker.removeSpellCheckSpan((SpellCheckSpan) what); } } /** * Create new SpellCheckSpans on the modified region. */ - private void updateSpellCheckSpans(int start, int end) { - if (isTextEditable() && isSuggestionsEnabled()) { - getSpellChecker().spellCheck(start, end); + private void updateSpellCheckSpans(int start, int end, boolean createSpellChecker) { + if (isTextEditable() && isSuggestionsEnabled() && !(this instanceof ExtractEditText)) { + if (mSpellChecker == null && createSpellChecker) { + mSpellChecker = new SpellChecker(this); + } + if (mSpellChecker != null) { + mSpellChecker.spellCheck(start, end); + } } } @@ -8936,7 +8942,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } void onLocaleChanged() { - removeMisspelledSpans((Editable) mText); // Will be re-created on demand in getWordIterator with the proper new locale mWordIterator = null; } @@ -8976,13 +8981,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return packRangeInLong(offset, offset); } - private SpellChecker getSpellChecker() { - if (mSpellChecker == null) { - mSpellChecker = new SpellChecker(this); - } - return mSpellChecker; - } - private long getLastTouchOffsets() { SelectionModifierCursorController selectionController = getSelectionController(); final int minOffset = selectionController.getMinTouchOffset(); @@ -9835,7 +9833,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 +9919,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; @@ -9937,7 +9935,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // There is no way to know if the word was indeed added. Re-check. // TODO The ExtractEditText should remove the span in the original text instead editable.removeSpan(suggestionInfo.suggestionSpan); - updateSpellCheckSpans(spanStart, spanEnd); + updateSpellCheckSpans(spanStart, spanEnd, false); } else { // SuggestionSpans are removed by replace: save them before SuggestionSpan[] suggestionSpans = editable.getSpans(spanStart, spanEnd, @@ -9989,14 +9987,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 +11467,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/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index fec4cbc..86118b1 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -19,6 +19,7 @@ package com.android.internal.os; import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.UID_ALL; import static android.text.format.DateUtils.SECOND_IN_MILLIS; +import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; @@ -35,6 +36,7 @@ import android.os.ParcelFormatException; import android.os.Parcelable; import android.os.Process; import android.os.SystemClock; +import android.os.SystemProperties; import android.os.WorkSource; import android.telephony.ServiceState; import android.telephony.SignalStrength; @@ -5713,11 +5715,17 @@ public final class BatteryStatsImpl extends BatteryStats { synchronized (this) { if (mNetworkSummaryCache == null || mNetworkSummaryCache.getElapsedRealtimeAge() > SECOND_IN_MILLIS) { - try { - mNetworkSummaryCache = mNetworkStatsFactory.readNetworkStatsSummary(); - } catch (IllegalStateException e) { - // log problem and return empty object - Log.wtf(TAG, "problem reading network stats", e); + mNetworkSummaryCache = null; + + if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) { + try { + mNetworkSummaryCache = mNetworkStatsFactory.readNetworkStatsSummary(); + } catch (IllegalStateException e) { + Log.wtf(TAG, "problem reading network stats", e); + } + } + + if (mNetworkSummaryCache == null) { mNetworkSummaryCache = new NetworkStats(SystemClock.elapsedRealtime(), 0); } } @@ -5730,12 +5738,18 @@ public final class BatteryStatsImpl extends BatteryStats { synchronized (this) { if (mNetworkDetailCache == null || mNetworkDetailCache.getElapsedRealtimeAge() > SECOND_IN_MILLIS) { - try { - mNetworkDetailCache = mNetworkStatsFactory - .readNetworkStatsDetail().groupedByUid(); - } catch (IllegalStateException e) { - // log problem and return empty object - Log.wtf(TAG, "problem reading network stats", e); + mNetworkDetailCache = null; + + if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) { + try { + mNetworkDetailCache = mNetworkStatsFactory + .readNetworkStatsDetail().groupedByUid(); + } catch (IllegalStateException e) { + Log.wtf(TAG, "problem reading network stats", e); + } + } + + if (mNetworkDetailCache == null) { mNetworkDetailCache = new NetworkStats(SystemClock.elapsedRealtime(), 0); } } diff --git a/core/java/com/android/server/NetworkManagementSocketTagger.java b/core/java/com/android/server/NetworkManagementSocketTagger.java index 8445ad1..c77992d 100644 --- a/core/java/com/android/server/NetworkManagementSocketTagger.java +++ b/core/java/com/android/server/NetworkManagementSocketTagger.java @@ -80,14 +80,15 @@ public final class NetworkManagementSocketTagger extends SocketTagger { } private void tagSocketFd(FileDescriptor fd, int tag, int uid) { - int errno; if (tag == -1 && uid == -1) return; - errno = native_tagSocketFd(fd, tag, uid); - if (errno < 0) { - Log.i(TAG, "tagSocketFd(" + fd.getInt$() + ", " - + tag + ", " + - + uid + ") failed with errno" + errno); + if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) { + final int errno = native_tagSocketFd(fd, tag, uid); + if (errno < 0) { + Log.i(TAG, "tagSocketFd(" + fd.getInt$() + ", " + + tag + ", " + + + uid + ") failed with errno" + errno); + } } } @@ -101,12 +102,13 @@ public final class NetworkManagementSocketTagger extends SocketTagger { private void unTagSocketFd(FileDescriptor fd) { final SocketTags options = threadSocketTags.get(); - int errno; if (options.statsTag == -1 && options.statsUid == -1) return; - errno = native_untagSocketFd(fd); - if (errno < 0) { - Log.w(TAG, "untagSocket(" + fd.getInt$() + ") failed with errno " + errno); + if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) { + final int errno = native_untagSocketFd(fd); + if (errno < 0) { + Log.w(TAG, "untagSocket(" + fd.getInt$() + ") failed with errno " + errno); + } } } @@ -116,16 +118,21 @@ public final class NetworkManagementSocketTagger extends SocketTagger { } public static void setKernelCounterSet(int uid, int counterSet) { - int errno = native_setCounterSet(counterSet, uid); - if (errno < 0) { - Log.w(TAG, "setKernelCountSet(" + uid + ", " + counterSet + ") failed with errno " + errno); + if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) { + final int errno = native_setCounterSet(counterSet, uid); + if (errno < 0) { + Log.w(TAG, "setKernelCountSet(" + uid + ", " + counterSet + ") failed with errno " + + errno); + } } } public static void resetKernelUidStats(int uid) { - int errno = native_deleteTagData(0, uid); - if (errno < 0) { - Slog.w(TAG, "problem clearing counters for uid " + uid + " : errno " + errno); + if (SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) { + int errno = native_deleteTagData(0, uid); + if (errno < 0) { + Slog.w(TAG, "problem clearing counters for uid " + uid + " : errno " + errno); + } } } diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp index 81bf4d5..ed6aaa1 100644 --- a/core/jni/android/graphics/TextLayoutCache.cpp +++ b/core/jni/android/graphics/TextLayoutCache.cpp @@ -19,6 +19,8 @@ #include "TextLayoutCache.h" #include "TextLayout.h" #include "SkFontHost.h" +#include <unicode/unistr.h> +#include <unicode/normlzr.h> extern "C" { #include "harfbuzz-unicode.h" @@ -30,6 +32,8 @@ namespace android { #define TYPEFACE_ARABIC "/system/fonts/DroidNaskh-Regular.ttf" #define TYPE_FACE_HEBREW_REGULAR "/system/fonts/DroidSansHebrew-Regular.ttf" #define TYPE_FACE_HEBREW_BOLD "/system/fonts/DroidSansHebrew-Bold.ttf" +#define TYPEFACE_BENGALI "/system/fonts/Lohit-Bengali.ttf" +#define TYPEFACE_THAI "/system/fonts/DroidSansThai.ttf" #if USE_TEXT_LAYOUT_CACHE @@ -503,7 +507,8 @@ void TextLayoutEngine::computeValues(SkPaint* paint, const UChar* chars, static void logGlyphs(HB_ShaperItem shaperItem) { LOGD(" -- glyphs count=%d", shaperItem.num_glyphs); for (size_t i = 0; i < shaperItem.num_glyphs; i++) { - LOGD(" -- glyph[%d] = %d, offset.x = %f, offset.y = %f", i, shaperItem.glyphs[i], + LOGD(" -- glyph[%d] = %d, offset.x = %0.2f, offset.y = %0.2f", i, + shaperItem.glyphs[i], HBFixedToFloat(shaperItem.offsets[i].x), HBFixedToFloat(shaperItem.offsets[i].y)); } @@ -519,8 +524,73 @@ void TextLayoutEngine::computeRunValues(SkPaint* paint, const UChar* chars, return; } + UErrorCode error = U_ZERO_ERROR; + bool useNormalizedString = false; + for (ssize_t i = count - 1; i >= 0; --i) { + UChar ch1 = chars[i]; + if (::ublock_getCode(ch1) == UBLOCK_COMBINING_DIACRITICAL_MARKS) { + // So we have found a diacritic, let's get now the main code point which is paired + // with it. As we can have several diacritics in a row, we need to iterate back again +#if DEBUG_GLYPHS + LOGD("The BiDi run '%s' is containing a Diacritic at position %d", + String8(chars, count).string(), int(i)); +#endif + ssize_t j = i - 1; + for (; j >= 0; --j) { + UChar ch2 = chars[j]; + if (::ublock_getCode(ch2) != UBLOCK_COMBINING_DIACRITICAL_MARKS) { + break; + } + } + + // We could not found the main code point, so we will just use the initial chars + if (j < 0) { + break; + } + +#if DEBUG_GLYPHS + LOGD("Found main code point at index %d", int(j)); +#endif + // We found the main code point, so we can normalize the "chunck" and fill + // the remaining with ZWSP so that the Paint.getTextWidth() APIs will still be able + // to get one advance per char + mBuffer.remove(); + Normalizer::normalize(UnicodeString(chars + j, i - j + 1), + UNORM_NFC, 0 /* no options */, mBuffer, error); + if (U_SUCCESS(error)) { + if (!useNormalizedString) { + useNormalizedString = true; + mNormalizedString.setTo(false /* not terminated*/, chars, count); + } + // Set the normalized chars + for (ssize_t k = j; k < j + mBuffer.length(); ++k) { + mNormalizedString.setCharAt(k, mBuffer.charAt(k - j)); + } + // Fill the remain part with ZWSP (ZWNJ and ZWJ would lead to weird results + // because some fonts are missing those glyphs) + for (ssize_t k = j + mBuffer.length(); k <= i; ++k) { + mNormalizedString.setCharAt(k, UNICODE_ZWSP); + } + } + i = j - 1; + } + } + +#if DEBUG_GLYPHS + if (useNormalizedString) { + LOGD("Will use normalized string '%s', length = %d", + String8(mNormalizedString.getTerminatedBuffer(), + mNormalizedString.length()).string(), + mNormalizedString.length()); + } else { + LOGD("Normalization is not needed or cannot be done, using initial string"); + } +#endif + + assert(mNormalizedString.length() == count); + // Set the string properties - mShaperItem.string = chars; + mShaperItem.string = useNormalizedString ? mNormalizedString.getTerminatedBuffer() : chars; mShaperItem.stringLength = count; // Define shaping paint properties @@ -532,14 +602,14 @@ void TextLayoutEngine::computeRunValues(SkPaint* paint, const UChar* chars, // Split the BiDi run into Script runs. Harfbuzz will populate the pos, length and script // into the shaperItem - ssize_t indexFontRun = isRTL ? count - 1 : 0; + ssize_t indexFontRun = isRTL ? mShaperItem.stringLength - 1 : 0; unsigned numCodePoints = 0; jfloat totalAdvance = 0; while ((isRTL) ? - hb_utf16_script_run_prev(&numCodePoints, &mShaperItem.item, chars, - count, &indexFontRun): - hb_utf16_script_run_next(&numCodePoints, &mShaperItem.item, chars, - count, &indexFontRun)) { + hb_utf16_script_run_prev(&numCodePoints, &mShaperItem.item, mShaperItem.string, + mShaperItem.stringLength, &indexFontRun): + hb_utf16_script_run_next(&numCodePoints, &mShaperItem.item, mShaperItem.string, + mShaperItem.stringLength, &indexFontRun)) { ssize_t startScriptRun = mShaperItem.item.pos; size_t countScriptRun = mShaperItem.item.length; @@ -611,7 +681,7 @@ void TextLayoutEngine::computeRunValues(SkPaint* paint, const UChar* chars, #if DEBUG_ADVANCES LOGD("Returned advances"); for (size_t i = 0; i < countScriptRun; i++) { - LOGD(" -- hb-adv[%d] = %f, log_clusters = %d, total = %f", i, + LOGD(" -- hb-adv[%d] = %0.2f, log_clusters = %d, total = %0.2f", i, (*outAdvances)[i], mShaperItem.log_clusters[i], totalFontRunAdvance); } #endif @@ -687,6 +757,20 @@ size_t TextLayoutEngine::shapeFontRun(SkPaint* paint, bool isRTL) { } break; + case HB_Script_Bengali: + typeface = getCachedTypeface(&mBengaliTypeface, TYPEFACE_BENGALI); +#if DEBUG_GLYPHS + LOGD("Using Bengali Typeface"); +#endif + break; + + case HB_Script_Thai: + typeface = getCachedTypeface(&mThaiTypeface, TYPEFACE_THAI); +#if DEBUG_GLYPHS + LOGD("Using Thai Typeface"); +#endif + break; + default: if (!typeface) { typeface = mDefaultTypeface; @@ -716,7 +800,9 @@ size_t TextLayoutEngine::shapeFontRun(SkPaint* paint, bool isRTL) { size_t baseGlyphCount = 0; switch (mShaperItem.item.script) { case HB_Script_Arabic: - case HB_Script_Hebrew: { + case HB_Script_Hebrew: + case HB_Script_Bengali: + case HB_Script_Thai:{ const uint16_t* text16 = (const uint16_t*)mShaperItem.string; SkUnichar firstUnichar = SkUTF16_NextUnichar(&text16); baseGlyphCount = paint->getBaseGlyphCount(firstUnichar); @@ -775,6 +861,13 @@ void TextLayoutEngine::deleteShaperItemGlyphArrays() { SkTypeface* TextLayoutEngine::getCachedTypeface(SkTypeface** typeface, const char path[]) { if (!*typeface) { *typeface = SkTypeface::CreateFromFile(path); + // CreateFromFile(path) can return NULL if the path is non existing + if (!*typeface) { +#if DEBUG_GLYPHS + LOGD("Font path '%s' is not valid, will use default font", path); +#endif + return mDefaultTypeface; + } (*typeface)->ref(); #if DEBUG_GLYPHS LOGD("Created SkTypeface from file '%s' with uniqueID = %d", path, (*typeface)->uniqueID()); diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h index fd9ccb1..510aa18 100644 --- a/core/jni/android/graphics/TextLayoutCache.h +++ b/core/jni/android/graphics/TextLayoutCache.h @@ -35,6 +35,8 @@ #include <unicode/ubidi.h> #include <unicode/ushape.h> +#include <unicode/unistr.h> + #include "HarfbuzzSkia.h" #include "harfbuzz-shaper.h" @@ -248,11 +250,29 @@ private: SkTypeface* mArabicTypeface; SkTypeface* mHebrewRegularTypeface; SkTypeface* mHebrewBoldTypeface; + SkTypeface* mBengaliTypeface; + SkTypeface* mThaiTypeface; + /** + * Cache of Harfbuzz faces + */ KeyedVector<SkFontID, HB_Face> mCachedHBFaces; + /** + * Cache of glyph array size + */ size_t mShaperItemGlyphArraySize; + /** + * Buffer for containing the ICU normalized form of a run + */ + UnicodeString mNormalizedString; + + /** + * Buffer for normalizing a piece of a run with ICU + */ + UnicodeString mBuffer; + size_t shapeFontRun(SkPaint* paint, bool isRTL); void computeValues(SkPaint* paint, const UChar* chars, diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp index 2aeca86..9dbe774 100644 --- a/core/jni/android_server_BluetoothService.cpp +++ b/core/jni/android_server_BluetoothService.cpp @@ -1645,6 +1645,25 @@ static jobject getChannelFdNative(JNIEnv *env, jobject object, jstring channelPa fd = dbus_returns_unixfd(env, reply); if (fd == -1) return NULL; + int flags = fcntl(fd, F_GETFL); + if (flags < 0) { + LOGE("Can't get flags with fcntl(): %s (%d)", + strerror(errno), errno); + releaseChannelFdNative(env, object, channelPath); + close(fd); + return NULL; + } + + flags &= ~O_NONBLOCK; + int status = fcntl(fd, F_SETFL, flags); + if (status < 0) { + LOGE("Can't set flags with fcntl(): %s (%d)", + strerror(errno), errno); + releaseChannelFdNative(env, object, channelPath); + close(fd); + return NULL; + } + // Create FileDescriptor object jobject fileDesc = jniCreateFileDescriptor(env, fd); if (fileDesc == NULL) { diff --git a/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml index 7a5bb6a..568933c 100644 --- a/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml @@ -63,7 +63,7 @@ </RelativeLayout> <!-- right side: password --> - <LinearLayout + <RelativeLayout android:layout_width="0dip" android:layout_weight="1" android:layout_height="match_parent" @@ -72,6 +72,7 @@ <LinearLayout android:orientation="vertical" + android:layout_centerInParent="true" android:layout_width="330dip" android:layout_height="wrap_content"> @@ -152,6 +153,15 @@ </LinearLayout> - </LinearLayout> + <!-- Area to overlay FaceLock --> + <View android:id="@+id/faceLockAreaView" + android:visibility="invisible" + android:layout_width="512dip" + android:layout_height="512dip" + android:layout_centerInParent="true" + android:background="@color/facelock_color_background" + /> + + </RelativeLayout> </LinearLayout> diff --git a/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml index 6df22ca..335a641 100644 --- a/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml @@ -61,92 +61,109 @@ </RelativeLayout> <!-- bottom: password --> - <LinearLayout + <RelativeLayout android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="1" - android:orientation="vertical" android:gravity="center"> - <!-- Password entry field --> <LinearLayout - android:orientation="horizontal" - android:layout_width="330dip" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:layout_marginTop="120dip" - android:layout_marginBottom="5dip" - android:background="@drawable/lockscreen_password_field_dark"> + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_centerInParent="true" + android:orientation="vertical" + android:gravity="center"> - <EditText android:id="@+id/passwordEntry" + <!-- Password entry field --> + <LinearLayout + android:orientation="horizontal" + android:layout_width="330dip" android:layout_height="wrap_content" - android:layout_width="0dip" - android:layout_weight="1" - android:singleLine="true" - android:textStyle="normal" - android:inputType="textPassword" - android:gravity="center" android:layout_gravity="center" - android:layout_marginLeft="@dimen/keyguard_lockscreen_pin_margin_left" - android:textSize="24sp" - android:textAppearance="?android:attr/textAppearanceMedium" - android:background="@null" - android:textColor="#ffffffff" - android:privateImeOptions="com.google.android.inputmethod.latin.forceAscii" + android:layout_marginTop="120dip" + android:layout_marginBottom="5dip" + android:background="@drawable/lockscreen_password_field_dark"> + + <EditText android:id="@+id/passwordEntry" + android:layout_height="wrap_content" + android:layout_width="0dip" + android:layout_weight="1" + android:singleLine="true" + android:textStyle="normal" + android:inputType="textPassword" + android:gravity="center" + android:layout_gravity="center" + android:layout_marginLeft="@dimen/keyguard_lockscreen_pin_margin_left" + android:textSize="24sp" + android:textAppearance="?android:attr/textAppearanceMedium" + android:background="@null" + android:textColor="#ffffffff" + android:privateImeOptions="com.google.android.inputmethod.latin.forceAscii" /> - <!-- This delete button is only visible for numeric PIN entry --> - <ImageButton android:id="@+id/pinDel" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:src="@android:drawable/ic_input_delete" - android:clickable="true" - android:padding="8dip" - android:layout_gravity="center" - android:background="?android:attr/selectableItemBackground" - android:visibility="gone" + <!-- This delete button is only visible for numeric PIN entry --> + <ImageButton android:id="@+id/pinDel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@android:drawable/ic_input_delete" + android:clickable="true" + android:padding="8dip" + android:layout_gravity="center" + android:background="?android:attr/selectableItemBackground" + android:visibility="gone" /> - <ImageView android:id="@+id/switch_ime_button" + <ImageView android:id="@+id/switch_ime_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/ic_lockscreen_ime" + android:clickable="true" + android:padding="8dip" + android:layout_gravity="center" + android:background="?android:attr/selectableItemBackground" + android:visibility="gone" + /> + + </LinearLayout> + + <View + android:layout_width="match_parent" + android:layout_height="0dip" + android:layout_weight="1" + /> + + <!-- Numeric keyboard --> + <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard" + android:layout_width="330dip" + android:layout_height="260dip" + android:background="#40000000" + android:keyBackground="@drawable/btn_keyboard_key_ics" + android:layout_marginBottom="80dip" + android:clickable="true" + /> + + <!-- emergency call button --> + <Button android:id="@+id/emergencyCallButton" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:src="@drawable/ic_lockscreen_ime" - android:clickable="true" - android:padding="8dip" - android:layout_gravity="center" - android:background="?android:attr/selectableItemBackground" + android:drawableLeft="@drawable/ic_emergency" + android:drawablePadding="8dip" + android:text="@string/lockscreen_emergency_call" android:visibility="gone" - /> + style="@style/Widget.Button.Transparent" + /> </LinearLayout> - <View - android:layout_width="match_parent" - android:layout_height="0dip" - android:layout_weight="1" - /> - - <!-- Numeric keyboard --> - <com.android.internal.widget.PasswordEntryKeyboardView android:id="@+id/keyboard" - android:layout_width="330dip" - android:layout_height="260dip" - android:background="#40000000" - android:keyBackground="@drawable/btn_keyboard_key_ics" - android:layout_marginBottom="80dip" - android:clickable="true" + <!-- Area to overlay FaceLock --> + <View android:id="@+id/faceLockAreaView" + android:visibility="invisible" + android:layout_width="512dip" + android:layout_height="512dip" + android:layout_centerInParent="true" + android:background="@color/facelock_color_background" /> - <!-- emergency call button --> - <Button - android:id="@+id/emergencyCallButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:drawableLeft="@drawable/ic_emergency" - android:drawablePadding="8dip" - android:text="@string/lockscreen_emergency_call" - android:visibility="gone" - style="@style/Widget.Button.Transparent" - /> + </RelativeLayout> - </LinearLayout> </LinearLayout> diff --git a/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_landscape.xml index c65dd83..0b94fc1 100644 --- a/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_landscape.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_landscape.xml @@ -93,29 +93,29 @@ android:layout_marginLeft="8dip" android:layout_marginRight="8dip"> - <Button android:id="@+id/ok" - android:text="@android:string/ok" + <Button android:id="@+id/emergencyCallButton" + android:text="@android:string/lockscreen_emergency_call" android:layout_alignParentBottom="true" + android:layout_centerHorizontal="true" android:layout_width="0dip" android:layout_height="wrap_content" android:layout_weight="1.0" android:layout_marginBottom="8dip" android:layout_marginRight="8dip" android:textSize="18sp" - /> + android:drawableLeft="@drawable/ic_emergency" + android:drawablePadding="8dip" + /> - <Button android:id="@+id/emergencyCallButton" - android:text="@android:string/lockscreen_emergency_call" + <Button android:id="@+id/ok" + android:text="@android:string/ok" android:layout_alignParentBottom="true" - android:layout_centerHorizontal="true" android:layout_width="0dip" android:layout_height="wrap_content" android:layout_weight="1.0" android:layout_marginBottom="8dip" android:layout_marginLeft="8dip" android:textSize="18sp" - android:drawableLeft="@drawable/ic_emergency" - android:drawablePadding="8dip" /> </LinearLayout> diff --git a/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml index 70d18cc..802ef82 100644 --- a/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml @@ -72,48 +72,65 @@ android:layout_height="match_parent" android:gravity="center_vertical|center_horizontal"> - <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern" - android:layout_width="354dip" - android:layout_height="354dip" - android:layout_gravity="center_vertical" - /> - - <!-- Emergency and forgot pattern buttons. --> - <LinearLayout - android:orientation="horizontal" - android:layout_width="match_parent" + <RelativeLayout + android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_below="@id/lockPattern" - android:layout_alignLeft="@id/lockPattern" - android:layout_alignRight="@id/lockPattern" - android:layout_marginTop="28dip" - android:gravity="center" - style="?android:attr/buttonBarStyle" - android:weightSum="2"> - - <Button android:id="@+id/forgotPatternButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - style="?android:attr/buttonBarButtonStyle" - android:drawableLeft="@drawable/lockscreen_forgot_password_button" - android:drawablePadding="8dip" - android:text="@string/lockscreen_forgot_pattern_button_text" - android:visibility="gone" + android:layout_centerInParent="true" + android:gravity="center_vertical|center_horizontal"> + + <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern" + android:layout_width="354dip" + android:layout_height="354dip" + android:layout_gravity="center_vertical" /> - <Button android:id="@+id/emergencyCallButton" - android:layout_width="wrap_content" + <!-- Emergency and forgot pattern buttons. --> + <LinearLayout + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_gravity="center" - style="?android:attr/buttonBarButtonStyle" - android:drawableLeft="@drawable/ic_emergency" - android:drawablePadding="8dip" - android:text="@string/lockscreen_emergency_call" - android:visibility="gone" - /> + android:orientation="horizontal" + android:layout_below="@id/lockPattern" + android:layout_alignLeft="@id/lockPattern" + android:layout_alignRight="@id/lockPattern" + android:layout_marginTop="28dip" + style="?android:attr/buttonBarStyle" + android:gravity="center" + android:weightSum="2"> + + <Button android:id="@+id/forgotPatternButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + style="?android:attr/buttonBarButtonStyle" + android:drawableLeft="@drawable/lockscreen_forgot_password_button" + android:drawablePadding="8dip" + android:text="@string/lockscreen_forgot_pattern_button_text" + android:visibility="gone" + /> - </LinearLayout> + <Button android:id="@+id/emergencyCallButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + style="?android:attr/buttonBarButtonStyle" + android:drawableLeft="@drawable/ic_emergency" + android:drawablePadding="8dip" + android:text="@string/lockscreen_emergency_call" + android:visibility="gone" + /> + + </LinearLayout> + + </RelativeLayout> + + <!-- Area to overlay FaceLock --> + <View android:id="@+id/faceLockAreaView" + android:visibility="invisible" + android:layout_width="512dip" + android:layout_height="512dip" + android:layout_centerInParent="true" + android:background="@color/facelock_color_background" + /> </RelativeLayout> diff --git a/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml index 7a623ce..40f2492 100644 --- a/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml @@ -62,52 +62,71 @@ </RelativeLayout> <!-- bottom: lock pattern, emergency dialer and forgot pattern button --> - <LinearLayout + <RelativeLayout android:layout_weight="1" android:layout_width="match_parent" android:layout_height="0dip" - android:orientation="vertical" android:gravity="center"> - <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern" - android:layout_width="354dip" - android:layout_height="354dip" - android:layout_marginTop="50dip"/> - - <!-- Emergency and forgot pattern buttons. --> - <LinearLayout - android:layout_width="match_parent" + <RelativeLayout + android:layout_width="wrap_content" android:layout_height="wrap_content" - android:orientation="horizontal" - style="?android:attr/buttonBarStyle" - android:gravity="center" - android:weightSum="2"> + android:layout_centerInParent="true" + android:gravity="center"> - <Button android:id="@+id/forgotPatternButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - style="?android:attr/buttonBarButtonStyle" - android:drawableLeft="@drawable/lockscreen_forgot_password_button" - android:drawablePadding="8dip" - android:text="@string/lockscreen_forgot_pattern_button_text" - android:visibility="gone" + <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern" + android:layout_width="354dip" + android:layout_height="354dip" + android:layout_marginTop="50dip" /> - <Button android:id="@+id/emergencyCallButton" - android:layout_width="wrap_content" + <!-- Emergency and forgot pattern buttons. --> + <LinearLayout + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_gravity="center" - style="?android:attr/buttonBarButtonStyle" - android:drawableLeft="@drawable/ic_emergency" - android:drawablePadding="8dip" - android:text="@string/lockscreen_emergency_call" - android:visibility="gone" - /> + android:orientation="horizontal" + android:layout_below="@id/lockPattern" + android:layout_alignLeft="@id/lockPattern" + android:layout_alignRight="@id/lockPattern" + style="?android:attr/buttonBarStyle" + android:gravity="center" + android:weightSum="2"> + + <Button android:id="@+id/forgotPatternButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + style="?android:attr/buttonBarButtonStyle" + android:drawableLeft="@drawable/lockscreen_forgot_password_button" + android:drawablePadding="8dip" + android:text="@string/lockscreen_forgot_pattern_button_text" + android:visibility="gone" + /> - </LinearLayout> + <Button android:id="@+id/emergencyCallButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + style="?android:attr/buttonBarButtonStyle" + android:drawableLeft="@drawable/ic_emergency" + android:drawablePadding="8dip" + android:text="@string/lockscreen_emergency_call" + android:visibility="gone" + /> - </LinearLayout> + </LinearLayout> -</com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient> + </RelativeLayout> + <!-- Area to overlay FaceLock --> + <View android:id="@+id/faceLockAreaView" + android:visibility="invisible" + android:layout_width="512dip" + android:layout_height="512dip" + android:layout_centerInParent="true" + android:background="@color/facelock_color_background" + /> + + </RelativeLayout> + +</com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient> diff --git a/core/res/res/layout/keyguard_screen_sim_pin_landscape.xml b/core/res/res/layout/keyguard_screen_sim_pin_landscape.xml index 59065e1..3cb19c3 100644 --- a/core/res/res/layout/keyguard_screen_sim_pin_landscape.xml +++ b/core/res/res/layout/keyguard_screen_sim_pin_landscape.xml @@ -91,29 +91,29 @@ android:layout_marginLeft="8dip" android:layout_marginRight="8dip"> - <Button android:id="@+id/ok" - android:text="@android:string/ok" + <Button android:id="@+id/emergencyCallButton" + android:text="@android:string/lockscreen_emergency_call" android:layout_alignParentBottom="true" + android:layout_centerHorizontal="true" android:layout_width="0dip" android:layout_height="wrap_content" android:layout_weight="1.0" android:layout_marginBottom="8dip" android:layout_marginRight="8dip" android:textSize="18sp" - /> + android:drawableLeft="@drawable/ic_emergency" + android:drawablePadding="4dip" + /> - <Button android:id="@+id/emergencyCallButton" - android:text="@android:string/lockscreen_emergency_call" + <Button android:id="@+id/ok" + android:text="@android:string/ok" android:layout_alignParentBottom="true" - android:layout_centerHorizontal="true" android:layout_width="0dip" android:layout_height="wrap_content" android:layout_weight="1.0" android:layout_marginBottom="8dip" android:layout_marginLeft="8dip" android:textSize="18sp" - android:drawableLeft="@drawable/ic_emergency" - android:drawablePadding="4dip" /> </LinearLayout> diff --git a/core/res/res/layout/keyguard_screen_sim_puk_landscape.xml b/core/res/res/layout/keyguard_screen_sim_puk_landscape.xml index b662e82..722dc26 100644 --- a/core/res/res/layout/keyguard_screen_sim_puk_landscape.xml +++ b/core/res/res/layout/keyguard_screen_sim_puk_landscape.xml @@ -153,29 +153,29 @@ android:layout_marginLeft="8dip" android:layout_marginRight="8dip"> - <Button android:id="@+id/ok" - android:text="@android:string/ok" + <Button android:id="@+id/emergencyCallButton" + android:text="@android:string/lockscreen_emergency_call" android:layout_alignParentBottom="true" + android:layout_centerHorizontal="true" android:layout_width="0dip" android:layout_height="wrap_content" android:layout_weight="1.0" android:layout_marginBottom="8dip" android:layout_marginRight="8dip" android:textSize="18sp" - /> + android:drawableLeft="@drawable/ic_emergency" + android:drawablePadding="4dip" + /> - <Button android:id="@+id/emergencyCallButton" - android:text="@android:string/lockscreen_emergency_call" + <Button android:id="@+id/ok" + android:text="@android:string/ok" android:layout_alignParentBottom="true" - android:layout_centerHorizontal="true" android:layout_width="0dip" android:layout_height="wrap_content" android:layout_weight="1.0" android:layout_marginBottom="8dip" android:layout_marginLeft="8dip" android:textSize="18sp" - android:drawableLeft="@drawable/ic_emergency" - android:drawablePadding="4dip" /> </LinearLayout> diff --git a/core/res/res/layout/twelve_key_entry.xml b/core/res/res/layout/twelve_key_entry.xml index 46301cd..09c749d 100644 --- a/core/res/res/layout/twelve_key_entry.xml +++ b/core/res/res/layout/twelve_key_entry.xml @@ -144,7 +144,7 @@ android:layout_marginRight="2dip" android:orientation="horizontal"> - <Button android:id="@+id/ok" + <Button android:id="@+id/cancel" android:layout_width="0sp" android:layout_height="fill_parent" android:layout_weight="1" @@ -152,7 +152,7 @@ android:layout_marginRight="2dip" android:textAppearance="?android:attr/textAppearanceMedium" android:textStyle="bold" - android:text="@android:string/ok" + android:text="@android:string/cancel" /> <Button android:id="@+id/zero" @@ -165,7 +165,7 @@ android:textStyle="bold" /> - <Button android:id="@+id/cancel" + <Button android:id="@+id/ok" android:layout_width="0sp" android:layout_height="fill_parent" android:layout_weight="1" @@ -173,7 +173,7 @@ android:layout_marginRight="2dip" android:textAppearance="?android:attr/textAppearanceMedium" android:textStyle="bold" - android:text="@android:string/cancel" + android:text="@android:string/ok" /> </LinearLayout> 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/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java index e22b018..e44023b 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java @@ -271,4 +271,46 @@ public class WifiClientTest extends AndroidTestCase { } + // Test case 6: test configured network status + @LargeTest + public void testWifiConfiguredNetworkStatus() { + + /* Initialize */ + mWifiManager.setWifiEnabled(false); + sleepAfterWifiEnable(); + + /* Ensure no network is CURRENT */ + List<WifiConfiguration> configList = mWifiManager.getConfiguredNetworks(); + for (WifiConfiguration c : configList) { + assertTrue(c.status != WifiConfiguration.Status.CURRENT); + } + + /* Enable wifi */ + mWifiManager.setWifiEnabled(true); + sleepAfterWifiEnable(); + + /* Ensure connected network is CURRENT */ + String connectedSSID = mWifiManager.getConnectionInfo().getSSID(); + configList = mWifiManager.getConfiguredNetworks(); + for (WifiConfiguration c : configList) { + if (c.SSID.contains(connectedSSID)) { + assertTrue(c.status == WifiConfiguration.Status.CURRENT); + } else { + assertTrue(c.status != WifiConfiguration.Status.CURRENT); + } + } + + /* Disable wifi */ + mWifiManager.setWifiEnabled(false); + sleepAfterWifiEnable(); + + /* Ensure no network is CURRENT */ + configList = mWifiManager.getConfiguredNetworks(); + for (WifiConfiguration c : configList) { + assertTrue(c.status != WifiConfiguration.Status.CURRENT); + } + } + + + } |