summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/java/android/app/Fragment.java3
-rw-r--r--core/java/android/app/FragmentManager.java7
-rw-r--r--core/java/android/app/LoaderManager.java3
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java21
-rw-r--r--core/java/android/content/BroadcastReceiver.java97
-rw-r--r--core/java/android/content/Loader.java15
-rw-r--r--core/java/android/inputmethodservice/ExtractEditText.java33
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java16
-rw-r--r--core/java/android/server/BluetoothAdapterStateMachine.java2
-rw-r--r--core/java/android/service/textservice/SpellCheckerService.java23
-rw-r--r--core/java/android/text/SpannableStringBuilder.java11
-rw-r--r--core/java/android/text/TextLine.java122
-rw-r--r--core/java/android/text/format/Formatter.java2
-rw-r--r--core/java/android/text/format/package.html7
-rw-r--r--core/java/android/text/method/WordIterator.java7
-rw-r--r--core/java/android/text/style/SuggestionSpan.java16
-rw-r--r--core/java/android/view/HardwareRenderer.java17
-rw-r--r--core/java/android/view/ViewGroup.java11
-rw-r--r--core/java/android/view/ViewRootImpl.java3
-rw-r--r--core/java/android/view/VolumePanel.java2
-rw-r--r--core/java/android/view/Window.java3
-rw-r--r--core/java/android/view/WindowManager.java10
-rw-r--r--core/java/android/view/WindowManagerPolicy.java8
-rw-r--r--core/java/android/view/textservice/SpellCheckerSession.java222
-rw-r--r--core/java/android/webkit/BrowserFrame.java13
-rw-r--r--core/java/android/webkit/ClientCertRequestHandler.java35
-rw-r--r--core/java/android/webkit/WebView.java15
-rw-r--r--core/java/android/webkit/WebViewCore.java13
-rw-r--r--core/java/android/widget/NumberPicker.java19
-rw-r--r--core/java/android/widget/SpellChecker.java125
-rw-r--r--core/java/android/widget/TextView.java84
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java36
-rw-r--r--core/java/com/android/server/NetworkManagementSocketTagger.java39
-rw-r--r--core/jni/android/graphics/TextLayoutCache.cpp111
-rw-r--r--core/jni/android/graphics/TextLayoutCache.h20
-rw-r--r--core/jni/android_server_BluetoothService.cpp19
-rw-r--r--core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml14
-rw-r--r--core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml149
-rw-r--r--core/res/res/layout-sw600dp/keyguard_screen_sim_pin_landscape.xml16
-rw-r--r--core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml91
-rw-r--r--core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml89
-rw-r--r--core/res/res/layout/keyguard_screen_sim_pin_landscape.xml16
-rw-r--r--core/res/res/layout/keyguard_screen_sim_puk_landscape.xml16
-rw-r--r--core/res/res/layout/twelve_key_entry.xml8
-rwxr-xr-xcore/res/res/values/strings.xml4
-rw-r--r--core/res/res/values/themes.xml2
-rw-r--r--core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/unit/WifiClientTest.java42
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 &lt;receiver&gt;}
- * 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>
* &nbsp;&nbsp;&nbsp;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 &lt;uses-permission&gt;}
* 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 &lt;uses-permission&gt;}
* 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);
+ }
+ }
+
+
+
}