summaryrefslogtreecommitdiffstats
path: root/core/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/app/Notification.java43
-rw-r--r--core/java/android/database/sqlite/SQLiteSession.java8
-rw-r--r--core/java/android/webkit/BrowserFrame.java13
-rw-r--r--core/java/android/webkit/CacheManager.java9
-rw-r--r--core/java/android/webkit/CookieManager.java200
-rw-r--r--core/java/android/webkit/CookieSyncManager.java6
-rw-r--r--core/java/android/webkit/JniUtil.java2
-rw-r--r--core/java/android/webkit/MimeTypeMap.java1
-rw-r--r--core/java/android/webkit/QuadF.java85
-rw-r--r--core/java/android/webkit/URLUtil.java3
-rw-r--r--core/java/android/webkit/WebViewClassic.java205
-rw-r--r--core/java/android/widget/TextView.java1
12 files changed, 305 insertions, 271 deletions
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 04ab407..bbb6a4e 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -918,6 +918,8 @@ public class Notification implements Parcelable
private Bundle mExtras;
private int mPriority;
private ArrayList<Action> mActions = new ArrayList<Action>(3);
+ private boolean mCanHasIntruder;
+ private boolean mIntruderActionsShowText;
/**
* Constructs a new Builder with the defaults:
@@ -1313,6 +1315,38 @@ public class Notification implements Parcelable
return this;
}
+ /**
+ * Specify whether this notification should pop up as an
+ * "intruder alert" (a small window that shares the screen with the
+ * current activity). This sort of notification is (as the name implies)
+ * very intrusive, so use it sparingly for notifications that require
+ * the user's attention.
+ *
+ * Notes:
+ * <ul>
+ * <li>Intruder alerts only show when the screen is on.</li>
+ * <li>Intruder alerts take precedence over fullScreenIntents.</li>
+ * </ul>
+ *
+ * @param intrude Whether to pop up an intruder alert (default false).
+ */
+ public Builder setUsesIntruderAlert(boolean intrude) {
+ mCanHasIntruder = intrude;
+ return this;
+ }
+
+ /**
+ * Control text on intruder alert action buttons. By default, action
+ * buttons in intruders do not show textual labels.
+ *
+ * @param showActionText Whether to show text labels beneath action
+ * icons (default false).
+ */
+ public Builder setIntruderActionsShowText(boolean showActionText) {
+ mIntruderActionsShowText = showActionText;
+ return this;
+ }
+
private void setFlag(int mask, boolean value) {
if (value) {
mFlags |= mask;
@@ -1394,7 +1428,7 @@ public class Notification implements Parcelable
}
}
- private RemoteViews makeIntruderView() {
+ private RemoteViews makeIntruderView(boolean showLabels) {
RemoteViews intruderView = new RemoteViews(mContext.getPackageName(),
R.layout.notification_intruder_content);
if (mLargeIcon != null) {
@@ -1422,7 +1456,8 @@ public class Notification implements Parcelable
final int buttonId = BUTTONS[i];
intruderView.setViewVisibility(buttonId, View.VISIBLE);
- intruderView.setImageViewResource(buttonId, action.icon);
+ intruderView.setTextViewText(buttonId, showLabels ? action.title : null);
+ intruderView.setTextViewCompoundDrawables(buttonId, 0, action.icon, 0, 0);
intruderView.setContentDescription(buttonId, action.title);
intruderView.setOnClickPendingIntent(buttonId, action.actionIntent);
}
@@ -1457,7 +1492,9 @@ public class Notification implements Parcelable
n.ledOffMS = mLedOffMs;
n.defaults = mDefaults;
n.flags = mFlags;
- n.intruderView = makeIntruderView();
+ if (mCanHasIntruder) {
+ n.intruderView = makeIntruderView(mIntruderActionsShowText);
+ }
if (mLedOnMs != 0 && mLedOffMs != 0) {
n.flags |= FLAG_SHOW_LIGHTS;
}
diff --git a/core/java/android/database/sqlite/SQLiteSession.java b/core/java/android/database/sqlite/SQLiteSession.java
index 43efb03..9410243 100644
--- a/core/java/android/database/sqlite/SQLiteSession.java
+++ b/core/java/android/database/sqlite/SQLiteSession.java
@@ -398,16 +398,16 @@ public final class SQLiteSession {
throwIfNoTransaction();
assert mConnection != null;
- endTransactionUnchecked(cancellationSignal);
+ endTransactionUnchecked(cancellationSignal, false);
}
- private void endTransactionUnchecked(CancellationSignal cancellationSignal) {
+ private void endTransactionUnchecked(CancellationSignal cancellationSignal, boolean yielding) {
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
}
final Transaction top = mTransactionStack;
- boolean successful = top.mMarkedSuccessful && !top.mChildFailed;
+ boolean successful = (top.mMarkedSuccessful || yielding) && !top.mChildFailed;
RuntimeException listenerException = null;
final SQLiteTransactionListener listener = top.mListener;
@@ -534,7 +534,7 @@ public final class SQLiteSession {
final int transactionMode = mTransactionStack.mMode;
final SQLiteTransactionListener listener = mTransactionStack.mListener;
final int connectionFlags = mConnectionFlags;
- endTransactionUnchecked(cancellationSignal); // might throw
+ endTransactionUnchecked(cancellationSignal, true); // might throw
if (sleepAfterYieldDelayMillis > 0) {
try {
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 72af251..7b6b54c 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -692,13 +692,10 @@ class BrowserFrame extends Handler {
* @return An InputStream to the android resource
*/
private InputStream inputStreamForAndroidResource(String url) {
- // This list needs to be kept in sync with the list in
- // external/webkit/WebKit/android/WebCoreSupport/WebUrlLoaderClient.cpp
- final String ANDROID_ASSET = "file:///android_asset/";
- final String ANDROID_RESOURCE = "file:///android_res/";
- final String ANDROID_CONTENT = "content:";
+ final String ANDROID_ASSET = URLUtil.ASSET_BASE;
+ final String ANDROID_RESOURCE = URLUtil.RESOURCE_BASE;
+ final String ANDROID_CONTENT = URLUtil.CONTENT_BASE;
- // file:///android_res
if (url.startsWith(ANDROID_RESOURCE)) {
url = url.replaceFirst(ANDROID_RESOURCE, "");
if (url == null || url.length() == 0) {
@@ -736,8 +733,6 @@ class BrowserFrame extends Handler {
Log.e(LOGTAG, "Exception: " + url);
return null;
}
-
- // file:///android_asset
} else if (url.startsWith(ANDROID_ASSET)) {
url = url.replaceFirst(ANDROID_ASSET, "");
try {
@@ -747,8 +742,6 @@ class BrowserFrame extends Handler {
} catch (IOException e) {
return null;
}
-
- // content://
} else if (mSettings.getAllowContentAccess() &&
url.startsWith(ANDROID_CONTENT)) {
try {
diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java
index 6a85e00..671c064 100644
--- a/core/java/android/webkit/CacheManager.java
+++ b/core/java/android/webkit/CacheManager.java
@@ -17,25 +17,18 @@
package android.webkit;
import android.content.Context;
-import android.net.http.AndroidHttpClient;
import android.net.http.Headers;
-import android.os.FileUtils;
import android.util.Log;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.util.List;
import java.util.Map;
-import com.android.org.bouncycastle.crypto.Digest;
-import com.android.org.bouncycastle.crypto.digests.SHA1Digest;
-
/**
* Manages the HTTP cache used by an application's {@link WebView} instances.
* @deprecated Access to the HTTP cache will be removed in a future release.
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 497cab7..5f7ef41 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -18,21 +18,10 @@ package android.webkit;
import android.net.ParseException;
import android.net.WebAddress;
-import android.net.http.AndroidHttpClient;
import android.os.AsyncTask;
import android.util.Log;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
/**
* Manages the cookies used by an application's {@link WebView} instances.
* Cookies are manipulated according to RFC2109.
@@ -43,197 +32,8 @@ public final class CookieManager {
private static final String LOGTAG = "webkit";
- private static final String DOMAIN = "domain";
-
- private static final String PATH = "path";
-
- private static final String EXPIRES = "expires";
-
- private static final String SECURE = "secure";
-
- private static final String MAX_AGE = "max-age";
-
- private static final String HTTP_ONLY = "httponly";
-
- private static final String HTTPS = "https";
-
- private static final char PERIOD = '.';
-
- private static final char COMMA = ',';
-
- private static final char SEMICOLON = ';';
-
- private static final char EQUAL = '=';
-
- private static final char PATH_DELIM = '/';
-
- private static final char QUESTION_MARK = '?';
-
- private static final char WHITE_SPACE = ' ';
-
- private static final char QUOTATION = '\"';
-
- private static final int SECURE_LENGTH = SECURE.length();
-
- private static final int HTTP_ONLY_LENGTH = HTTP_ONLY.length();
-
- // RFC2109 defines 4k as maximum size of a cookie
- private static final int MAX_COOKIE_LENGTH = 4 * 1024;
-
- // RFC2109 defines 20 as max cookie count per domain. As we track with base
- // domain, we allow 50 per base domain
- private static final int MAX_COOKIE_COUNT_PER_BASE_DOMAIN = 50;
-
- // RFC2109 defines 300 as max count of domains. As we track with base
- // domain, we set 200 as max base domain count
- private static final int MAX_DOMAIN_COUNT = 200;
-
- // max cookie count to limit RAM cookie takes less than 100k, it is based on
- // average cookie entry size is less than 100 bytes
- private static final int MAX_RAM_COOKIES_COUNT = 1000;
-
- // max domain count to limit RAM cookie takes less than 100k,
- private static final int MAX_RAM_DOMAIN_COUNT = 15;
-
private int mPendingCookieOperations = 0;
- /**
- * This contains a list of 2nd-level domains that aren't allowed to have
- * wildcards when combined with country-codes. For example: [.co.uk].
- */
- private final static String[] BAD_COUNTRY_2LDS =
- { "ac", "co", "com", "ed", "edu", "go", "gouv", "gov", "info",
- "lg", "ne", "net", "or", "org" };
-
- static {
- Arrays.sort(BAD_COUNTRY_2LDS);
- }
-
- /**
- * Package level class to be accessed by cookie sync manager
- */
- static class Cookie {
- static final byte MODE_NEW = 0;
-
- static final byte MODE_NORMAL = 1;
-
- static final byte MODE_DELETED = 2;
-
- static final byte MODE_REPLACED = 3;
-
- String domain;
-
- String path;
-
- String name;
-
- String value;
-
- long expires;
-
- long lastAcessTime;
-
- long lastUpdateTime;
-
- boolean secure;
-
- byte mode;
-
- Cookie() {
- }
-
- Cookie(String defaultDomain, String defaultPath) {
- domain = defaultDomain;
- path = defaultPath;
- expires = -1;
- }
-
- boolean exactMatch(Cookie in) {
- // An exact match means that domain, path, and name are equal. If
- // both values are null, the cookies match. If both values are
- // non-null, the cookies match. If one value is null and the other
- // is non-null, the cookies do not match (i.e. "foo=;" and "foo;")
- boolean valuesMatch = !((value == null) ^ (in.value == null));
- return domain.equals(in.domain) && path.equals(in.path) &&
- name.equals(in.name) && valuesMatch;
- }
-
- boolean domainMatch(String urlHost) {
- if (domain.startsWith(".")) {
- if (urlHost.endsWith(domain.substring(1))) {
- int len = domain.length();
- int urlLen = urlHost.length();
- if (urlLen > len - 1) {
- // make sure bar.com doesn't match .ar.com
- return urlHost.charAt(urlLen - len) == PERIOD;
- }
- return true;
- }
- return false;
- } else {
- // exact match if domain is not leading w/ dot
- return urlHost.equals(domain);
- }
- }
-
- boolean pathMatch(String urlPath) {
- if (urlPath.startsWith(path)) {
- int len = path.length();
- if (len == 0) {
- Log.w(LOGTAG, "Empty cookie path");
- return false;
- }
- int urlLen = urlPath.length();
- if (path.charAt(len-1) != PATH_DELIM && urlLen > len) {
- // make sure /wee doesn't match /we
- return urlPath.charAt(len) == PATH_DELIM;
- }
- return true;
- }
- return false;
- }
-
- public String toString() {
- return "domain: " + domain + "; path: " + path + "; name: " + name
- + "; value: " + value;
- }
- }
-
- private static final CookieComparator COMPARATOR = new CookieComparator();
-
- private static final class CookieComparator implements Comparator<Cookie> {
- public int compare(Cookie cookie1, Cookie cookie2) {
- // According to RFC 2109, multiple cookies are ordered in a way such
- // that those with more specific Path attributes precede those with
- // less specific. Ordering with respect to other attributes (e.g.,
- // Domain) is unspecified.
- // As Set is not modified if the two objects are same, we do want to
- // assign different value for each cookie.
- int diff = cookie2.path.length() - cookie1.path.length();
- if (diff != 0) return diff;
-
- diff = cookie2.domain.length() - cookie1.domain.length();
- if (diff != 0) return diff;
-
- // If cookie2 has a null value, it should come later in
- // the list.
- if (cookie2.value == null) {
- // If both cookies have null values, fall back to using the name
- // difference.
- if (cookie1.value != null) {
- return -1;
- }
- } else if (cookie1.value == null) {
- // Now we know that cookie2 does not have a null value, if
- // cookie1 has a null value, place it later in the list.
- return 1;
- }
-
- // Fallback to comparing the name to ensure consistent order.
- return cookie1.name.compareTo(cookie2.name);
- }
- }
-
private CookieManager() {
}
diff --git a/core/java/android/webkit/CookieSyncManager.java b/core/java/android/webkit/CookieSyncManager.java
index 19fa096..4e99335 100644
--- a/core/java/android/webkit/CookieSyncManager.java
+++ b/core/java/android/webkit/CookieSyncManager.java
@@ -18,10 +18,7 @@ package android.webkit;
import android.content.Context;
import android.util.Log;
-import android.webkit.CookieManager.Cookie;
-import java.util.ArrayList;
-import java.util.Iterator;
/**
* The CookieSyncManager is used to synchronize the browser cookie store
@@ -62,9 +59,6 @@ public final class CookieSyncManager extends WebSyncManager {
private static CookieSyncManager sRef;
- // time when last update happened
- private long mLastUpdate;
-
private CookieSyncManager(Context context) {
super(context, "CookieSyncManager");
}
diff --git a/core/java/android/webkit/JniUtil.java b/core/java/android/webkit/JniUtil.java
index 343d34a..e3e6092 100644
--- a/core/java/android/webkit/JniUtil.java
+++ b/core/java/android/webkit/JniUtil.java
@@ -100,7 +100,7 @@ class JniUtil {
return sContext.getPackageName();
}
- private static final String ANDROID_CONTENT = "content:";
+ private static final String ANDROID_CONTENT = URLUtil.CONTENT_BASE;
/**
* Called by JNI. Calculates the size of an input stream by reading it.
diff --git a/core/java/android/webkit/MimeTypeMap.java b/core/java/android/webkit/MimeTypeMap.java
index 35483c9..da8901a 100644
--- a/core/java/android/webkit/MimeTypeMap.java
+++ b/core/java/android/webkit/MimeTypeMap.java
@@ -17,7 +17,6 @@
package android.webkit;
import android.text.TextUtils;
-import java.util.HashMap;
import java.util.regex.Pattern;
import libcore.net.MimeUtils;
diff --git a/core/java/android/webkit/QuadF.java b/core/java/android/webkit/QuadF.java
new file mode 100644
index 0000000..e9011e3
--- /dev/null
+++ b/core/java/android/webkit/QuadF.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.webkit;
+
+import android.graphics.PointF;
+
+/**
+ * A quadrilateral, determined by four points, clockwise order. Typically
+ * p1 is "top-left" and p4 is "bottom-left" following webkit's rectangle-to-
+ * FloatQuad conversion.
+ */
+class QuadF {
+ public PointF p1;
+ public PointF p2;
+ public PointF p3;
+ public PointF p4;
+
+ public QuadF() {
+ p1 = new PointF();
+ p2 = new PointF();
+ p3 = new PointF();
+ p4 = new PointF();
+ }
+
+ public void offset(float dx, float dy) {
+ p1.offset(dx, dy);
+ p2.offset(dx, dy);
+ p3.offset(dx, dy);
+ p4.offset(dx, dy);
+ }
+
+ /**
+ * Determines if the quadrilateral contains the given point. This does
+ * not work if the quadrilateral is self-intersecting or if any inner
+ * angle is reflex (greater than 180 degrees).
+ */
+ public boolean containsPoint(float x, float y) {
+ return isPointInTriangle(x, y, p1, p2, p3) ||
+ isPointInTriangle(x, y, p1, p3, p4);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder("QuadF(");
+ s.append(p1.x).append(",").append(p1.y);
+ s.append(" - ");
+ s.append(p2.x).append(",").append(p2.y);
+ s.append(" - ");
+ s.append(p3.x).append(",").append(p3.y);
+ s.append(" - ");
+ s.append(p4.x).append(",").append(p4.y);
+ s.append(")");
+ return s.toString();
+ }
+
+ private static boolean isPointInTriangle(float x0, float y0,
+ PointF r1, PointF r2, PointF r3) {
+ // Use the barycentric technique
+ float x13 = r1.x - r3.x;
+ float y13 = r1.y - r3.y;
+ float x23 = r2.x - r3.x;
+ float y23 = r2.y - r3.y;
+ float x03 = x0 - r3.x;
+ float y03 = y0 - r3.y;
+
+ float determinant = (y23 * x13) - (x23 * y13);
+ float lambda1 = ((y23 * x03) - (x23 * y03))/determinant;
+ float lambda2 = ((x13 * y03) - (y13 * x03))/determinant;
+ float lambda3 = 1 - lambda1 - lambda2;
+ return lambda1 >= 0.0f && lambda2 >= 0.0f && lambda3 >= 0.0f;
+ }
+}
diff --git a/core/java/android/webkit/URLUtil.java b/core/java/android/webkit/URLUtil.java
index 9970c93..b47f04f 100644
--- a/core/java/android/webkit/URLUtil.java
+++ b/core/java/android/webkit/URLUtil.java
@@ -38,6 +38,7 @@ public final class URLUtil {
static final String RESOURCE_BASE = "file:///android_res/";
static final String FILE_BASE = "file://";
static final String PROXY_BASE = "file:///cookieless_proxy/";
+ static final String CONTENT_BASE = "content:";
/**
* Cleans up (if possible) user-entered web addresses
@@ -253,7 +254,7 @@ public final class URLUtil {
* @return True iff the url is a content: url.
*/
public static boolean isContentUrl(String url) {
- return (null != url) && url.startsWith("content:");
+ return (null != url) && url.startsWith(CONTENT_BASE);
}
/**
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 5ae2fe0..7ddff8e 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -43,6 +43,7 @@ import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Picture;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
@@ -56,9 +57,7 @@ import android.net.http.SslCertificate;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
-import android.os.Looper;
import android.os.Message;
-import android.os.StrictMode;
import android.os.SystemClock;
import android.provider.Settings;
import android.security.KeyChain;
@@ -758,22 +757,21 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
this.setContentView(mContentView);
}
- public void show(Rect cursorRect, int windowLeft, int windowTop) {
+ public void show(Point cursorBottom, Point cursorTop,
+ int windowLeft, int windowTop) {
measureContent();
int width = mContentView.getMeasuredWidth();
int height = mContentView.getMeasuredHeight();
- int y = cursorRect.top - height;
+ int y = cursorTop.y - height;
+ int x = cursorTop.x - (width / 2);
if (y < windowTop) {
// There's not enough room vertically, move it below the
// handle.
- // The selection handle is vertically offset by 1/4 of the
- // line height.
ensureSelectionHandles();
- y = cursorRect.bottom - (cursorRect.height() / 4) +
- mSelectHandleCenter.getIntrinsicHeight();
+ y = cursorBottom.y + mSelectHandleCenter.getIntrinsicHeight();
+ x = cursorBottom.x - (width / 2);
}
- int x = cursorRect.centerX() - (width / 2);
if (x < windowLeft) {
x = windowLeft;
}
@@ -1151,12 +1149,18 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
private Drawable mSelectHandleLeft;
private Drawable mSelectHandleRight;
private Drawable mSelectHandleCenter;
- private Rect mSelectCursorBase = new Rect();
+ private Point mSelectHandleLeftOffset;
+ private Point mSelectHandleRightOffset;
+ private Point mSelectHandleCenterOffset;
+ private Point mSelectCursorBase = new Point();
private int mSelectCursorBaseLayerId;
- private Rect mSelectCursorExtent = new Rect();
+ private QuadF mSelectCursorBaseTextQuad = new QuadF();
+ private Point mSelectCursorExtent = new Point();
private int mSelectCursorExtentLayerId;
- private Rect mSelectDraggingCursor;
- private Point mSelectDraggingOffset = new Point();
+ private QuadF mSelectCursorExtentTextQuad = new QuadF();
+ private Point mSelectDraggingCursor;
+ private Point mSelectDraggingOffset;
+ private QuadF mSelectDraggingTextQuad;
private boolean mIsCaretSelection;
static final int HANDLE_ID_START = 0;
static final int HANDLE_ID_END = 1;
@@ -3894,9 +3898,11 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
if (mSelectingText) {
if (mSelectCursorBaseLayerId == mCurrentScrollingLayerId) {
mSelectCursorBase.offset(dx, dy);
+ mSelectCursorBaseTextQuad.offset(dx, dy);
}
if (mSelectCursorExtentLayerId == mCurrentScrollingLayerId) {
mSelectCursorExtent.offset(dx, dy);
+ mSelectCursorExtentTextQuad.offset(dx, dy);
}
}
if (mAutoCompletePopup != null &&
@@ -4774,6 +4780,13 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
com.android.internal.R.drawable.text_select_handle_left);
mSelectHandleRight = mContext.getResources().getDrawable(
com.android.internal.R.drawable.text_select_handle_right);
+ mSelectHandleCenterOffset = new Point(0,
+ -mSelectHandleCenter.getIntrinsicHeight());
+ mSelectHandleLeftOffset = new Point(0,
+ -mSelectHandleLeft.getIntrinsicHeight());
+ mSelectHandleRightOffset = new Point(
+ -mSelectHandleLeft.getIntrinsicWidth() / 2,
+ -mSelectHandleRight.getIntrinsicHeight());
}
}
@@ -4813,10 +4826,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
* startX, startY, endX, endY
*/
private void getSelectionHandles(int[] handles) {
- handles[0] = mSelectCursorBase.left;
- handles[1] = mSelectCursorBase.bottom;
- handles[2] = mSelectCursorExtent.left;
- handles[3] = mSelectCursorExtent.bottom;
+ handles[0] = mSelectCursorBase.x;
+ handles[1] = mSelectCursorBase.y;
+ handles[2] = mSelectCursorExtent.x;
+ handles[3] = mSelectCursorExtent.y;
if (!nativeIsBaseFirst(mNativeClass)) {
int swap = handles[0];
handles[0] = handles[2];
@@ -5332,17 +5345,66 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
ClipboardManager cm = (ClipboardManager)(mContext
.getSystemService(Context.CLIPBOARD_SERVICE));
if (cm.hasPrimaryClip()) {
- Rect cursorRect = contentToViewRect(mSelectCursorBase);
+ Point cursorPoint = new Point(contentToViewX(mSelectCursorBase.x),
+ contentToViewY(mSelectCursorBase.y));
+ Point cursorTop = calculateCaretTop();
+ cursorTop.set(contentToViewX(cursorTop.x),
+ contentToViewY(cursorTop.y));
+
int[] location = new int[2];
mWebView.getLocationInWindow(location);
- cursorRect.offset(location[0] - getScrollX(), location[1] - getScrollY());
+ int offsetX = location[0] - getScrollX();
+ int offsetY = location[1] - getScrollY();
+ cursorPoint.offset(offsetX, offsetY);
+ cursorTop.offset(offsetX, offsetY);
if (mPasteWindow == null) {
mPasteWindow = new PastePopupWindow();
}
- mPasteWindow.show(cursorRect, location[0], location[1]);
+ mPasteWindow.show(cursorPoint, cursorTop, location[0], location[1]);
}
}
+ /**
+ * Given segment AB, this finds the point C along AB that is closest to
+ * point and then returns it scale along AB. The scale factor is AC/AB.
+ *
+ * @param x The x coordinate of the point near segment AB that determines
+ * the scale factor.
+ * @param y The y coordinate of the point near segment AB that determines
+ * the scale factor.
+ * @param a The first point of the line segment.
+ * @param b The second point of the line segment.
+ * @return The scale factor AC/AB, where C is the point on AB closest to
+ * point.
+ */
+ private static float scaleAlongSegment(int x, int y, PointF a, PointF b) {
+ // The bottom line of the text box is line AB
+ float abX = b.x - a.x;
+ float abY = b.y - a.y;
+ float ab2 = (abX * abX) + (abY * abY);
+
+ // The line from first point in text bounds to bottom is AP
+ float apX = x - a.x;
+ float apY = y - a.y;
+ float abDotAP = (apX * abX) + (apY * abY);
+ float scale = abDotAP / ab2;
+ return scale;
+ }
+
+ /**
+ * Assuming arbitrary shape of a quadralateral forming text bounds, this
+ * calculates the top of a caret.
+ */
+ private Point calculateCaretTop() {
+ float scale = scaleAlongSegment(mSelectCursorBase.x, mSelectCursorBase.y,
+ mSelectCursorBaseTextQuad.p4, mSelectCursorBaseTextQuad.p3);
+ int x = Math.round(scaleCoordinate(scale,
+ mSelectCursorBaseTextQuad.p1.x, mSelectCursorBaseTextQuad.p2.x));
+ int y = Math.round(scaleCoordinate(scale,
+ mSelectCursorBaseTextQuad.p1.y, mSelectCursorBaseTextQuad.p2.y));
+ return new Point(x, y);
+ }
+
private void hidePasteButton() {
if (mPasteWindow != null) {
mPasteWindow.hide();
@@ -5351,9 +5413,54 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
private void syncSelectionCursors() {
mSelectCursorBaseLayerId =
- nativeGetHandleLayerId(mNativeClass, HANDLE_ID_BASE, mSelectCursorBase);
+ nativeGetHandleLayerId(mNativeClass, HANDLE_ID_BASE,
+ mSelectCursorBase, mSelectCursorBaseTextQuad);
mSelectCursorExtentLayerId =
- nativeGetHandleLayerId(mNativeClass, HANDLE_ID_EXTENT, mSelectCursorExtent);
+ nativeGetHandleLayerId(mNativeClass, HANDLE_ID_EXTENT,
+ mSelectCursorExtent, mSelectCursorExtentTextQuad);
+ }
+
+ private void adjustSelectionCursors() {
+ boolean wasDraggingStart = (mSelectDraggingCursor == mSelectCursorBase);
+ int oldX = mSelectDraggingCursor.x;
+ int oldY = mSelectDraggingCursor.y;
+ int oldStartX = mSelectCursorBase.x;
+ int oldStartY = mSelectCursorBase.y;
+ int oldEndX = mSelectCursorExtent.x;
+ int oldEndY = mSelectCursorExtent.y;
+
+ syncSelectionCursors();
+ boolean dragChanged = oldX != mSelectDraggingCursor.x ||
+ oldY != mSelectDraggingCursor.y;
+ if (dragChanged && !mIsCaretSelection) {
+ boolean draggingStart;
+ if (wasDraggingStart) {
+ float endStart = distanceSquared(oldEndX, oldEndY,
+ mSelectCursorBase);
+ float endEnd = distanceSquared(oldEndX, oldEndY,
+ mSelectCursorExtent);
+ draggingStart = endStart > endEnd;
+ } else {
+ float startStart = distanceSquared(oldStartX, oldStartY,
+ mSelectCursorBase);
+ float startEnd = distanceSquared(oldStartX, oldStartY,
+ mSelectCursorExtent);
+ draggingStart = startStart > startEnd;
+ }
+ mSelectDraggingCursor = (draggingStart
+ ? mSelectCursorBase : mSelectCursorExtent);
+ mSelectDraggingTextQuad = (draggingStart
+ ? mSelectCursorBaseTextQuad : mSelectCursorExtentTextQuad);
+ mSelectDraggingOffset = (draggingStart
+ ? mSelectHandleLeftOffset : mSelectHandleRightOffset);
+ }
+ mSelectDraggingCursor.set(oldX, oldY);
+ }
+
+ private float distanceSquared(int x, int y, Point p) {
+ float dx = p.x - x;
+ float dy = p.y - y;
+ return (dx * dx) + (dy * dy);
}
private boolean setupWebkitSelect() {
@@ -5370,14 +5477,14 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
private void updateWebkitSelection() {
int[] handles = null;
if (mIsCaretSelection) {
- mSelectCursorExtent.set(mSelectCursorBase);
+ mSelectCursorExtent.set(mSelectCursorBase.x, mSelectCursorBase.y);
}
if (mSelectingText) {
handles = new int[4];
- handles[0] = mSelectCursorBase.centerX();
- handles[1] = mSelectCursorBase.centerY();
- handles[2] = mSelectCursorExtent.centerX();
- handles[3] = mSelectCursorExtent.centerY();
+ handles[0] = mSelectCursorBase.x;
+ handles[1] = mSelectCursorBase.y;
+ handles[2] = mSelectCursorExtent.x;
+ handles[3] = mSelectCursorExtent.y;
} else {
nativeSetTextSelection(mNativeClass, 0);
}
@@ -5968,12 +6075,15 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
}
mSelectionStarted = false;
if (mSelectingText) {
+ ensureSelectionHandles();
int shiftedY = y - getTitleHeight() + getScrollY();
int shiftedX = x + getScrollX();
if (mSelectHandleCenter != null && mSelectHandleCenter.getBounds()
.contains(shiftedX, shiftedY)) {
mSelectionStarted = true;
mSelectDraggingCursor = mSelectCursorBase;
+ mSelectDraggingOffset = mSelectHandleCenterOffset;
+ mSelectDraggingTextQuad = mSelectCursorBaseTextQuad;
mPrivateHandler.removeMessages(CLEAR_CARET_HANDLE);
hidePasteButton();
} else if (mSelectHandleLeft != null
@@ -5981,19 +6091,18 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
.contains(shiftedX, shiftedY)) {
mSelectionStarted = true;
mSelectDraggingCursor = mSelectCursorBase;
+ mSelectDraggingOffset = mSelectHandleLeftOffset;
+ mSelectDraggingTextQuad = mSelectCursorBaseTextQuad;
} else if (mSelectHandleRight != null
&& mSelectHandleRight.getBounds()
.contains(shiftedX, shiftedY)) {
mSelectionStarted = true;
mSelectDraggingCursor = mSelectCursorExtent;
+ mSelectDraggingOffset = mSelectHandleRightOffset;
+ mSelectDraggingTextQuad = mSelectCursorExtentTextQuad;
} else if (mIsCaretSelection) {
selectionDone();
}
- if (mSelectDraggingCursor != null) {
- mSelectDraggingOffset.set(
- mSelectDraggingCursor.left - contentX,
- mSelectDraggingCursor.top - contentY);
- }
if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "select=" + contentX + "," + contentY);
}
@@ -6073,9 +6182,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
parent.requestDisallowInterceptTouchEvent(true);
}
if (deltaX != 0 || deltaY != 0) {
- mSelectDraggingCursor.offsetTo(
- contentX + mSelectDraggingOffset.x,
- contentY + mSelectDraggingOffset.y);
+ snapDraggingCursor(contentX, contentY);
updateWebkitSelection();
mLastTouchX = x;
mLastTouchY = y;
@@ -6684,6 +6791,30 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
mTouchMode = TOUCH_DONE_MODE;
}
+ private void snapDraggingCursor(int x, int y) {
+ x += viewToContentDimension(mSelectDraggingOffset.x);
+ y += viewToContentDimension(mSelectDraggingOffset.y);
+ if (mSelectDraggingTextQuad.containsPoint(x, y)) {
+ float scale = scaleAlongSegment(x, y,
+ mSelectDraggingTextQuad.p4, mSelectDraggingTextQuad.p3);
+ // clamp scale to ensure point is on the bottom segment
+ scale = Math.max(0.0f, scale);
+ scale = Math.min(scale, 1.0f);
+ float newX = scaleCoordinate(scale,
+ mSelectDraggingTextQuad.p4.x, mSelectDraggingTextQuad.p3.x);
+ float newY = scaleCoordinate(scale,
+ mSelectDraggingTextQuad.p4.y, mSelectDraggingTextQuad.p3.y);
+ mSelectDraggingCursor.set(Math.round(newX), Math.round(newY));
+ } else {
+ mSelectDraggingCursor.set(x, y);
+ }
+ }
+
+ private static float scaleCoordinate(float scale, float coord1, float coord2) {
+ float diff = coord2 - coord1;
+ return coord1 + (scale * diff);
+ }
+
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
@@ -8735,6 +8866,8 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
setupWebkitSelect();
} else if (!mSelectionStarted) {
syncSelectionCursors();
+ } else {
+ adjustSelectionCursors();
}
if (mIsCaretSelection) {
resetCaretTimer();
@@ -9398,7 +9531,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
private static native void nativeSetPauseDrawing(int instance, boolean pause);
private static native void nativeSetTextSelection(int instance, int selection);
private static native int nativeGetHandleLayerId(int instance, int handle,
- Rect cursorLocation);
+ Point cursorLocation, QuadF textQuad);
private static native boolean nativeIsBaseFirst(int instance);
private static native void nativeMapLayerRect(int instance, int layerId,
Rect rect);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 2c7a120..4bdb3e2 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -5251,7 +5251,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
- createEditorIfNeeded("onCreateInputConnection");
if (onCheckIsTextEditor() && isEnabled()) {
if (getEditor().mInputMethodState == null) {
getEditor().mInputMethodState = new InputMethodState();