Merge "New null (disconnected) RSSI for wifi and mobile." into jb-dev
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 9a8d802..2eea171 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1852,14 +1852,17 @@
             int[] rowIds = {R.id.inbox_text0, R.id.inbox_text1, R.id.inbox_text2, R.id.inbox_text3,
                     R.id.inbox_text4};
 
+            // Make sure all rows are gone in case we reuse a view.
+            for (int rowId : rowIds) {
+                contentView.setViewVisibility(rowId, View.GONE);
+            }
+
             int i=0;
             while (i < mTexts.size() && i < rowIds.length) {
                 CharSequence str = mTexts.get(i);
                 if (str != null && !str.equals("")) {
                     contentView.setViewVisibility(rowIds[i], View.VISIBLE);
                     contentView.setTextViewText(rowIds[i], str);
-                } else {
-                    contentView.setViewVisibility(rowIds[i], View.GONE);
                 }
                 i++;
             }
diff --git a/core/java/android/preference/DialogPreference.java b/core/java/android/preference/DialogPreference.java
index c59ed18..a643c8a 100644
--- a/core/java/android/preference/DialogPreference.java
+++ b/core/java/android/preference/DialogPreference.java
@@ -261,6 +261,8 @@
     
     @Override
     protected void onClick() {
+        if (mDialog != null && mDialog.isShowing()) return;
+
         showDialog(null);
     }
 
diff --git a/core/java/android/webkit/WebCoreThreadWatchdog.java b/core/java/android/webkit/WebCoreThreadWatchdog.java
index 655db31..a22e6e8 100644
--- a/core/java/android/webkit/WebCoreThreadWatchdog.java
+++ b/core/java/android/webkit/WebCoreThreadWatchdog.java
@@ -26,6 +26,10 @@
 import android.os.Process;
 import android.webkit.WebViewCore.EventHub;
 
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
 // A Runnable that will monitor if the WebCore thread is still
 // processing messages by pinging it every so often. It is safe
 // to call the public methods of this class from any thread.
@@ -51,25 +55,31 @@
     // After the first timeout, use a shorter period before re-prompting the user.
     private static final int SUBSEQUENT_TIMEOUT_PERIOD = 15 * 1000;
 
-    private Context mContext;
     private Handler mWebCoreThreadHandler;
     private Handler mHandler;
     private boolean mPaused;
 
+    private Set<WebViewClassic> mWebViews;
+
     private static WebCoreThreadWatchdog sInstance;
 
-    public synchronized static WebCoreThreadWatchdog start(Context context,
-            Handler webCoreThreadHandler) {
+    public synchronized static WebCoreThreadWatchdog start(Handler webCoreThreadHandler) {
         if (sInstance == null) {
-            sInstance = new WebCoreThreadWatchdog(context, webCoreThreadHandler);
+            sInstance = new WebCoreThreadWatchdog(webCoreThreadHandler);
             new Thread(sInstance, "WebCoreThreadWatchdog").start();
         }
         return sInstance;
     }
 
-    public synchronized static void updateContext(Context context) {
+    public synchronized static void registerWebView(WebViewClassic w) {
         if (sInstance != null) {
-            sInstance.setContext(context);
+            sInstance.addWebView(w);
+        }
+    }
+
+    public synchronized static void unregisterWebView(WebViewClassic w) {
+        if (sInstance != null) {
+            sInstance.removeWebView(w);
         }
     }
 
@@ -85,12 +95,18 @@
         }
     }
 
-    private void setContext(Context context) {
-        mContext = context;
+    private void addWebView(WebViewClassic w) {
+        if (mWebViews == null) {
+            mWebViews = new HashSet<WebViewClassic>();
+        }
+        mWebViews.add(w);
     }
 
-    private WebCoreThreadWatchdog(Context context, Handler webCoreThreadHandler) {
-        mContext = context;
+    private void removeWebView(WebViewClassic w) {
+        mWebViews.remove(w);
+    }
+
+    private WebCoreThreadWatchdog(Handler webCoreThreadHandler) {
         mWebCoreThreadHandler = webCoreThreadHandler;
     }
 
@@ -147,39 +163,41 @@
                         break;
 
                     case TIMED_OUT:
-                        if ((mContext == null) || !(mContext instanceof Activity)) return;
-                        new AlertDialog.Builder(mContext)
-                            .setMessage(com.android.internal.R.string.webpage_unresponsive)
-                            .setPositiveButton(com.android.internal.R.string.force_close,
-                                    new DialogInterface.OnClickListener() {
-                                        @Override
-                                        public void onClick(DialogInterface dialog, int which) {
-                                        // User chose to force close.
-                                        Process.killProcess(Process.myPid());
+                        boolean postedDialog = false;
+                        synchronized (WebCoreThreadWatchdog.class) {
+                            Iterator<WebViewClassic> it = mWebViews.iterator();
+                            // Check each WebView we are aware of and find one that is capable of
+                            // showing the user a prompt dialog.
+                            while (it.hasNext()) {
+                                WebView activeView = it.next().getWebView();
+
+                                if (activeView.getWindowToken() != null &&
+                                        activeView.getViewRootImpl() != null) {
+                                    postedDialog = activeView.post(new PageNotRespondingRunnable(
+                                            activeView.getContext(), this));
+
+                                    if (postedDialog) {
+                                        // We placed the message into the UI thread for an attached
+                                        // WebView so we've made our best attempt to display the
+                                        // "page not responding" dialog to the user. Although the
+                                        // message is in the queue, there is no guarantee when/if
+                                        // the runnable will execute. In the case that the runnable
+                                        // never executes, the user will need to terminate the
+                                        // process manually.
+                                        break;
                                     }
-                                })
-                            .setNegativeButton(com.android.internal.R.string.wait,
-                                    new DialogInterface.OnClickListener() {
-                                        @Override
-                                        public void onClick(DialogInterface dialog, int which) {
-                                            // The user chose to wait. The last HEARTBEAT message
-                                            // will still be in the WebCore thread's queue, so all
-                                            // we need to do is post another TIMED_OUT so that the
-                                            // user will get prompted again if the WebCore thread
-                                            // doesn't sort itself out.
-                                            sendMessageDelayed(obtainMessage(TIMED_OUT),
-                                                    SUBSEQUENT_TIMEOUT_PERIOD);
-                                       }
-                                    })
-                            .setOnCancelListener(new DialogInterface.OnCancelListener() {
-                                    @Override
-                                    public void onCancel(DialogInterface dialog) {
-                                        sendMessageDelayed(obtainMessage(TIMED_OUT),
-                                                SUBSEQUENT_TIMEOUT_PERIOD);
-                                    }
-                            })
-                            .setIcon(android.R.drawable.ic_dialog_alert)
-                            .show();
+                                }
+                            }
+
+                            if (!postedDialog) {
+                                // There's no active webview we can use to show the dialog, so
+                                // wait again. If we never get a usable view, the user will
+                                // never get the chance to terminate the process, and will
+                                // need to do it manually.
+                                sendMessageDelayed(obtainMessage(TIMED_OUT),
+                                        SUBSEQUENT_TIMEOUT_PERIOD);
+                            }
+                        }
                         break;
                     }
                 }
@@ -205,4 +223,55 @@
 
         Looper.loop();
     }
+
+    private class PageNotRespondingRunnable implements Runnable {
+        Context mContext;
+        private Handler mWatchdogHandler;
+
+        public PageNotRespondingRunnable(Context context, Handler watchdogHandler) {
+            mContext = context;
+            mWatchdogHandler = watchdogHandler;
+        }
+
+        @Override
+        public void run() {
+            // This must run on the UI thread as it is displaying an AlertDialog.
+            assert Looper.getMainLooper().getThread() == Thread.currentThread();
+            new AlertDialog.Builder(mContext)
+                    .setMessage(com.android.internal.R.string.webpage_unresponsive)
+                    .setPositiveButton(com.android.internal.R.string.force_close,
+                            new DialogInterface.OnClickListener() {
+                                @Override
+                                public void onClick(DialogInterface dialog, int which) {
+                                    // User chose to force close.
+                                    Process.killProcess(Process.myPid());
+                                }
+                            })
+                    .setNegativeButton(com.android.internal.R.string.wait,
+                            new DialogInterface.OnClickListener() {
+                                @Override
+                                public void onClick(DialogInterface dialog, int which) {
+                                    // The user chose to wait. The last HEARTBEAT message
+                                    // will still be in the WebCore thread's queue, so all
+                                    // we need to do is post another TIMED_OUT so that the
+                                    // user will get prompted again if the WebCore thread
+                                    // doesn't sort itself out.
+                                    mWatchdogHandler.sendMessageDelayed(
+                                            mWatchdogHandler.obtainMessage(TIMED_OUT),
+                                            SUBSEQUENT_TIMEOUT_PERIOD);
+                                }
+                            })
+                    .setOnCancelListener(
+                            new DialogInterface.OnCancelListener() {
+                                @Override
+                                public void onCancel(DialogInterface dialog) {
+                                    mWatchdogHandler.sendMessageDelayed(
+                                            mWatchdogHandler.obtainMessage(TIMED_OUT),
+                                            SUBSEQUENT_TIMEOUT_PERIOD);
+                                }
+                            })
+                    .setIcon(android.R.drawable.ic_dialog_alert)
+                    .show();
+        }
+    }
 }
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 7786564..5b266ca 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -431,14 +431,18 @@
             boolean isCharacterDelete = false;
             int textLength = text.length();
             int originalLength = original.length();
-            if (textLength > originalLength) {
-                isCharacterAdd = (textLength == originalLength + 1)
-                        && TextUtils.regionMatches(text, 0, original, 0,
-                                originalLength);
-            } else if (originalLength > textLength) {
-                isCharacterDelete = (textLength == originalLength - 1)
-                        && TextUtils.regionMatches(text, 0, original, 0,
-                                textLength);
+            int selectionStart = Selection.getSelectionStart(editable);
+            int selectionEnd = Selection.getSelectionEnd(editable);
+            if (selectionStart == selectionEnd) {
+                if (textLength > originalLength) {
+                    isCharacterAdd = (textLength == originalLength + 1)
+                            && TextUtils.regionMatches(text, 0, original, 0,
+                                    originalLength);
+                } else if (originalLength > textLength) {
+                    isCharacterDelete = (textLength == originalLength - 1)
+                            && TextUtils.regionMatches(text, 0, original, 0,
+                                    textLength);
+                }
             }
             if (isCharacterAdd) {
                 sendCharacter(text.charAt(textLength - 1));
@@ -3365,11 +3369,6 @@
                 nativeSetPauseDrawing(mNativeClass, false);
             }
         }
-        // Ensure that the watchdog has a currently valid Context to be able to display
-        // a prompt dialog. For example, if the Activity was finished whilst the WebCore
-        // thread was blocked and the Activity is started again, we may reuse the blocked
-        // thread, but we'll have a new Activity.
-        WebCoreThreadWatchdog.updateContext(mContext);
         // We get a call to onResume for new WebViews (i.e. mIsPaused will be false). We need
         // to ensure that the Watchdog thread is running for the new WebView, so call
         // it outside the if block above.
@@ -7486,6 +7485,7 @@
                     int cursorPosition = start + text.length();
                     replaceTextfieldText(start, end, text,
                             cursorPosition, cursorPosition);
+                    selectionDone();
                     break;
                 }
 
@@ -7512,7 +7512,9 @@
                 }
 
                 case CLEAR_CARET_HANDLE:
-                    selectionDone();
+                    if (mIsCaretSelection) {
+                        selectionDone();
+                    }
                     break;
 
                 case KEY_PRESS:
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 76cd1c9..af7914e 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -178,8 +178,10 @@
 
                 // Start the singleton watchdog which will monitor the WebCore thread
                 // to verify it's still processing messages.
-                WebCoreThreadWatchdog.start(context, sWebCoreHandler);
+                WebCoreThreadWatchdog.start(sWebCoreHandler);
             }
+            // Make sure the Watchdog is aware of this new WebView.
+            WebCoreThreadWatchdog.registerWebView(w);
         }
         // Create an EventHub to handle messages before and after the thread is
         // ready.
@@ -1979,6 +1981,7 @@
             mEventHub.sendMessageAtFrontOfQueue(
                     Message.obtain(null, EventHub.DESTROY));
             mEventHub.blockMessages();
+            WebCoreThreadWatchdog.unregisterWebView(mWebViewClassic);
         }
     }
 
diff --git a/core/java/android/webkit/WebViewInputDispatcher.java b/core/java/android/webkit/WebViewInputDispatcher.java
index 9eeb311..c5a86d8 100644
--- a/core/java/android/webkit/WebViewInputDispatcher.java
+++ b/core/java/android/webkit/WebViewInputDispatcher.java
@@ -822,21 +822,31 @@
     }
 
     private void enqueueEventLocked(DispatchEvent d) {
-        if (!shouldSkipWebKit(d.mEventType)) {
+        if (!shouldSkipWebKit(d)) {
             enqueueWebKitEventLocked(d);
         } else {
             enqueueUiEventLocked(d);
         }
     }
 
-    private boolean shouldSkipWebKit(int eventType) {
-        switch (eventType) {
+    private boolean shouldSkipWebKit(DispatchEvent d) {
+        switch (d.mEventType) {
             case EVENT_TYPE_CLICK:
             case EVENT_TYPE_HOVER:
             case EVENT_TYPE_SCROLL:
             case EVENT_TYPE_HIT_TEST:
                 return false;
             case EVENT_TYPE_TOUCH:
+                // TODO: This should be cleaned up. We now have WebViewInputDispatcher
+                // and WebViewClassic both checking for slop and doing their own
+                // thing - they should be consolidated. And by consolidated, I mean
+                // WebViewClassic's version should just be deleted.
+                // The reason this is done is because webpages seem to expect
+                // that they only get an ontouchmove if the slop has been exceeded.
+                if (mIsTapCandidate && d.mEvent != null
+                        && d.mEvent.getActionMasked() == MotionEvent.ACTION_MOVE) {
+                    return true;
+                }
                 return !mPostSendTouchEventsToWebKit
                         || mPostDoNotSendTouchEventsToWebKitUntilNextGesture;
         }
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 821a251b..987c0ac 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -1623,7 +1623,7 @@
                     if (line.startsWith("File")) {
                         int equals = line.indexOf('=');
                         if (equals > 0) {
-                            cachePlaylistEntry(line, playListDirectory);
+                            cachePlaylistEntry(line.substring(equals + 1), playListDirectory);
                         }
                     }
                     line = reader.readLine();