summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rwxr-xr-xcore/java/android/animation/ValueAnimator.java4
-rw-r--r--core/java/android/app/ActivityManager.java29
-rw-r--r--core/java/android/app/AlertDialog.java16
-rw-r--r--core/java/android/app/ApplicationErrorReport.java23
-rw-r--r--core/java/android/app/ContextImpl.java3
-rw-r--r--core/java/android/app/DialogFragment.java2
-rw-r--r--core/java/android/content/res/AssetManager.java4
-rwxr-xr-xcore/java/android/content/res/Resources.java14
-rw-r--r--core/java/android/content/res/TypedArray.java2
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java4
-rw-r--r--core/java/android/net/Uri.java9
-rw-r--r--core/java/android/os/Build.java9
-rw-r--r--core/java/android/os/HandlerThread.java6
-rw-r--r--core/java/android/os/Looper.java2
-rw-r--r--core/java/android/preference/VolumePreference.java4
-rw-r--r--core/java/android/provider/CallLog.java3
-rw-r--r--core/java/android/provider/ContactsContract.java126
-rw-r--r--core/java/android/server/BluetoothAdapterStateMachine.java125
-rw-r--r--core/java/android/server/BluetoothEventLoop.java21
-rwxr-xr-xcore/java/android/server/BluetoothService.java68
-rw-r--r--core/java/android/text/TextLine.java33
-rw-r--r--core/java/android/text/TextPaint.java30
-rw-r--r--core/java/android/util/XmlPullAttributes.java2
-rw-r--r--core/java/android/view/Display.java6
-rw-r--r--core/java/android/view/GLES20Canvas.java7
-rw-r--r--core/java/android/view/HardwareRenderer.java10
-rw-r--r--core/java/android/view/LayoutInflater.java6
-rw-r--r--core/java/android/view/View.java30
-rw-r--r--core/java/android/view/ViewConfiguration.java2
-rw-r--r--core/java/android/view/ViewRootImpl.java4
-rw-r--r--core/java/android/webkit/WebView.java5
-rw-r--r--core/java/android/webkit/webdriver/By.java252
-rw-r--r--core/java/android/webkit/webdriver/WebDriver.java843
-rw-r--r--core/java/android/webkit/webdriver/WebDriverException.java38
-rw-r--r--core/java/android/webkit/webdriver/WebElement.java388
-rw-r--r--core/java/android/webkit/webdriver/WebElementNotFoundException.java41
-rw-r--r--core/java/android/webkit/webdriver/WebElementStaleException.java42
-rw-r--r--core/java/android/webkit/webdriver/WebViewClient.java125
-rw-r--r--core/java/android/webkit/webdriver/WebchromeClientWrapper.java193
-rw-r--r--core/java/android/widget/AbsSeekBar.java53
-rw-r--r--core/java/android/widget/SeekBar.java2
-rw-r--r--core/java/android/widget/TextView.java103
-rw-r--r--core/java/com/android/internal/app/ActionBarImpl.java11
-rw-r--r--core/java/com/android/internal/os/ProcessStats.java6
-rw-r--r--core/java/com/android/internal/view/menu/ActionMenuPresenter.java6
-rw-r--r--core/java/com/android/internal/view/menu/ActionMenuView.java86
-rw-r--r--core/java/com/android/internal/widget/ActionBarContainer.java5
-rw-r--r--core/java/com/android/internal/widget/ActionBarContextView.java35
-rw-r--r--core/java/com/android/internal/widget/ActionBarView.java42
-rw-r--r--core/java/com/android/internal/widget/ScrollingTabContainerView.java16
50 files changed, 653 insertions, 2243 deletions
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 5df8bdc..edd0fa3 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -54,8 +54,8 @@ public class ValueAnimator extends Animator {
* Messages sent to timing handler: START is sent when an animation first begins, FRAME is sent
* by the handler to itself to process the next animation frame
*/
- private static final int ANIMATION_START = 0;
- private static final int ANIMATION_FRAME = 1;
+ static final int ANIMATION_START = 0;
+ static final int ANIMATION_FRAME = 1;
/**
* Values used with internal variable mPlayingState to indicate the current state of an
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index f7e5cf1..93e30af 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -18,6 +18,7 @@ package android.app;
import com.android.internal.app.IUsageStats;
import com.android.internal.os.PkgUsageStats;
+import com.android.internal.util.MemInfoReader;
import android.content.ComponentName;
import android.content.Context;
@@ -28,6 +29,7 @@ import android.content.pm.IPackageDataObserver;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Point;
import android.os.Debug;
import android.os.Handler;
import android.os.Parcel;
@@ -38,6 +40,8 @@ import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.util.Slog;
+import android.view.Display;
import java.util.ArrayList;
import java.util.HashMap;
@@ -206,6 +210,31 @@ public class ActivityManager {
}
/**
+ * Used by persistent processes to determine if they are running on a
+ * higher-end device so should be okay using hardware drawing acceleration
+ * (which tends to consume a lot more RAM).
+ * @hide
+ */
+ static public boolean isHighEndGfx(Display display) {
+ MemInfoReader reader = new MemInfoReader();
+ reader.readMemInfo();
+ if (reader.getTotalSize() >= (512*1024*1024)) {
+ // If the device has at least 512MB RAM available to the kernel,
+ // we can afford the overhead of graphics acceleration.
+ return true;
+ }
+ Point p = new Point();
+ display.getRealSize(p);
+ int pixels = p.x * p.y;
+ if (pixels >= (1024*600)) {
+ // If this is a sufficiently large screen, then there are enough
+ // pixels on it that we'd really like to use hw drawing.
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Information you can retrieve about tasks that the user has most recently
* started or visited.
*/
diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java
index 491fcfe..c09e87f 100644
--- a/core/java/android/app/AlertDialog.java
+++ b/core/java/android/app/AlertDialog.java
@@ -75,6 +75,18 @@ public class AlertDialog extends Dialog implements DialogInterface {
* the holographic alert theme with a light background.
*/
public static final int THEME_HOLO_LIGHT = 3;
+
+ /**
+ * Special theme constant for {@link #AlertDialog(Context, int)}: use
+ * the device's default alert theme with a dark background.
+ */
+ public static final int THEME_DEVICE_DEFAULT_DARK = 4;
+
+ /**
+ * Special theme constant for {@link #AlertDialog(Context, int)}: use
+ * the device's default alert theme with a dark background.
+ */
+ public static final int THEME_DEVICE_DEFAULT_LIGHT = 5;
protected AlertDialog(Context context) {
this(context, resolveDialogTheme(context, 0), true);
@@ -113,6 +125,10 @@ public class AlertDialog extends Dialog implements DialogInterface {
return com.android.internal.R.style.Theme_Holo_Dialog_Alert;
} else if (resid == THEME_HOLO_LIGHT) {
return com.android.internal.R.style.Theme_Holo_Light_Dialog_Alert;
+ } else if (resid == THEME_DEVICE_DEFAULT_DARK) {
+ return com.android.internal.R.style.Theme_DeviceDefault_Dialog_Alert;
+ } else if (resid == THEME_DEVICE_DEFAULT_LIGHT) {
+ return com.android.internal.R.style.Theme_DeviceDefault_Light_Dialog_Alert;
} else if (resid >= 0x01000000) { // start of real resource IDs.
return resid;
} else {
diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java
index 6524c9a..588125d 100644
--- a/core/java/android/app/ApplicationErrorReport.java
+++ b/core/java/android/app/ApplicationErrorReport.java
@@ -332,20 +332,31 @@ public class ApplicationErrorReport implements Parcelable {
exceptionMessage = tr.getMessage();
// Populate fields with the "root cause" exception
+ Throwable rootTr = tr;
while (tr.getCause() != null) {
tr = tr.getCause();
+ if (tr.getStackTrace() != null && tr.getStackTrace().length > 0) {
+ rootTr = tr;
+ }
String msg = tr.getMessage();
if (msg != null && msg.length() > 0) {
exceptionMessage = msg;
}
}
- exceptionClassName = tr.getClass().getName();
- StackTraceElement trace = tr.getStackTrace()[0];
- throwFileName = trace.getFileName();
- throwClassName = trace.getClassName();
- throwMethodName = trace.getMethodName();
- throwLineNumber = trace.getLineNumber();
+ exceptionClassName = rootTr.getClass().getName();
+ if (rootTr.getStackTrace().length > 0) {
+ StackTraceElement trace = rootTr.getStackTrace()[0];
+ throwFileName = trace.getFileName();
+ throwClassName = trace.getClassName();
+ throwMethodName = trace.getMethodName();
+ throwLineNumber = trace.getLineNumber();
+ } else {
+ throwFileName = "unknown";
+ throwClassName = "unknown";
+ throwMethodName = "unknown";
+ throwLineNumber = 0;
+ }
}
/**
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index b4bdb2f..2139704 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -364,7 +364,8 @@ class ContextImpl extends Context {
Resources.selectSystemTheme(0,
outerContext.getApplicationInfo().targetSdkVersion,
com.android.internal.R.style.Theme_Dialog,
- com.android.internal.R.style.Theme_Holo_Dialog)),
+ com.android.internal.R.style.Theme_Holo_Dialog,
+ com.android.internal.R.style.Theme_DeviceDefault_Dialog)),
ctx.mMainThread.getHandler());
}});
diff --git a/core/java/android/app/DialogFragment.java b/core/java/android/app/DialogFragment.java
index cce7cd6..8921578 100644
--- a/core/java/android/app/DialogFragment.java
+++ b/core/java/android/app/DialogFragment.java
@@ -204,7 +204,7 @@ public class DialogFragment extends Fragment
public void setStyle(int style, int theme) {
mStyle = style;
if (mStyle == STYLE_NO_FRAME || mStyle == STYLE_NO_INPUT) {
- mTheme = com.android.internal.R.style.Theme_Holo_Dialog_NoFrame;
+ mTheme = com.android.internal.R.style.Theme_DeviceDefault_Dialog_NoFrame;
}
if (theme != 0) {
mTheme = theme;
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 931cb18..ffefaa2 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -62,7 +62,7 @@ public final class AssetManager {
private static final boolean DEBUG_REFS = false;
private static final Object sSync = new Object();
- private static AssetManager sSystem = null;
+ /*package*/ static AssetManager sSystem = null;
private final TypedValue mValue = new TypedValue();
private final long[] mOffsets = new long[2];
@@ -252,7 +252,7 @@ public final class AssetManager {
}
}
- private final void makeStringBlocks(boolean copyFromSystem) {
+ /*package*/ final void makeStringBlocks(boolean copyFromSystem) {
final int sysNum = copyFromSystem ? sSystem.mStringBlocks.length : 0;
final int num = getStringBlockCount();
mStringBlocks = new StringBlock[num];
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 324c9fd..24f8319 100755
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -77,7 +77,7 @@ public class Resources {
private static final int ID_OTHER = 0x01000004;
private static final Object mSync = new Object();
- private static Resources mSystem = null;
+ /*package*/ static Resources mSystem = null;
// Information about preloaded resources. Note that they are not
// protected by a lock, because while preloading in zygote we are all
@@ -134,18 +134,24 @@ public class Resources {
/** @hide */
public static int selectDefaultTheme(int curTheme, int targetSdkVersion) {
return selectSystemTheme(curTheme, targetSdkVersion,
- com.android.internal.R.style.Theme, com.android.internal.R.style.Theme_Holo);
+ com.android.internal.R.style.Theme,
+ com.android.internal.R.style.Theme_Holo,
+ com.android.internal.R.style.Theme_DeviceDefault);
}
/** @hide */
- public static int selectSystemTheme(int curTheme, int targetSdkVersion, int orig, int holo) {
+ public static int selectSystemTheme(int curTheme, int targetSdkVersion,
+ int orig, int holo, int deviceDefault) {
if (curTheme != 0) {
return curTheme;
}
if (targetSdkVersion < Build.VERSION_CODES.HONEYCOMB) {
return orig;
}
- return holo;
+ if (targetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ return holo;
+ }
+ return deviceDefault;
}
/**
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index 37fdeb6..2df492e 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -42,7 +42,7 @@ public class TypedArray {
/*package*/ int[] mData;
/*package*/ int[] mIndices;
/*package*/ int mLength;
- private TypedValue mValue = new TypedValue();
+ /*package*/ TypedValue mValue = new TypedValue();
/**
* Return the number of values in this array.
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 9481a88..370e22a 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -617,7 +617,9 @@ public class InputMethodService extends AbstractInputMethodService {
@Override public void onCreate() {
mTheme = Resources.selectSystemTheme(mTheme,
getApplicationInfo().targetSdkVersion,
- android.R.style.Theme_InputMethod, android.R.style.Theme_Holo_InputMethod);
+ android.R.style.Theme_InputMethod,
+ android.R.style.Theme_Holo,
+ android.R.style.Theme_DeviceDefault_InputMethod);
super.setTheme(mTheme);
super.onCreate();
mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 3b21590..2c875c8 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -1689,7 +1689,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
return HierarchicalUri.readFrom(in);
}
- throw new AssertionError("Unknown URI type: " + type);
+ throw new IllegalArgumentException("Unknown URI type: " + type);
}
public Uri[] newArray(int size) {
@@ -1996,7 +1996,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
parcel.writeInt(Representation.DECODED);
parcel.writeString(decoded);
} else {
- throw new AssertionError();
+ throw new IllegalArgumentException("Neither encoded nor decoded");
}
}
}
@@ -2037,7 +2037,8 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
case Representation.DECODED:
return fromDecoded(parcel.readString());
default:
- throw new AssertionError();
+ throw new IllegalArgumentException("Unknown representation: "
+ + representation);
}
}
@@ -2221,7 +2222,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
case Representation.DECODED:
return fromDecoded(parcel.readString());
default:
- throw new AssertionError();
+ throw new IllegalArgumentException("Bad representation: " + representation);
}
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 6c7c58d..1e9ee7c 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -287,6 +287,15 @@ public class Build {
* {@link android.R.attr#hardwareAccelerated android:hardwareAccelerated}
* to turn it off if needed, although this is strongly discouraged since
* it will result in poor performance on larger screen devices.
+ * <li> The default theme for applications is now the "device default" theme:
+ * {@link android.R.style#Theme_DeviceDefault}. This may be the
+ * holo dark theme or a different dark theme defined by the specific device.
+ * The {@link android.R.style#Theme_Holo} family must not be modified
+ * for a device to be considered compatible. Applications that explicitly
+ * request a theme from the Holo family will be guaranteed that these themes
+ * will not change character within the same platform version. Applications
+ * that wish to blend in with the device should use a theme from the
+ * {@link android.R.style#Theme_DeviceDefault} family.
* </ul>
*/
public static final int ICE_CREAM_SANDWICH = CUR_DEVELOPMENT;
diff --git a/core/java/android/os/HandlerThread.java b/core/java/android/os/HandlerThread.java
index 911439a..d61b3b4 100644
--- a/core/java/android/os/HandlerThread.java
+++ b/core/java/android/os/HandlerThread.java
@@ -21,9 +21,9 @@ package android.os;
* used to create handler classes. Note that start() must still be called.
*/
public class HandlerThread extends Thread {
- private int mPriority;
- private int mTid = -1;
- private Looper mLooper;
+ int mPriority;
+ int mTid = -1;
+ Looper mLooper;
public HandlerThread(String name) {
super(name);
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index c61f28a..5607f7f 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -54,7 +54,7 @@ public class Looper {
private static final String TAG = "Looper";
// sThreadLocal.get() will return null unless you've called prepare().
- private static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
+ static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
final MessageQueue mQueue;
final Thread mThread;
diff --git a/core/java/android/preference/VolumePreference.java b/core/java/android/preference/VolumePreference.java
index b48e8ce..fe5e76c 100644
--- a/core/java/android/preference/VolumePreference.java
+++ b/core/java/android/preference/VolumePreference.java
@@ -114,7 +114,9 @@ public class VolumePreference extends SeekBarDialogPreference implements
}
public void onActivityStop() {
- cleanup();
+ if (mSeekBarVolumizer != null) {
+ mSeekBarVolumizer.stopSample();
+ }
}
/**
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 9c6f5c9..c5a924b 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -241,6 +241,9 @@ public class CallLog {
values.put(DATE, Long.valueOf(start));
values.put(DURATION, Long.valueOf(duration));
values.put(NEW, Integer.valueOf(1));
+ if (callType == MISSED_TYPE) {
+ values.put(IS_READ, Integer.valueOf(0));
+ }
if (ci != null) {
values.put(CACHED_NAME, ci.name);
values.put(CACHED_NUMBER_TYPE, ci.numberType);
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 5765dde..d867e35 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -2890,8 +2890,6 @@ public final class ContactsContract {
* values.put(StreamItems.TEXT, "Breakfasted at Tiffanys");
* values.put(StreamItems.TIMESTAMP, timestamp);
* values.put(StreamItems.COMMENTS, "3 people reshared this");
- * values.put(StreamItems.ACTION, action);
- * values.put(StreamItems.ACTION_URI, actionUri);
* Uri streamItemUri = getContentResolver().insert(
* Uri.withAppendedPath(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
* RawContacts.StreamItems.CONTENT_DIRECTORY), values);
@@ -2905,8 +2903,6 @@ public final class ContactsContract {
* values.put(StreamItems.TEXT, "Breakfasted at Tiffanys");
* values.put(StreamItems.TIMESTAMP, timestamp);
* values.put(StreamItems.COMMENTS, "3 people reshared this");
- * values.put(StreamItems.ACTION, action);
- * values.put(StreamItems.ACTION_URI, actionUri);
* Uri streamItemUri = getContentResolver().insert(StreamItems.CONTENT_URI, values);
* long streamItemId = ContentUris.parseId(streamItemUri);
* </dd>
@@ -2924,8 +2920,6 @@ public final class ContactsContract {
* values.clear();
* values.put(StreamItemPhotos.SORT_INDEX, 1);
* values.put(StreamItemPhotos.PHOTO, photoData);
- * values.put(StreamItemPhotos.ACTION, action);
- * values.put(StreamItemPhotos.ACTION_URI, actionUri);
* getContentResolver().insert(Uri.withAppendedPath(
* ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId),
* StreamItems.StreamItemPhotos.CONTENT_DIRECTORY), values);
@@ -2938,8 +2932,6 @@ public final class ContactsContract {
* values.put(StreamItemPhotos.STREAM_ITEM_ID, streamItemId);
* values.put(StreamItemPhotos.SORT_INDEX, 1);
* values.put(StreamItemPhotos.PHOTO, photoData);
- * values.put(StreamItemPhotos.ACTION, action);
- * values.put(StreamItemPhotos.ACTION_URI, actionUri);
* getContentResolver().insert(StreamItems.CONTENT_PHOTO_URI, values);
* </pre>
* Note that this latter form allows the insertion of a stream item and its
@@ -3081,16 +3073,56 @@ public final class ContactsContract {
public static final String RES_PACKAGE = "res_package";
/**
- * The resource ID of the icon for the source of the stream item.
- * This resource should be scoped by the {@link #RES_PACKAGE}.
- * <P>Type: NUMBER</P>
+ * The account type to which the raw_contact of this item is associated. See
+ * {@link RawContacts#ACCOUNT_TYPE}
+ *
+ * <p>TYPE: text</p>
+ * <p>read-only</p>
+ */
+ public static final String ACCOUNT_TYPE = "account_type";
+
+ /**
+ * The account name to which the raw_contact of this item is associated. See
+ * {@link RawContacts#ACCOUNT_NAME}
+ *
+ * <p>TYPE: text</p>
+ * <p>read-only</p>
+ */
+ public static final String ACCOUNT_NAME = "account_name";
+
+ /**
+ * The data set within the account that the raw_contact of this row belongs to. This allows
+ * multiple sync adapters for the same account type to distinguish between
+ * each others' data.
+ * {@link RawContacts#DATA_SET}
+ *
+ * <P>Type: TEXT</P>
+ * <p>read-only</p>
+ */
+ public static final String DATA_SET = "data_set";
+
+ /**
+ * The source_id of the raw_contact that this row belongs to.
+ * {@link RawContacts#SOURCE_ID}
+ *
+ * <P>Type: TEXT</P>
+ * <p>read-only</p>
+ */
+ public static final String RAW_CONTACT_SOURCE_ID = "raw_contact_source_id";
+
+ /**
+ * The resource name of the icon for the source of the stream item.
+ * This resource should be scoped by the {@link #RES_PACKAGE}. As this can only reference
+ * drawables, the "@drawable/" prefix must be omitted.
+ * <P>Type: TEXT</P>
*/
public static final String RES_ICON = "icon";
/**
- * The resource ID of the label describing the source of the status update, e.g. "Google
- * Talk". This resource should be scoped by the {@link #RES_PACKAGE}.
- * <p>Type: NUMBER</p>
+ * The resource name of the label describing the source of the status update, e.g. "Google
+ * Talk". This resource should be scoped by the {@link #RES_PACKAGE}. As this can only
+ * reference strings, the "@string/" prefix must be omitted.
+ * <p>Type: TEXT</p>
*/
public static final String RES_LABEL = "label";
@@ -3136,18 +3168,14 @@ public final class ContactsContract {
*/
public static final String COMMENTS = "comments";
- /**
- * The activity action to execute when the item is tapped.
- * <P>Type: TEXT</P>
- */
- public static final String ACTION = "action";
-
- /**
- * The URI that is launched when the item is pressed. May be handled by
- * the source app, but could also reference a website (e.g. YouTube).
- * <P>Type: TEXT</P>
- */
- public static final String ACTION_URI = "action_uri";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC1 = "stream_item_sync1";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC2 = "stream_item_sync2";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC3 = "stream_item_sync3";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC4 = "stream_item_sync4";
}
/**
@@ -3171,8 +3199,6 @@ public final class ContactsContract {
* ContentValues values = new ContentValues();
* values.put(StreamItemPhotos.SORT_INDEX, 1);
* values.put(StreamItemPhotos.PHOTO, photoData);
- * values.put(StreamItemPhotos.ACTION, action);
- * values.put(StreamItemPhotos.ACTION_URI, actionUri);
* Uri photoUri = getContentResolver().insert(Uri.withAppendedPath(
* ContentUris.withAppendedId(StreamItems.CONTENT_URI, streamItemId)
* StreamItems.StreamItemPhotos#CONTENT_DIRECTORY), values);
@@ -3186,8 +3212,6 @@ public final class ContactsContract {
* values.put(StreamItemPhotos.STREAM_ITEM_ID, streamItemId);
* values.put(StreamItemPhotos.SORT_INDEX, 1);
* values.put(StreamItemPhotos.PHOTO, photoData);
- * values.put(StreamItemPhotos.ACTION, action);
- * values.put(StreamItemPhotos.ACTION_URI, actionUri);
* Uri photoUri = getContentResolver().insert(StreamItems.CONTENT_PHOTO_URI, values);
* long photoId = ContentUris.parseId(photoUri);
* </pre>
@@ -3353,18 +3377,14 @@ public final class ContactsContract {
*/
public static final String PHOTO_URI = "photo_uri";
- /**
- * The activity action to execute when the photo is tapped.
- * <P>Type: TEXT</P>
- */
- public static final String ACTION = "action";
-
- /**
- * The URI that is launched when the photo is pressed. May be handled by
- * the source app, but could also reference a website (e.g. YouTube).
- * <P>Type: TEXT</P>
- */
- public static final String ACTION_URI = "action_uri";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC1 = "stream_item_photo_sync1";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC2 = "stream_item_photo_sync2";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC3 = "stream_item_photo_sync3";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC4 = "stream_item_photo_sync4";
}
/**
@@ -6496,28 +6516,6 @@ public final class ContactsContract {
public static final String NOTES = "notes";
/**
- * The Activity action to open the group in the source app (e.g.
- * {@link Intent#ACTION_VIEW}). Can be NULL if the group does not have a dedicated viewer.
- * This is used in conjunction with {@link #ACTION_URI}: In order to show an "Open in
- * (sourceapp)"-button, both of these fields must be set
- * <p>
- * Type: TEXT
- */
- public static final String ACTION = "action";
-
-
- /**
- * Uri to open the group in the source app.
- * Can be NULL if the group does not have a dedicated viewer.
- * This is used in conjunction with {@link #ACTION}: In order to show an "Open in
- * (sourceapp)"-button, both of these fields must be set
- * <p>
- * Type: TEXT
- */
- public static final String ACTION_URI = "action_uri";
-
-
- /**
* The ID of this group if it is a System Group, i.e. a group that has a special meaning
* to the sync adapter, null otherwise.
* <P>Type: TEXT</P>
diff --git a/core/java/android/server/BluetoothAdapterStateMachine.java b/core/java/android/server/BluetoothAdapterStateMachine.java
index 031375e..e15e61f 100644
--- a/core/java/android/server/BluetoothAdapterStateMachine.java
+++ b/core/java/android/server/BluetoothAdapterStateMachine.java
@@ -39,14 +39,14 @@ import java.io.PrintWriter;
* (BluetootOn)<----------------------<-
* | ^ -------------------->- |
* | | | |
- * TURN_OFF | | BECAME_PAIRABLE m1 | | USER_TURN_ON
+ * TURN_OFF | | SCAN_MODE_CHANGED m1 | | USER_TURN_ON
* AIRPLANE_MODE_ON | | | |
* V | | |
* (Switching) (PerProcessState)
* | ^ | |
- * BECAME_NON_PAIRABLE& | | TURN_ON(_CONTINUE) | |
+ * POWER_STATE_CHANGED & | | TURN_ON(_CONTINUE) | |
* ALL_DEVICES_DISCONNECTED | | m2 | |
- * V |------------------------< | BECAME_PAIRABLE
+ * V |------------------------< | SCAN_MODE_CHANGED
* (HotOff)-------------------------->- PER_PROCESS_TURN_ON
* / ^
* / | SERVICE_RECORD_LOADED
@@ -61,7 +61,7 @@ import java.io.PrintWriter;
* Legend:
* m1 = TURN_HOT
* m2 = Transition to HotOff when number of process wanting BT on is 0.
- * BECAME_NON_PAIRABLE will make the transition.
+ * POWER_STATE_CHANGED will make the transition.
*/
final class BluetoothAdapterStateMachine extends StateMachine {
private static final String TAG = "BluetoothAdapterStateMachine";
@@ -83,10 +83,10 @@ final class BluetoothAdapterStateMachine extends StateMachine {
static final int SERVICE_RECORD_LOADED = 51;
// Event indicates all the remote Bluetooth devices has been disconnected
static final int ALL_DEVICES_DISCONNECTED = 52;
- // Event indicates the Bluetooth is connectable
- static final int BECAME_PAIRABLE = 53;
- // Event indicates the Bluetooth is non-connectable.
- static final int BECAME_NON_PAIRABLE = 54;
+ // Event indicates the Bluetooth scan mode has changed
+ static final int SCAN_MODE_CHANGED = 53;
+ // Event indicates the powered state has changed
+ static final int POWER_STATE_CHANGED = 54;
// Event indicates airplane mode is turned on
static final int AIRPLANE_MODE_ON = 55;
// Event indicates airplane mode is turned off
@@ -107,6 +107,8 @@ final class BluetoothAdapterStateMachine extends StateMachine {
private static final int TURN_COLD = 103;
// Device disconnecting timeout happens
private static final int DEVICES_DISCONNECT_TIMEOUT = 104;
+ // Prepare Bluetooth timeout happens
+ private static final int PREPARE_BLUETOOTH_TIMEOUT = 105;
private Context mContext;
private BluetoothService mBluetoothService;
@@ -125,6 +127,8 @@ final class BluetoothAdapterStateMachine extends StateMachine {
// timeout value waiting for all the devices to be disconnected
private static final int DEVICES_DISCONNECT_TIMEOUT_TIME = 3000;
+ private static final int PREPARE_BLUETOOTH_TIMEOUT_TIME = 7000;
+
BluetoothAdapterStateMachine(Context context, BluetoothService bluetoothService,
BluetoothAdapter bluetoothAdapter) {
super(TAG);
@@ -165,7 +169,7 @@ final class BluetoothAdapterStateMachine extends StateMachine {
}
@Override
public boolean processMessage(Message message) {
- if (DBG) log("PowerOff process message: " + message.what);
+ log("PowerOff process message: " + message.what);
boolean retValue = HANDLED;
switch(message.what) {
@@ -257,6 +261,7 @@ final class BluetoothAdapterStateMachine extends StateMachine {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
+ log("prepareBluetooth sleep interrupted: " + pollCount);
break;
}
}
@@ -274,6 +279,7 @@ final class BluetoothAdapterStateMachine extends StateMachine {
return false;
}
+ sendMessageDelayed(PREPARE_BLUETOOTH_TIMEOUT, PREPARE_BLUETOOTH_TIMEOUT_TIME);
return true;
}
}
@@ -291,13 +297,20 @@ final class BluetoothAdapterStateMachine extends StateMachine {
@Override
public boolean processMessage(Message message) {
- if (DBG) log("WarmUp process message: " + message.what);
+ log("WarmUp process message: " + message.what);
boolean retValue = HANDLED;
switch(message.what) {
case SERVICE_RECORD_LOADED:
+ removeMessages(PREPARE_BLUETOOTH_TIMEOUT);
transitionTo(mHotOff);
break;
+ case PREPARE_BLUETOOTH_TIMEOUT:
+ Log.e(TAG, "Bluetooth adapter SDP failed to load");
+ shutoffBluetooth();
+ transitionTo(mPowerOff);
+ broadcastState(BluetoothAdapter.STATE_OFF);
+ break;
case USER_TURN_ON: // handle this at HotOff state
case TURN_ON_CONTINUE: // Once in HotOff state, continue turn bluetooth
// on to the BluetoothOn state
@@ -331,7 +344,7 @@ final class BluetoothAdapterStateMachine extends StateMachine {
@Override
public boolean processMessage(Message message) {
- if (DBG) log("HotOff process message: " + message.what);
+ log("HotOff process message: " + message.what);
boolean retValue = HANDLED;
switch(message.what) {
@@ -348,8 +361,7 @@ final class BluetoothAdapterStateMachine extends StateMachine {
break;
case AIRPLANE_MODE_ON:
case TURN_COLD:
- mBluetoothService.shutoffBluetooth();
- mEventLoop.stop();
+ shutoffBluetooth();
transitionTo(mPowerOff);
broadcastState(BluetoothAdapter.STATE_OFF);
break;
@@ -390,32 +402,32 @@ final class BluetoothAdapterStateMachine extends StateMachine {
}
@Override
public boolean processMessage(Message message) {
- if (DBG) log("Switching process message: " + message.what);
+ log("Switching process message: " + message.what);
boolean retValue = HANDLED;
switch(message.what) {
- case BECAME_PAIRABLE:
- mBluetoothService.initBluetoothAfterTurningOn();
- transitionTo(mBluetoothOn);
- broadcastState(BluetoothAdapter.STATE_ON);
- // run bluetooth now that it's turned on
- // Note runBluetooth should be called only in adapter STATE_ON
- mBluetoothService.runBluetooth();
+ case SCAN_MODE_CHANGED:
+ // This event matches mBluetoothService.switchConnectable action
+ if (mPublicState == BluetoothAdapter.STATE_TURNING_ON) {
+ // set pairable if it's not
+ mBluetoothService.setPairable();
+ mBluetoothService.initBluetoothAfterTurningOn();
+ transitionTo(mBluetoothOn);
+ broadcastState(BluetoothAdapter.STATE_ON);
+ // run bluetooth now that it's turned on
+ // Note runBluetooth should be called only in adapter STATE_ON
+ mBluetoothService.runBluetooth();
+ }
break;
- case BECAME_NON_PAIRABLE:
- if (mBluetoothService.getAdapterConnectionState() ==
- BluetoothAdapter.STATE_DISCONNECTED) {
- removeMessages(DEVICES_DISCONNECT_TIMEOUT);
+ case POWER_STATE_CHANGED:
+ if (!((Boolean) message.obj)) {
transitionTo(mHotOff);
finishSwitchingOff();
}
break;
case ALL_DEVICES_DISCONNECTED:
removeMessages(DEVICES_DISCONNECT_TIMEOUT);
- if (mBluetoothService.getScanMode() == BluetoothAdapter.SCAN_MODE_NONE) {
- transitionTo(mHotOff);
- finishSwitchingOff();
- }
+ mBluetoothService.switchConnectable(false);
break;
case DEVICES_DISCONNECT_TIMEOUT:
sendMessage(ALL_DEVICES_DISCONNECTED);
@@ -461,7 +473,7 @@ final class BluetoothAdapterStateMachine extends StateMachine {
}
@Override
public boolean processMessage(Message message) {
- if (DBG) log("BluetoothOn process message: " + message.what);
+ log("BluetoothOn process message: " + message.what);
boolean retValue = HANDLED;
switch(message.what) {
@@ -482,9 +494,14 @@ final class BluetoothAdapterStateMachine extends StateMachine {
case AIRPLANE_MODE_ON:
transitionTo(mSwitching);
broadcastState(BluetoothAdapter.STATE_TURNING_OFF);
- mBluetoothService.switchConnectable(false);
- mBluetoothService.disconnectDevices();
- sendMessageDelayed(DEVICES_DISCONNECT_TIMEOUT, DEVICES_DISCONNECT_TIMEOUT_TIME);
+ if (mBluetoothService.getAdapterConnectionState() !=
+ BluetoothAdapter.STATE_DISCONNECTED) {
+ mBluetoothService.disconnectDevices();
+ sendMessageDelayed(DEVICES_DISCONNECT_TIMEOUT,
+ DEVICES_DISCONNECT_TIMEOUT_TIME);
+ } else {
+ mBluetoothService.switchConnectable(false);
+ }
// we turn all the way to PowerOff with AIRPLANE_MODE_ON
if (message.what == AIRPLANE_MODE_ON) {
@@ -514,15 +531,25 @@ final class BluetoothAdapterStateMachine extends StateMachine {
private class PerProcessState extends State {
IBluetoothStateChangeCallback mCallback = null;
+ boolean isTurningOn = false;
@Override
public void enter() {
- if (DBG) log("Enter PerProcessState: " + getCurrentMessage().what);
+ int what = getCurrentMessage().what;
+ if (DBG) log("Enter PerProcessState: " + what);
+
+ if (what == PER_PROCESS_TURN_ON) {
+ isTurningOn = true;
+ } else if (what == PER_PROCESS_TURN_OFF) {
+ isTurningOn = false;
+ } else {
+ Log.e(TAG, "enter PerProcessState: wrong msg: " + what);
+ }
}
@Override
public boolean processMessage(Message message) {
- if (DBG) log("PerProcessState process message: " + message.what);
+ log("PerProcessState process message: " + message.what);
boolean retValue = HANDLED;
switch (message.what) {
@@ -534,8 +561,20 @@ final class BluetoothAdapterStateMachine extends StateMachine {
perProcessCallback(true, mCallback);
}
break;
- case BECAME_PAIRABLE:
- perProcessCallback(true, mCallback);
+ case SCAN_MODE_CHANGED:
+ if (isTurningOn) {
+ perProcessCallback(true, mCallback);
+ isTurningOn = false;
+ }
+ break;
+ case POWER_STATE_CHANGED:
+ if (!((Boolean) message.obj)) {
+ transitionTo(mHotOff);
+ if (!mContext.getResources().getBoolean
+ (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {
+ deferMessage(obtainMessage(TURN_COLD));
+ }
+ }
break;
case USER_TURN_ON:
broadcastState(BluetoothAdapter.STATE_TURNING_ON);
@@ -579,13 +618,6 @@ final class BluetoothAdapterStateMachine extends StateMachine {
mBluetoothService.switchConnectable(false);
}
break;
- case BECAME_NON_PAIRABLE:
- transitionTo(mHotOff);
- if (!mContext.getResources().getBoolean
- (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {
- deferMessage(obtainMessage(TURN_COLD));
- }
- break;
case AIRPLANE_MODE_ON:
mBluetoothService.switchConnectable(false);
allProcessesCallback(false);
@@ -602,6 +634,11 @@ final class BluetoothAdapterStateMachine extends StateMachine {
}
}
+ private void shutoffBluetooth() {
+ mBluetoothService.shutoffBluetooth();
+ mEventLoop.stop();
+ mBluetoothService.cleanNativeAfterShutoffBluetooth();
+ }
private void perProcessCallback(boolean on, IBluetoothStateChangeCallback c) {
if (c == null) return;
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index d73f8c9..6eff796 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -322,6 +322,12 @@ class BluetoothEventLoop {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
} else if (name.equals("Pairable") || name.equals("Discoverable")) {
+ adapterProperties.setProperty(name, propValues[1]);
+
+ if (name.equals("Discoverable")) {
+ mBluetoothState.sendMessage(BluetoothAdapterStateMachine.SCAN_MODE_CHANGED);
+ }
+
String pairable = name.equals("Pairable") ? propValues[1] :
adapterProperties.getProperty("Pairable");
String discoverable = name.equals("Discoverable") ? propValues[1] :
@@ -331,16 +337,6 @@ class BluetoothEventLoop {
if (pairable == null || discoverable == null)
return;
- adapterProperties.setProperty(name, propValues[1]);
-
- if (name.equals("Pairable")) {
- if (pairable.equals("true")) {
- mBluetoothState.sendMessage(BluetoothAdapterStateMachine.BECAME_PAIRABLE);
- } else {
- mBluetoothState.sendMessage(BluetoothAdapterStateMachine.BECAME_NON_PAIRABLE);
- }
- }
-
int mode = BluetoothService.bluezStringToScanMode(
pairable.equals("true"),
discoverable.equals("true"));
@@ -377,10 +373,11 @@ class BluetoothEventLoop {
mBluetoothService.updateBluetoothState(value);
}
} else if (name.equals("Powered")) {
+ mBluetoothState.sendMessage(BluetoothAdapterStateMachine.POWER_STATE_CHANGED,
+ propValues[1].equals("true") ? new Boolean(true) : new Boolean(false));
// bluetoothd has restarted, re-read all our properties.
// Note: bluez only sends this property change when it restarts.
- if (propValues[1].equals("true"))
- onRestartRequired();
+ onRestartRequired();
} else if (name.equals("DiscoverableTimeout")) {
adapterProperties.setProperty(name, propValues[1]);
}
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 0357958..ee14673 100755
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -437,7 +437,21 @@ public class BluetoothService extends IBluetooth.Stub {
* power off Bluetooth
*/
synchronized void shutoffBluetooth() {
+ if (mAdapterSdpHandles != null) removeReservedServiceRecordsNative(mAdapterSdpHandles);
+ setBluetoothTetheringNative(false, BluetoothPanProfileHandler.NAP_ROLE,
+ BluetoothPanProfileHandler.NAP_BRIDGE);
tearDownNativeDataNative();
+ }
+
+ /**
+ * Data clean up after Bluetooth shutoff
+ */
+ synchronized void cleanNativeAfterShutoffBluetooth() {
+ // Ths method is called after shutdown of event loop in the Bluetooth shut down
+ // procedure
+
+ // the adapter property could be changed before event loop is stoped, clear it again
+ mAdapterProperties.clear();
disableNative();
if (mRestart) {
mRestart = false;
@@ -743,26 +757,6 @@ public class BluetoothService extends IBluetooth.Stub {
public synchronized boolean setScanMode(int mode, int duration) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
"Need WRITE_SECURE_SETTINGS permission");
- return setScanMode(mode, duration, true);
- }
-
- /**
- * @param on true set the local Bluetooth module to be connectable
- * but not dicoverable
- * false set the local Bluetooth module to be not connectable
- * and not dicoverable
- */
- /*package*/ synchronized void switchConnectable(boolean on) {
- if (on) {
- // 0 is a dummy value, does not apply for SCAN_MODE_CONNECTABLE
- setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE, 0, false);
- } else {
- // 0 is a dummy value, does not apply for SCAN_MODE_NONE
- setScanMode(BluetoothAdapter.SCAN_MODE_NONE, 0, false);
- }
- }
-
- private synchronized boolean setScanMode(int mode, int duration, boolean allowOnlyInOnState) {
boolean pairable;
boolean discoverable;
@@ -785,19 +779,33 @@ public class BluetoothService extends IBluetooth.Stub {
return false;
}
- if (allowOnlyInOnState) {
- setPropertyBoolean("Discoverable", discoverable);
- setPropertyBoolean("Pairable", pairable);
- } else {
- // allowed to set the property through native layer directly
- setAdapterPropertyBooleanNative("Discoverable", discoverable ? 1 : 0);
- // do setting pairable after setting discoverable since the adapter
- // state machine uses pairable event for state change
- setAdapterPropertyBooleanNative("Pairable", pairable ? 1 : 0);
- }
+ setPropertyBoolean("Discoverable", discoverable);
+ setPropertyBoolean("Pairable", pairable);
return true;
}
+ /**
+ * @param on true set the local Bluetooth module to be connectable
+ * The dicoverability is recovered to what it was before
+ * switchConnectable(false) call
+ * false set the local Bluetooth module to be not connectable
+ * and not dicoverable
+ */
+ /*package*/ synchronized void switchConnectable(boolean on) {
+ setAdapterPropertyBooleanNative("Powered", on ? 1 : 0);
+ }
+
+ /*package*/ synchronized void setPairable() {
+ String pairableString = getProperty("Pairable", false);
+ if (pairableString == null) {
+ Log.e(TAG, "null pairableString");
+ return;
+ }
+ if (pairableString.equals("false")) {
+ setAdapterPropertyBooleanNative("Pairable", 1);
+ }
+ }
+
/*package*/ synchronized String getProperty(String name, boolean checkState) {
// If checkState is false, check if the event loop is running.
// before making the call to Bluez
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 3e5f32e..376e4f5 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -16,8 +16,6 @@
package android.text;
-import com.android.internal.util.ArrayUtils;
-
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
@@ -30,6 +28,8 @@ import android.text.style.MetricAffectingSpan;
import android.text.style.ReplacementSpan;
import android.util.Log;
+import com.android.internal.util.ArrayUtils;
+
/**
* Represents a line of styled text, for measuring in visual order and
* for rendering.
@@ -720,7 +720,7 @@ class TextLine {
float ret = 0;
int contextLen = contextEnd - contextStart;
- if (needWidth || (c != null && (wp.bgColor != 0 || runIsRtl))) {
+ if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineColor !=0 || runIsRtl))) {
int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR;
if (mCharsValid) {
ret = wp.getTextRunAdvances(mChars, start, runLen,
@@ -739,15 +739,32 @@ class TextLine {
}
if (wp.bgColor != 0) {
- int color = wp.getColor();
- Paint.Style s = wp.getStyle();
+ int previousColor = wp.getColor();
+ Paint.Style previousStyle = wp.getStyle();
+
wp.setColor(wp.bgColor);
wp.setStyle(Paint.Style.FILL);
-
c.drawRect(x, top, x + ret, bottom, wp);
- wp.setStyle(s);
- wp.setColor(color);
+ wp.setStyle(previousStyle);
+ wp.setColor(previousColor);
+ }
+
+ if (wp.underlineColor != 0) {
+ // kStdUnderline_Offset = 1/9, defined in SkTextFormatParams.h
+ float middle = y + wp.baselineShift + (1.0f / 9.0f) * wp.getTextSize();
+ // kStdUnderline_Thickness = 1/18, defined in SkTextFormatParams.h
+ float halfHeight = wp.underlineThickness * (1.0f / 18.0f / 2.0f) * wp.getTextSize();
+
+ int previousColor = wp.getColor();
+ Paint.Style previousStyle = wp.getStyle();
+
+ wp.setColor(wp.underlineColor);
+ wp.setStyle(Paint.Style.FILL);
+ c.drawRect(x, middle - halfHeight, x + ret, middle + halfHeight, wp);
+
+ wp.setStyle(previousStyle);
+ wp.setColor(previousColor);
}
drawTextRun(c, wp, start, end, contextStart, contextEnd, runIsRtl,
diff --git a/core/java/android/text/TextPaint.java b/core/java/android/text/TextPaint.java
index f9e7cac..de57dfa 100644
--- a/core/java/android/text/TextPaint.java
+++ b/core/java/android/text/TextPaint.java
@@ -23,11 +23,22 @@ import android.graphics.Paint;
* data used during text measuring and drawing.
*/
public class TextPaint extends Paint {
+ // Special value 0 means no background paint
public int bgColor;
public int baselineShift;
public int linkColor;
public int[] drawableState;
public float density = 1.0f;
+ /**
+ * Special value 0 means no custom underline
+ * @hide
+ */
+ public int underlineColor;
+ /**
+ * Defined as a multiplier of the default underline thickness. Use 1.0f for default thickness.
+ * @hide
+ */
+ public float underlineThickness;
public TextPaint() {
super();
@@ -53,5 +64,24 @@ public class TextPaint extends Paint {
linkColor = tp.linkColor;
drawableState = tp.drawableState;
density = tp.density;
+ underlineColor = tp.underlineColor;
+ underlineThickness = tp.underlineThickness;
+ }
+
+ /**
+ * Defines a custom underline for this Paint.
+ * @param color underline solid color
+ * @param thickness underline thickness, defined as a multiplier of the default underline
+ * thickness.
+ * @hide
+ */
+ public void setUnderlineText(boolean isUnderlined, int color, float thickness) {
+ setUnderlineText(false);
+ if (isUnderlined) {
+ underlineColor = color;
+ underlineThickness = thickness;
+ } else {
+ underlineColor = 0;
+ }
}
}
diff --git a/core/java/android/util/XmlPullAttributes.java b/core/java/android/util/XmlPullAttributes.java
index ecedbe1..6c8bb39 100644
--- a/core/java/android/util/XmlPullAttributes.java
+++ b/core/java/android/util/XmlPullAttributes.java
@@ -143,5 +143,5 @@ class XmlPullAttributes implements AttributeSet {
return getAttributeResourceValue(null, "style", 0);
}
- private XmlPullParser mParser;
+ /*package*/ XmlPullParser mParser;
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index d9efe0c..85e990a 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -366,9 +366,9 @@ public class Display {
// Following fields are initialized from native code
private int mPixelFormat;
private float mRefreshRate;
- private float mDensity;
- private float mDpiX;
- private float mDpiY;
+ /*package*/ float mDensity;
+ /*package*/ float mDpiX;
+ /*package*/ float mDpiY;
private final Point mTmpPoint = new Point();
private final DisplayMetrics mTmpMetrics = new DisplayMetrics();
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index a7fe95d..e586370 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -509,13 +509,6 @@ class GLES20Canvas extends HardwareCanvas {
private static native void nSetMatrix(int renderer, int matrix);
@Override
- public int getNativeMatrix() {
- return nGetMatrix(mRenderer);
- }
-
- private static native int nGetMatrix(int renderer);
-
- @Override
public void getMatrix(Matrix matrix) {
nGetMatrix(mRenderer, matrix.native_instance);
}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index f2b6b1f..f2c3131 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -199,8 +199,10 @@ public abstract class HardwareRenderer {
* @param attachInfo AttachInfo tied to the specified view.
* @param callbacks Callbacks invoked when drawing happens.
* @param dirty The dirty rectangle to update, can be null.
+ *
+ * @return true if the dirty rect was ignored, false otherwise
*/
- abstract void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
+ abstract boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
Rect dirty);
/**
@@ -757,7 +759,7 @@ public abstract class HardwareRenderer {
}
@Override
- void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
+ boolean draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
Rect dirty) {
if (canDraw()) {
if (!hasDirtyRegions()) {
@@ -825,8 +827,12 @@ public abstract class HardwareRenderer {
sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
checkEglErrors();
+
+ return dirty == null;
}
}
+
+ return false;
}
/**
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 332a0fa..9628d6b 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -75,9 +75,9 @@ public abstract class LayoutInflater {
private Factory2 mPrivateFactory;
private Filter mFilter;
- private final Object[] mConstructorArgs = new Object[2];
+ final Object[] mConstructorArgs = new Object[2];
- private static final Class<?>[] mConstructorSignature = new Class[] {
+ static final Class<?>[] mConstructorSignature = new Class[] {
Context.class, AttributeSet.class};
private static final HashMap<String, Constructor<? extends View>> sConstructorMap =
@@ -705,7 +705,7 @@ public abstract class LayoutInflater {
* Recursive method used to descend down the xml hierarchy and instantiate
* views, instantiate their children, and then call onFinishInflate().
*/
- private void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs,
+ void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException {
final int depth = parser.getDepth();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5b77cf7..54bd637 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6249,15 +6249,7 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit
}
// Walk up the hierarchy to determine if we're inside a scrolling container.
- boolean isInScrollingContainer = false;
- ViewParent p = getParent();
- while (p != null && p instanceof ViewGroup) {
- if (((ViewGroup) p).shouldDelayChildPressedState()) {
- isInScrollingContainer = true;
- break;
- }
- p = p.getParent();
- }
+ boolean isInScrollingContainer = isInScrollingContainer();
// For views inside a scrolling container, delay the pressed feedback for
// a short period in case this is a scroll.
@@ -6307,6 +6299,20 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit
}
/**
+ * @hide
+ */
+ public boolean isInScrollingContainer() {
+ ViewParent p = getParent();
+ while (p != null && p instanceof ViewGroup) {
+ if (((ViewGroup) p).shouldDelayChildPressedState()) {
+ return true;
+ }
+ p = p.getParent();
+ }
+ return false;
+ }
+
+ /**
* Remove the longpress detection timer.
*/
private void removeLongPressCallback() {
@@ -8774,6 +8780,12 @@ public class View implements Drawable.Callback2, KeyEvent.Callback, Accessibilit
* @see #SCROLLBARS_OUTSIDE_OVERLAY
* @see #SCROLLBARS_OUTSIDE_INSET
*/
+ @ViewDebug.ExportedProperty(mapping = {
+ @ViewDebug.IntToString(from = SCROLLBARS_INSIDE_OVERLAY, to = "INSIDE_OVERLAY"),
+ @ViewDebug.IntToString(from = SCROLLBARS_INSIDE_INSET, to = "INSIDE_INSET"),
+ @ViewDebug.IntToString(from = SCROLLBARS_OUTSIDE_OVERLAY, to = "OUTSIDE_OVERLAY"),
+ @ViewDebug.IntToString(from = SCROLLBARS_OUTSIDE_INSET, to = "OUTSIDE_INSET")
+ })
public int getScrollBarStyle() {
return mViewFlags & SCROLLBARS_STYLE_MASK;
}
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 9520958..5e104f9 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -225,7 +225,7 @@ public class ViewConfiguration {
private boolean sHasPermanentMenuKey;
private boolean sHasPermanentMenuKeySet;
- private static final SparseArray<ViewConfiguration> sConfigurations =
+ static final SparseArray<ViewConfiguration> sConfigurations =
new SparseArray<ViewConfiguration>(2);
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 380fc15..f23c366 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1798,7 +1798,9 @@ public final class ViewRootImpl extends Handler implements ViewParent,
currentDirty = null;
}
- mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this, currentDirty);
+ if (mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this, currentDirty)) {
+ mPreviousDirty.set(0, 0, mWidth, mHeight);
+ }
}
if (animating) {
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 7620a63..2bb9da1 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -537,7 +537,10 @@ public class WebView extends AbsoluteLayout
// This should be ViewConfiguration.getTapTimeout()
// But system time out is 100ms, which is too short for the browser.
// In the browser, if it switches out of tap too soon, jump tap won't work.
- private static final int TAP_TIMEOUT = 200;
+ // In addition, a double tap on a trackpad will always have a duration of
+ // 300ms, so this value must be at least that (otherwise we will timeout the
+ // first tap and convert it to a long press).
+ private static final int TAP_TIMEOUT = 300;
// This should be ViewConfiguration.getLongPressTimeout()
// But system time out is 500ms, which is too short for the browser.
// With a short timeout, it's difficult to treat trigger a short press.
diff --git a/core/java/android/webkit/webdriver/By.java b/core/java/android/webkit/webdriver/By.java
deleted file mode 100644
index fa4fe74..0000000
--- a/core/java/android/webkit/webdriver/By.java
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit.webdriver;
-
-import java.util.List;
-
-/**
- * Mechanism to locate elements within the DOM of the page.
- * @hide
- */
-public abstract class By {
- public abstract WebElement findElement(WebElement element);
- public abstract List<WebElement> findElements(WebElement element);
-
- /**
- * Locates an element by its HTML id attribute.
- *
- * @param id The HTML id attribute to look for.
- * @return A By instance that locates elements by their HTML id attributes.
- */
- public static By id(final String id) {
- throwIfNull(id);
- return new By() {
- @Override
- public WebElement findElement(WebElement element) {
- return element.findElementById(id);
- }
-
- @Override
- public List<WebElement> findElements(WebElement element) {
- return element.findElementsById(id); // Yes, it happens a lot.
- }
-
- @Override
- public String toString() {
- return "By.id: " + id;
- }
- };
- }
-
- /**
- * Locates an element by the matching the exact text on the HTML link.
- *
- * @param linkText The exact text to match against.
- * @return A By instance that locates elements by the text displayed by
- * the link.
- */
- public static By linkText(final String linkText) {
- throwIfNull(linkText);
- return new By() {
- @Override
- public WebElement findElement(WebElement element) {
- return element.findElementByLinkText(linkText);
- }
-
- @Override
- public List<WebElement> findElements(WebElement element) {
- return element.findElementsByLinkText(linkText);
- }
-
- @Override
- public String toString() {
- return "By.linkText: " + linkText;
- }
- };
- }
-
- /**
- * Locates an element by matching partial part of the text displayed by an
- * HTML link.
- *
- * @param linkText The text that should be contained by the text displayed
- * on the link.
- * @return A By instance that locates elements that contain the given link
- * text.
- */
- public static By partialLinkText(final String linkText) {
- throwIfNull(linkText);
- return new By() {
- @Override
- public WebElement findElement(WebElement element) {
- return element.findElementByPartialLinkText(linkText);
- }
-
- @Override
- public List<WebElement> findElements(WebElement element) {
- return element.findElementsByPartialLinkText(linkText);
- }
-
- @Override
- public String toString() {
- return "By.partialLinkText: " + linkText;
- }
- };
- }
-
- /**
- * Locates an element by matching its HTML name attribute.
- *
- * @param name The value of the HTML name attribute.
- * @return A By instance that locates elements by the HTML name attribute.
- */
- public static By name(final String name) {
- throwIfNull(name);
- return new By() {
- @Override
- public WebElement findElement(WebElement element) {
- return element.findElementByName(name);
- }
-
- @Override
- public List<WebElement> findElements(WebElement element) {
- return element.findElementsByName(name);
- }
-
- @Override
- public String toString() {
- return "By.name: " + name;
- }
- };
- }
-
- /**
- * Locates an element by matching its class name.
- * @param className The class name
- * @return A By instance that locates elements by their class name attribute.
- */
- public static By className(final String className) {
- throwIfNull(className);
- return new By() {
- @Override
- public WebElement findElement(WebElement element) {
- return element.findElementByClassName(className);
- }
-
- @Override
- public List<WebElement> findElements(WebElement element) {
- return element.findElementsByClassName(className);
- }
-
- @Override
- public String toString() {
- return "By.className: " + className;
- }
- };
- }
-
- /**
- * Locates an element by matching its css property.
- *
- * @param css The css property.
- * @return A By instance that locates elements by their css property.
- */
- public static By css(final String css) {
- throwIfNull(css);
- return new By() {
- @Override
- public WebElement findElement(WebElement element) {
- return element.findElementByCss(css);
- }
-
- @Override
- public List<WebElement> findElements(WebElement element) {
- return element.findElementsByCss(css);
- }
-
- @Override
- public String toString() {
- return "By.css: " + css;
- }
- };
- }
-
- /**
- * Locates an element by matching its HTML tag name.
- *
- * @param tagName The HTML tag name to look for.
- * @return A By instance that locates elements using the name of the
- * HTML tag.
- */
- public static By tagName(final String tagName) {
- throwIfNull(tagName);
- return new By() {
- @Override
- public WebElement findElement(WebElement element) {
- return element.findElementByTagName(tagName);
- }
-
- @Override
- public List<WebElement> findElements(WebElement element) {
- return element.findElementsByTagName(tagName);
- }
-
- @Override
- public String toString() {
- return "By.tagName: " + tagName;
- }
- };
- }
-
- /**
- * Locates an element using an XPath expression.
- *
- * <p>When using XPath, be aware that this follows standard conventions: a
- * search prefixed with "//" will search the entire document, not just the
- * children of the current node. Use ".//" to limit your search to the
- * children of this {@link android.webkit.webdriver.WebElement}.
- *
- * @param xpath The XPath expression to use.
- * @return A By instance that locates elements using the given XPath.
- */
- public static By xpath(final String xpath) {
- throwIfNull(xpath);
- return new By() {
- @Override
- public WebElement findElement(WebElement element) {
- return element.findElementByXPath(xpath);
- }
-
- @Override
- public List<WebElement> findElements(WebElement element) {
- return element.findElementsByXPath(xpath);
- }
-
- @Override
- public String toString() {
- return "By.xpath: " + xpath;
- }
- };
- }
-
- private static void throwIfNull(String argument) {
- if (argument == null) {
- throw new IllegalArgumentException(
- "Cannot find elements with null locator.");
- }
- }
-}
diff --git a/core/java/android/webkit/webdriver/WebDriver.java b/core/java/android/webkit/webdriver/WebDriver.java
deleted file mode 100644
index 79e6523..0000000
--- a/core/java/android/webkit/webdriver/WebDriver.java
+++ /dev/null
@@ -1,843 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit.webdriver;
-
-import android.graphics.Point;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemClock;
-import android.view.InputDevice;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.webkit.WebView;
-import android.webkit.WebViewCore;
-
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-
-import com.android.internal.R;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Drives a web application by controlling the WebView. This class
- * provides a DOM-like API allowing to get information about the page,
- * navigate, and interact with the web application. This is particularly useful
- * for testing a web application.
- *
- * <p/>{@link android.webkit.webdriver.WebDriver} should be created in the main
- * thread, and invoked from another thread. Here is a sample usage:
- *
- * public class WebDriverStubActivity extends Activity {
- * private WebDriver mDriver;
- *
- * public void onCreate(Bundle savedInstanceState) {
- * super.onCreate(savedInstanceState);
- * WebView view = new WebView(this);
- * mDriver = new WebDriver(view);
- * setContentView(view);
- * }
- *
- *
- * public WebDriver getDriver() {
- * return mDriver;
- * }
- *}
- *
- * public class WebDriverTest extends
- * ActivityInstrumentationTestCase2<WebDriverStubActivity>{
- * private WebDriver mDriver;
- *
- * public WebDriverTest() {
- * super(WebDriverStubActivity.class);
- * }
- *
- * protected void setUp() throws Exception {
- * super.setUp();
- * mDriver = getActivity().getDriver();
- * }
- *
- * public void testGoogle() {
- * mDriver.get("http://google.com");
- * WebElement searchBox = mDriver.findElement(By.name("q"));
- * q.sendKeys("Cheese!");
- * q.submit();
- * assertTrue(mDriver.findElements(By.partialLinkText("Cheese")).size() > 0);
- * }
- *}
- *
- * @hide
- */
-public class WebDriver {
- // Timeout for page load in milliseconds.
- private static final int LOADING_TIMEOUT = 30000;
- // Timeout for executing JavaScript in the WebView in milliseconds.
- private static final int JS_EXECUTION_TIMEOUT = 10000;
- // Timeout for the MotionEvent to be completely handled
- private static final int MOTION_EVENT_TIMEOUT = 1000;
- // Timeout for detecting a new page load
- private static final int PAGE_STARTED_LOADING = 500;
- // Timeout for handling KeyEvents
- private static final int KEY_EVENT_TIMEOUT = 2000;
-
- // Commands posted to the handler
- private static final int CMD_GET_URL = 1;
- private static final int CMD_EXECUTE_SCRIPT = 2;
- private static final int CMD_SEND_TOUCH = 3;
- private static final int CMD_SEND_KEYS = 4;
- private static final int CMD_NAV_REFRESH = 5;
- private static final int CMD_NAV_BACK = 6;
- private static final int CMD_NAV_FORWARD = 7;
- private static final int CMD_SEND_KEYCODE = 8;
- private static final int CMD_MOVE_CURSOR_RIGHTMOST_POS = 9;
- private static final int CMD_MESSAGE_RELAY_ECHO = 10;
-
- private static final String ELEMENT_KEY = "ELEMENT";
- private static final String STATUS = "status";
- private static final String VALUE = "value";
-
- private static final long MAIN_THREAD = Thread.currentThread().getId();
-
- // This is updated by a callabck from JavaScript when the result is ready.
- private String mJsResult;
-
- // Used for synchronization
- private final Object mSyncObject;
- private final Object mSyncPageLoad;
-
- // Updated when the command is done executing in the main thread.
- private volatile boolean mCommandDone;
- // Used by WebViewClientWrapper.onPageStarted() to notify that
- // a page started loading.
- private volatile boolean mPageStartedLoading;
- // Used by WebChromeClientWrapper.onProgressChanged to notify when
- // a page finished loading.
- private volatile boolean mPageFinishedLoading;
- private WebView mWebView;
- private Navigation mNavigation;
- // This WebElement represents the object document.documentElement
- private WebElement mDocumentElement;
-
-
- // This Handler runs in the main UI thread.
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case CMD_GET_URL:
- final String url = (String) msg.obj;
- mWebView.loadUrl(url);
- break;
- case CMD_EXECUTE_SCRIPT:
- mWebView.loadUrl("javascript:" + (String) msg.obj);
- break;
- case CMD_MESSAGE_RELAY_ECHO:
- notifyCommandDone();
- break;
- case CMD_SEND_TOUCH:
- touchScreen((Point) msg.obj);
- notifyCommandDone();
- break;
- case CMD_SEND_KEYS:
- dispatchKeys((CharSequence[]) msg.obj);
- notifyCommandDone();
- break;
- case CMD_NAV_REFRESH:
- mWebView.reload();
- break;
- case CMD_NAV_BACK:
- mWebView.goBack();
- break;
- case CMD_NAV_FORWARD:
- mWebView.goForward();
- break;
- case CMD_SEND_KEYCODE:
- dispatchKeyCodes((int[]) msg.obj);
- notifyCommandDone();
- break;
- case CMD_MOVE_CURSOR_RIGHTMOST_POS:
- moveCursorToLeftMostPos((String) msg.obj);
- notifyCommandDone();
- break;
- }
- }
- };
-
- /**
- * Error codes from the WebDriver wire protocol
- * http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes
- */
- private enum ErrorCode {
- SUCCESS(0),
- NO_SUCH_ELEMENT(7),
- NO_SUCH_FRAME(8),
- UNKNOWN_COMMAND(9),
- UNSUPPORTED_OPERATION(9), // Alias
- STALE_ELEMENT_REFERENCE(10),
- ELEMENT_NOT_VISISBLE(11),
- INVALID_ELEMENT_STATE(12),
- UNKNOWN_ERROR(13),
- ELEMENT_NOT_SELECTABLE(15),
- XPATH_LOOKUP_ERROR(19),
- NO_SUCH_WINDOW(23),
- INVALID_COOKIE_DOMAIN(24),
- UNABLE_TO_SET_COOKIE(25),
- MODAL_DIALOG_OPENED(26),
- MODAL_DIALOG_OPEN(27),
- SCRIPT_TIMEOUT(28);
-
- private final int mCode;
- private static ErrorCode[] values = ErrorCode.values();
-
- ErrorCode(int code) {
- this.mCode = code;
- }
-
- public int getCode() {
- return mCode;
- }
-
- public static ErrorCode get(final int intValue) {
- for (int i = 0; i < values.length; i++) {
- if (values[i].getCode() == intValue) {
- return values[i];
- }
- }
- return UNKNOWN_ERROR;
- }
- }
-
- public WebDriver(WebView webview) {
- this.mWebView = webview;
- mWebView.requestFocus();
- if (mWebView == null) {
- throw new IllegalArgumentException("WebView cannot be null");
- }
- if (!mWebView.getSettings().getJavaScriptEnabled()) {
- throw new RuntimeException("Javascript is disabled in the WebView. "
- + "Enable it to use WebDriver");
- }
- shouldRunInMainThread(true);
-
- mSyncObject = new Object();
- mSyncPageLoad = new Object();
- this.mWebView = webview;
- WebChromeClientWrapper chromeWrapper = new WebChromeClientWrapper(
- webview.getWebChromeClient(), this);
- mWebView.setWebChromeClient(chromeWrapper);
- WebViewClientWrapper viewWrapper = new WebViewClientWrapper(
- webview.getWebViewClient(), this);
- mWebView.setWebViewClient(viewWrapper);
- mWebView.addJavascriptInterface(new JavascriptResultReady(),
- "webdriver");
- mDocumentElement = new WebElement(this, "");
- mNavigation = new Navigation();
- }
-
- /**
- * @return The title of the current page, null if not set.
- */
- public String getTitle() {
- return mWebView.getTitle();
- }
-
- /**
- * Loads a URL in the WebView. This function is blocking and will return
- * when the page has finished loading.
- *
- * @param url The URL to load.
- */
- public void get(String url) {
- mNavigation.to(url);
- }
-
- /**
- * @return The source page of the currently loaded page in WebView.
- */
- public String getPageSource() {
- return (String) executeScript("return new XMLSerializer()."
- + "serializeToString(document);");
- }
-
- /**
- * Find the first {@link android.webkit.webdriver.WebElement} using the
- * given method.
- *
- * @param by The locating mechanism to use.
- * @return The first matching element on the current context.
- * @throws {@link android.webkit.webdriver.WebElementNotFoundException} if
- * no matching element was found.
- */
- public WebElement findElement(By by) {
- checkNotNull(mDocumentElement, "Load a page using WebDriver.get() "
- + "before looking for elements.");
- return by.findElement(mDocumentElement);
- }
-
- /**
- * Finds all {@link android.webkit.webdriver.WebElement} within the page
- * using the given method.
- *
- * @param by The locating mechanism to use.
- * @return A list of all {@link android.webkit.webdriver.WebElement} found,
- * or an empty list if nothing matches.
- */
- public List<WebElement> findElements(By by) {
- checkNotNull(mDocumentElement, "Load a page using WebDriver.get() "
- + "before looking for elements.");
- return by.findElements(mDocumentElement);
- }
-
- /**
- * Clears the WebView's state and closes associated views.
- */
- public void quit() {
- mWebView.clearCache(true);
- mWebView.clearFormData();
- mWebView.clearHistory();
- mWebView.clearSslPreferences();
- mWebView.clearView();
- mWebView.removeAllViewsInLayout();
- }
-
- /**
- * Executes javascript in the context of the main frame.
- *
- * If the script has a return value the following happens:
- * <ul>
- * <li>For an HTML element, this method returns a WebElement</li>
- * <li>For a decimal, a Double is returned</li>
- * <li>For non-decimal number, a Long is returned</li>
- * <li>For a boolean, a Boolean is returned</li>
- * <li>For all other cases, a String is returned</li>
- * <li>For an array, this returns a List<Object> with each object
- * following the rules above.</li>
- * <li>For an object literal this returns a Map<String, Object>. Note that
- * Object literals keys can only be Strings. Non Strings keys will
- * be filtered out.</li>
- * </ul>
- *
- * <p> Arguments must be a number, a boolean, a string a WebElement or
- * a list of any combination of the above. The arguments will be made
- * available to the javascript via the "arguments" magic variable,
- * as if the function was called via "Function.apply".
- *
- * @param script The JavaScript to execute.
- * @param args The arguments to the script. Can be any of a number, boolean,
- * string, WebElement or a List of those.
- * @return A Boolean, Long, Double, String, WebElement, List or null.
- */
- public Object executeScript(final String script, final Object... args) {
- String scriptArgs = "[" + convertToJsArgs(args) + "]";
- String injectScriptJs = getResourceAsString(R.raw.execute_script_android);
- return executeRawJavascript("(" + injectScriptJs +
- ")(" + escapeAndQuote(script) + ", " + scriptArgs + ", true)");
- }
-
- public Navigation navigate() {
- return mNavigation;
- }
-
-
- /**
- * @hide
- */
- public class Navigation {
- /* package */ Navigation () {}
-
- public void back() {
- navigate(CMD_NAV_BACK, null);
- }
-
- public void forward() {
- navigate(CMD_NAV_FORWARD, null);
- }
-
- public void to(String url) {
- navigate(CMD_GET_URL, url);
- }
-
- public void refresh() {
- navigate(CMD_NAV_REFRESH, null);
- }
-
- private void navigate(int command, String url) {
- synchronized (mSyncPageLoad) {
- mPageFinishedLoading = false;
- Message msg = mHandler.obtainMessage(command);
- msg.obj = url;
- mHandler.sendMessage(msg);
- waitForPageLoad();
- }
- }
- }
-
- /**
- * Converts the arguments passed to a JavaScript friendly format.
- *
- * @param args The arguments to convert.
- * @return Comma separated Strings containing the arguments.
- */
- /* package */ String convertToJsArgs(final Object... args) {
- StringBuilder toReturn = new StringBuilder();
- int length = args.length;
- for (int i = 0; i < length; i++) {
- toReturn.append((i > 0) ? "," : "");
- if (args[i] instanceof List<?>) {
- toReturn.append("[");
- List<Object> aList = (List<Object>) args[i];
- for (int j = 0 ; j < aList.size(); j++) {
- String comma = ((j == 0) ? "" : ",");
- toReturn.append(comma + convertToJsArgs(aList.get(j)));
- }
- toReturn.append("]");
- } else if (args[i] instanceof Map<?, ?>) {
- Map<Object, Object> aMap = (Map<Object, Object>) args[i];
- String toAdd = "{";
- for (Object key: aMap.keySet()) {
- toAdd += key + ":"
- + convertToJsArgs(aMap.get(key)) + ",";
- }
- toReturn.append(toAdd.substring(0, toAdd.length() -1) + "}");
- } else if (args[i] instanceof WebElement) {
- // WebElement are represented in JavaScript by Objects as
- // follow: {ELEMENT:"id"} where "id" refers to the id
- // of the HTML element in the javascript cache that can
- // be accessed throught bot.inject.cache.getCache_()
- toReturn.append("{\"" + ELEMENT_KEY + "\":\""
- + ((WebElement) args[i]).getId() + "\"}");
- } else if (args[i] instanceof Number || args[i] instanceof Boolean) {
- toReturn.append(String.valueOf(args[i]));
- } else if (args[i] instanceof String) {
- toReturn.append(escapeAndQuote((String) args[i]));
- } else {
- throw new IllegalArgumentException(
- "Javascript arguments can be "
- + "a Number, a Boolean, a String, a WebElement, "
- + "or a List or a Map of those. Got: "
- + ((args[i] == null) ? "null" : args[i].getClass()
- + ", value: " + args[i].toString()));
- }
- }
- return toReturn.toString();
- }
-
- /* package */ Object executeRawJavascript(final String script) {
- if (mWebView.getUrl() == null) {
- throw new WebDriverException("Cannot operate on a blank page. "
- + "Load a page using WebDriver.get().");
- }
- String result = executeCommand(CMD_EXECUTE_SCRIPT,
- "if (!window.webdriver || !window.webdriver.resultReady) {" +
- " return;" +
- "}" +
- "window.webdriver.resultReady(" + script + ")",
- JS_EXECUTION_TIMEOUT);
- if (result == null || "undefined".equals(result)) {
- return null;
- }
- try {
- JSONObject json = new JSONObject(result);
- throwIfError(json);
- Object value = json.get(VALUE);
- return convertJsonToJavaObject(value);
- } catch (JSONException e) {
- throw new RuntimeException("Failed to parse JavaScript result: "
- + result.toString(), e);
- }
- }
-
- /* package */ String getResourceAsString(final int resourceId) {
- InputStream is = mWebView.getResources().openRawResource(resourceId);
- BufferedReader br = new BufferedReader(new InputStreamReader(is));
- StringBuilder sb = new StringBuilder();
- String line = null;
- try {
- while ((line = br.readLine()) != null) {
- sb.append(line);
- }
- br.close();
- is.close();
- } catch (IOException e) {
- throw new RuntimeException("Failed to open JavaScript resource.", e);
- }
- return sb.toString();
- }
-
- /* package */ void sendTouchScreen(Point coords) {
- // Reset state
- resetPageLoadState();
- executeCommand(CMD_SEND_TOUCH, coords,LOADING_TIMEOUT);
- // Wait for the events to be fully handled
- waitForMessageRelay(MOTION_EVENT_TIMEOUT);
-
- // If a page started loading, block until page finishes loading
- waitForPageLoadIfNeeded();
- }
-
- /* package */ void resetPageLoadState() {
- synchronized (mSyncPageLoad) {
- mPageStartedLoading = false;
- mPageFinishedLoading = false;
- }
- }
-
- /* package */ void waitForPageLoadIfNeeded() {
- synchronized (mSyncPageLoad) {
- Long end = System.currentTimeMillis() + PAGE_STARTED_LOADING;
- // Wait PAGE_STARTED_LOADING milliseconds to see if we detect a
- // page load.
- while (!mPageStartedLoading && (System.currentTimeMillis() <= end)) {
- try {
- // This is notified by WebChromeClientWrapper#onProgressChanged
- // when the page finished loading.
- mSyncPageLoad.wait(PAGE_STARTED_LOADING);
- } catch (InterruptedException e) {
- new RuntimeException(e);
- }
- }
- if (mPageStartedLoading) {
- waitForPageLoad();
- }
- }
- }
-
- private void touchScreen(Point coords) {
- // Convert to screen coords
- // screen = JS x zoom - offset
- float zoom = mWebView.getScale();
- float xOffset = mWebView.getX();
- float yOffset = mWebView.getY();
- Point screenCoords = new Point( (int)(coords.x*zoom - xOffset),
- (int)(coords.y*zoom - yOffset));
-
- long downTime = SystemClock.uptimeMillis();
- MotionEvent down = MotionEvent.obtain(downTime,
- SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, screenCoords.x,
- screenCoords.y, 0);
- down.setSource(InputDevice.SOURCE_TOUCHSCREEN);
- MotionEvent up = MotionEvent.obtain(downTime,
- SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, screenCoords.x,
- screenCoords.y, 0);
- up.setSource(InputDevice.SOURCE_TOUCHSCREEN);
- // Dispatch the events to WebView
- mWebView.dispatchTouchEvent(down);
- mWebView.dispatchTouchEvent(up);
- }
-
- /* package */ void notifyPageStartedLoading() {
- synchronized (mSyncPageLoad) {
- mPageStartedLoading = true;
- mSyncPageLoad.notify();
- }
- }
-
- /* package */ void notifyPageFinishedLoading() {
- synchronized (mSyncPageLoad) {
- mPageFinishedLoading = true;
- mSyncPageLoad.notify();
- }
- }
-
- /**
- *
- * @param keys The first element of the CharSequence should be the
- * existing value in the text input, or the empty string if none.
- */
- /* package */ void sendKeys(CharSequence[] keys) {
- executeCommand(CMD_SEND_KEYS, keys, KEY_EVENT_TIMEOUT);
- // Wait for all KeyEvents to be handled
- waitForMessageRelay(KEY_EVENT_TIMEOUT);
- }
-
- /* package */ void sendKeyCodes(int[] keycodes) {
- executeCommand(CMD_SEND_KEYCODE, keycodes, KEY_EVENT_TIMEOUT);
- // Wait for all KeyEvents to be handled
- waitForMessageRelay(KEY_EVENT_TIMEOUT);
- }
-
- /* package */ void moveCursorToRightMostPosition(String value) {
- executeCommand(CMD_MOVE_CURSOR_RIGHTMOST_POS, value, KEY_EVENT_TIMEOUT);
- waitForMessageRelay(KEY_EVENT_TIMEOUT);
- }
-
- private void moveCursorToLeftMostPos(String value) {
- // If there is text, move the cursor to the rightmost position
- if (value != null && !value.equals("")) {
- long downTime = SystemClock.uptimeMillis();
- KeyEvent down = new KeyEvent(downTime, SystemClock.uptimeMillis(),
- KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT, 0);
- KeyEvent up = new KeyEvent(downTime, SystemClock.uptimeMillis(),
- KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_RIGHT,
- value.length());
- mWebView.dispatchKeyEvent(down);
- mWebView.dispatchKeyEvent(up);
- }
- }
-
- private void dispatchKeyCodes(int[] keycodes) {
- for (int i = 0; i < keycodes.length; i++) {
- KeyEvent down = new KeyEvent(KeyEvent.ACTION_DOWN, keycodes[i]);
- KeyEvent up = new KeyEvent(KeyEvent.ACTION_UP, keycodes[i]);
- mWebView.dispatchKeyEvent(down);
- mWebView.dispatchKeyEvent(up);
- }
- }
-
- private void dispatchKeys(CharSequence[] keys) {
- KeyCharacterMap chararcterMap = KeyCharacterMap.load(
- KeyCharacterMap.VIRTUAL_KEYBOARD);
- for (int i = 0; i < keys.length; i++) {
- CharSequence s = keys[i];
- for (int j = 0; j < s.length(); j++) {
- KeyEvent[] events =
- chararcterMap.getEvents(new char[]{s.charAt(j)});
- for (KeyEvent e : events) {
- mWebView.dispatchKeyEvent(e);
- }
- }
- }
- }
-
- private void waitForMessageRelay(long timeout) {
- synchronized (mSyncObject) {
- mCommandDone = false;
- }
- Message msg = Message.obtain();
- msg.what = WebViewCore.EventHub.MESSAGE_RELAY;
- Message echo = mHandler.obtainMessage(CMD_MESSAGE_RELAY_ECHO);
- msg.obj = echo;
-
- mWebView.getWebViewCore().sendMessage(msg);
- synchronized (mSyncObject) {
- long end = System.currentTimeMillis() + timeout;
- while (!mCommandDone && (System.currentTimeMillis() <= end)) {
- try {
- // This is notifed by the mHandler when it receives the
- // MESSAGE_RELAY back
- mSyncObject.wait(timeout);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- }
- }
-
- private void waitForPageLoad() {
- long endLoad = System.currentTimeMillis() + LOADING_TIMEOUT;
- while (!mPageFinishedLoading
- && (System.currentTimeMillis() <= endLoad)) {
- try {
- mSyncPageLoad.wait(LOADING_TIMEOUT);
- } catch (InterruptedException e) {
- throw new RuntimeException();
- }
- }
- }
-
- /**
- * Wraps the given string into quotes and escape existing quotes
- * and backslashes.
- * "foo" -> "\"foo\""
- * "foo\"" -> "\"foo\\\"\""
- * "fo\o" -> "\"fo\\o\""
- *
- * @param toWrap The String to wrap in quotes
- * @return a String wrapping the original String in quotes
- */
- private static String escapeAndQuote(final String toWrap) {
- StringBuilder toReturn = new StringBuilder("\"");
- for (int i = 0; i < toWrap.length(); i++) {
- char c = toWrap.charAt(i);
- if (c == '\"') {
- toReturn.append("\\\"");
- } else if (c == '\\') {
- toReturn.append("\\\\");
- } else {
- toReturn.append(c);
- }
- }
- toReturn.append("\"");
- return toReturn.toString();
- }
-
- private Object convertJsonToJavaObject(final Object toConvert) {
- try {
- if (toConvert == null
- || toConvert.equals(null)
- || "undefined".equals(toConvert)
- || "null".equals(toConvert)) {
- return null;
- } else if (toConvert instanceof Boolean) {
- return toConvert;
- } else if (toConvert instanceof Double
- || toConvert instanceof Float) {
- return Double.valueOf(String.valueOf(toConvert));
- } else if (toConvert instanceof Integer
- || toConvert instanceof Long) {
- return Long.valueOf(String.valueOf(toConvert));
- } else if (toConvert instanceof JSONArray) { // List
- return convertJsonArrayToList((JSONArray) toConvert);
- } else if (toConvert instanceof JSONObject) { // Map or WebElment
- JSONObject map = (JSONObject) toConvert;
- if (map.opt(ELEMENT_KEY) != null) { // WebElement
- return new WebElement(this, (String) map.get(ELEMENT_KEY));
- } else { // Map
- return convertJsonObjectToMap(map);
- }
- } else {
- return toConvert.toString();
- }
- } catch (JSONException e) {
- throw new RuntimeException("Failed to parse JavaScript result: "
- + toConvert.toString(), e);
- }
- }
-
- private List<Object> convertJsonArrayToList(final JSONArray json) {
- List<Object> toReturn = Lists.newArrayList();
- for (int i = 0; i < json.length(); i++) {
- try {
- toReturn.add(convertJsonToJavaObject(json.get(i)));
- } catch (JSONException e) {
- throw new RuntimeException("Failed to parse JSON: "
- + json.toString(), e);
- }
- }
- return toReturn;
- }
-
- private Map<Object, Object> convertJsonObjectToMap(final JSONObject json) {
- Map<Object, Object> toReturn = Maps.newHashMap();
- for (Iterator it = json.keys(); it.hasNext();) {
- String key = (String) it.next();
- try {
- Object value = json.get(key);
- toReturn.put(convertJsonToJavaObject(key),
- convertJsonToJavaObject(value));
- } catch (JSONException e) {
- throw new RuntimeException("Failed to parse JSON:"
- + json.toString(), e);
- }
- }
- return toReturn;
- }
-
- private void throwIfError(final JSONObject jsonObject) {
- ErrorCode status;
- String errorMsg;
- try {
- status = ErrorCode.get((Integer) jsonObject.get(STATUS));
- errorMsg = String.valueOf(jsonObject.get(VALUE));
- } catch (JSONException e) {
- throw new RuntimeException("Failed to parse JSON Object: "
- + jsonObject, e);
- }
- switch (status) {
- case SUCCESS:
- return;
- case NO_SUCH_ELEMENT:
- throw new WebElementNotFoundException("Could not find "
- + "WebElement.");
- case STALE_ELEMENT_REFERENCE:
- throw new WebElementStaleException("WebElement is stale.");
- default:
- throw new WebDriverException("Error: " + errorMsg);
- }
- }
-
- private void shouldRunInMainThread(boolean value) {
- assert (value == (MAIN_THREAD == Thread.currentThread().getId()));
- }
-
- /**
- * Interface called from JavaScript when the result is ready.
- */
- private class JavascriptResultReady {
-
- /**
- * A callback from JavaScript to Java that passes the result as a
- * parameter. This method is available from the WebView's
- * JavaScript DOM as window.webdriver.resultReady().
- *
- * @param result The result that should be sent to Java from Javascript.
- */
- public void resultReady(final String result) {
- synchronized (mSyncObject) {
- mJsResult = result;
- mCommandDone = true;
- mSyncObject.notify();
- }
- }
- }
-
- /* package */ void notifyCommandDone() {
- synchronized (mSyncObject) {
- mCommandDone = true;
- mSyncObject.notify();
- }
- }
-
- /**
- * Executes the given command by posting a message to mHandler. This thread
- * will block until the command which runs in the main thread is done.
- *
- * @param command The command to run.
- * @param arg The argument for that command.
- * @param timeout A timeout in milliseconds.
- */
- private String executeCommand(int command, final Object arg, long timeout) {
- shouldRunInMainThread(false);
- synchronized (mSyncObject) {
- mCommandDone = false;
- Message msg = mHandler.obtainMessage(command);
- msg.obj = arg;
- mHandler.sendMessage(msg);
-
- long end = System.currentTimeMillis() + timeout;
- while (!mCommandDone) {
- if (System.currentTimeMillis() >= end) {
- throw new RuntimeException("Timeout executing command: "
- + command);
- }
- try {
- mSyncObject.wait(timeout);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- }
- return mJsResult;
- }
-
- private void checkNotNull(Object obj, String errosMsg) {
- if (obj == null) {
- throw new NullPointerException(errosMsg);
- }
- }
-}
diff --git a/core/java/android/webkit/webdriver/WebDriverException.java b/core/java/android/webkit/webdriver/WebDriverException.java
deleted file mode 100644
index 1a579c2..0000000
--- a/core/java/android/webkit/webdriver/WebDriverException.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit.webdriver;
-
-/**
- * @hide
- */
-public class WebDriverException extends RuntimeException {
- public WebDriverException() {
- super();
- }
-
- public WebDriverException(String reason) {
- super(reason);
- }
-
- public WebDriverException(String reason, Throwable cause) {
- super(reason, cause);
- }
-
- public WebDriverException(Throwable cause) {
- super(cause);
- }
-}
diff --git a/core/java/android/webkit/webdriver/WebElement.java b/core/java/android/webkit/webdriver/WebElement.java
deleted file mode 100644
index 02c1595..0000000
--- a/core/java/android/webkit/webdriver/WebElement.java
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit.webdriver;
-
-import android.graphics.Point;
-import android.view.KeyEvent;
-
-import com.android.internal.R;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * Represents an HTML element. Typically most interactions with a web page
- * will be performed through this class.
- *
- * @hide
- */
-public class WebElement {
- private final String mId;
- private final WebDriver mDriver;
-
- private static final String LOCATOR_ID = "id";
- private static final String LOCATOR_LINK_TEXT = "linkText";
- private static final String LOCATOR_PARTIAL_LINK_TEXT = "partialLinkText";
- private static final String LOCATOR_NAME = "name";
- private static final String LOCATOR_CLASS_NAME = "className";
- private static final String LOCATOR_CSS = "css";
- private static final String LOCATOR_TAG_NAME = "tagName";
- private static final String LOCATOR_XPATH = "xpath";
-
- /**
- * Package constructor to prevent clients from creating a new WebElement
- * instance.
- *
- * <p> A WebElement represents an HTML element on the page.
- * The corresponding HTML element is stored in a JS cache in the page
- * that can be accessed through JavaScript using "bot.inject.cache".
- *
- * @param driver The WebDriver instance to use.
- * @param id The index of the HTML element in the JavaSctipt cache.
- * document.documentElement object.
- */
- /* package */ WebElement(final WebDriver driver, final String id) {
- this.mId = id;
- this.mDriver = driver;
- }
-
- /**
- * Finds the first {@link android.webkit.webdriver.WebElement} using the
- * given method.
- *
- * @param by The locating mechanism to use.
- * @return The first matching element on the current context.
- */
- public WebElement findElement(final By by) {
- return by.findElement(this);
- }
-
- /**
- * Finds all {@link android.webkit.webdriver.WebElement} within the page
- * using the given method.
- *
- * @param by The locating mechanism to use.
- * @return A list of all {@link android.webkit.webdriver.WebElement} found,
- * or an empty list if nothing matches.
- */
- public List<WebElement> findElements(final By by) {
- return by.findElements(this);
- }
-
- /**
- * Gets the visisble (i.e. not hidden by CSS) innerText of this element,
- * inlcuding sub-elements.
- *
- * @return the innerText of this element.
- * @throws {@link android.webkit.webdriver.WebElementStaleException} if this
- * element is stale, i.e. not on the current DOM.
- */
- public String getText() {
- String getText = mDriver.getResourceAsString(R.raw.get_text_android);
- return (String) executeAtom(getText, this);
- }
-
- /**
- * Gets the value of an HTML attribute for this element or the value of the
- * property with the same name if the attribute is not present. If neither
- * is set, null is returned.
- *
- * @param attribute the HTML attribute.
- * @return the value of that attribute or the value of the property with the
- * same name if the attribute is not set, or null if neither are set. For
- * boolean attribute values this will return the string "true" or "false".
- */
- public String getAttribute(String attribute) {
- String getAttribute = mDriver.getResourceAsString(
- R.raw.get_attribute_value_android);
- return (String) executeAtom(getAttribute, this, attribute);
- }
-
- /**
- * @return the tag name of this element.
- */
- public String getTagName() {
- return (String) mDriver.executeScript("return arguments[0].tagName;",
- this);
- }
-
- /**
- * @return true if this element is enabled, false otherwise.
- */
- public boolean isEnabled() {
- String isEnabled = mDriver.getResourceAsString(
- R.raw.is_enabled_android);
- return (Boolean) executeAtom(isEnabled, this);
- }
-
- /**
- * Determines whether this element is selected or not. This applies to input
- * elements such as checkboxes, options in a select, and radio buttons.
- *
- * @return True if this element is selected, false otherwise.
- */
- public boolean isSelected() {
- String isSelected = mDriver.getResourceAsString(
- R.raw.is_selected_android);
- return (Boolean) executeAtom(isSelected, this);
- }
-
- /**
- * Selects an element on the page. This works for selecting checkboxes,
- * options in a select, and radio buttons.
- */
- public void setSelected() {
- String setSelected = mDriver.getResourceAsString(
- R.raw.set_selected_android);
- executeAtom(setSelected, this);
- }
-
- /**
- * This toggles the checkboxe state from selected to not selected, or
- * from not selected to selected.
- *
- * @return True if the toggled element is selected, false otherwise.
- */
- public boolean toggle() {
- String toggle = mDriver.getResourceAsString(R.raw.toggle_android);
- return (Boolean) executeAtom(toggle, this);
- }
-
- /**
- * Sends the KeyEvents for the given sequence of characters to the
- * WebElement to simulate typing. The KeyEvents are generated using the
- * device's {@link android.view.KeyCharacterMap.VIRTUAL_KEYBOARD}.
- *
- * @param keys The keys to send to this WebElement
- */
- public void sendKeys(CharSequence... keys) {
- if (keys == null || keys.length == 0) {
- return;
- }
- click();
- mDriver.moveCursorToRightMostPosition(getAttribute("value"));
- mDriver.sendKeys(keys);
- }
-
- /**
- * Use this to send one of the key code constants defined in
- * {@link android.view.KeyEvent}
- *
- * @param keys
- */
- public void sendKeyCodes(int... keys) {
- if (keys == null || keys.length == 0) {
- return;
- }
- click();
- mDriver.moveCursorToRightMostPosition(getAttribute("value"));
- mDriver.sendKeyCodes(keys);
- }
-
- /**
- * Sends a touch event to the center coordinates of this WebElement.
- */
- public void click() {
- Point topLeft = getLocation();
- Point size = getSize();
- int jsX = topLeft.x + size.x/2;
- int jsY = topLeft.y + size.y/2;
- Point center = new Point(jsX, jsY);
- mDriver.sendTouchScreen(center);
- }
-
- /**
- * Submits the form containing this WebElement.
- */
- public void submit() {
- mDriver.resetPageLoadState();
- String submit = mDriver.getResourceAsString(R.raw.submit_android);
- executeAtom(submit, this);
- mDriver.waitForPageLoadIfNeeded();
- }
-
- /**
- * Clears the text value if this is a text entry element. Does nothing
- * otherwise.
- */
- public void clear() {
- String value = getAttribute("value");
- if (value == null || value.equals("")) {
- return;
- }
- int length = value.length();
- int[] keys = new int[length];
- for (int i = 0; i < length; i++) {
- keys[i] = KeyEvent.KEYCODE_DEL;
- }
- sendKeyCodes(keys);
- }
-
- /**
- * @return the value of the given CSS property if found, null otherwise.
- */
- public String getCssValue(String cssProperty) {
- String getCssProp = mDriver.getResourceAsString(
- R.raw.get_value_of_css_property_android);
- return (String) executeAtom(getCssProp, this, cssProperty);
- }
-
- /**
- * Gets the width and height of the rendered element.
- *
- * @return a {@link android.graphics.Point}, where Point.x represents the
- * width, and Point.y represents the height of the element.
- */
- public Point getSize() {
- String getSize = mDriver.getResourceAsString(R.raw.get_size_android);
- Map<String, Long> map = (Map<String, Long>) executeAtom(getSize, this);
- return new Point(map.get("width").intValue(),
- map.get("height").intValue());
- }
-
- /**
- * Gets the location of the top left corner of this element on the screen.
- * If the element is not visisble, this will scroll to get the element into
- * the visisble screen.
- *
- * @return a {@link android.graphics.Point} containing the x and y
- * coordinates of the top left corner of this element.
- */
- public Point getLocation() {
- String getLocation = mDriver.getResourceAsString(
- R.raw.get_top_left_coordinates_android);
- Map<String,Long> map = (Map<String, Long>) executeAtom(getLocation,
- this);
- return new Point(map.get("x").intValue(), map.get("y").intValue());
- }
-
- /**
- * @return True if the WebElement is displayed on the screen,
- * false otherwise.
- */
- public boolean isDisplayed() {
- String isDisplayed = mDriver.getResourceAsString(
- R.raw.is_displayed_android);
- return (Boolean) executeAtom(isDisplayed, this);
- }
-
- /*package*/ String getId() {
- return mId;
- }
-
- /* package */ WebElement findElementById(final String locator) {
- return findElement(LOCATOR_ID, locator);
- }
-
- /* package */ WebElement findElementByLinkText(final String linkText) {
- return findElement(LOCATOR_LINK_TEXT, linkText);
- }
-
- /* package */ WebElement findElementByPartialLinkText(
- final String linkText) {
- return findElement(LOCATOR_PARTIAL_LINK_TEXT, linkText);
- }
-
- /* package */ WebElement findElementByName(final String name) {
- return findElement(LOCATOR_NAME, name);
- }
-
- /* package */ WebElement findElementByClassName(final String className) {
- return findElement(LOCATOR_CLASS_NAME, className);
- }
-
- /* package */ WebElement findElementByCss(final String css) {
- return findElement(LOCATOR_CSS, css);
- }
-
- /* package */ WebElement findElementByTagName(final String tagName) {
- return findElement(LOCATOR_TAG_NAME, tagName);
- }
-
- /* package */ WebElement findElementByXPath(final String xpath) {
- return findElement(LOCATOR_XPATH, xpath);
- }
-
- /* package */ List<WebElement> findElementsById(final String locator) {
- return findElements(LOCATOR_ID, locator);
- }
-
- /* package */ List<WebElement> findElementsByLinkText(final String linkText) {
- return findElements(LOCATOR_LINK_TEXT, linkText);
- }
-
- /* package */ List<WebElement> findElementsByPartialLinkText(
- final String linkText) {
- return findElements(LOCATOR_PARTIAL_LINK_TEXT, linkText);
- }
-
- /* package */ List<WebElement> findElementsByName(final String name) {
- return findElements(LOCATOR_NAME, name);
- }
-
- /* package */ List<WebElement> findElementsByClassName(final String className) {
- return findElements(LOCATOR_CLASS_NAME, className);
- }
-
- /* package */ List<WebElement> findElementsByCss(final String css) {
- return findElements(LOCATOR_CSS, css);
- }
-
- /* package */ List<WebElement> findElementsByTagName(final String tagName) {
- return findElements(LOCATOR_TAG_NAME, tagName);
- }
-
- /* package */ List<WebElement> findElementsByXPath(final String xpath) {
- return findElements(LOCATOR_XPATH, xpath);
- }
-
- private Object executeAtom(final String atom, final Object... args) {
- String scriptArgs = mDriver.convertToJsArgs(args);
- return mDriver.executeRawJavascript("(" +
- atom + ")(" + scriptArgs + ")");
- }
-
- private List<WebElement> findElements(String strategy, String locator) {
- String findElements = mDriver.getResourceAsString(
- R.raw.find_elements_android);
- if (mId.equals("")) {
- return (List<WebElement>) executeAtom(findElements,
- strategy, locator);
- } else {
- return (List<WebElement>) executeAtom(findElements,
- strategy, locator, this);
- }
- }
-
- private WebElement findElement(String strategy, String locator) {
- String findElement = mDriver.getResourceAsString(
- R.raw.find_element_android);
- WebElement el;
- if (mId.equals("")) {
- el = (WebElement) executeAtom(findElement,
- strategy, locator);
- } else {
- el = (WebElement) executeAtom(findElement,
- strategy, locator, this);
- }
- if (el == null) {
- throw new WebElementNotFoundException("Could not find element "
- + "with " + strategy + ": " + locator);
- }
- return el;
- }
-}
diff --git a/core/java/android/webkit/webdriver/WebElementNotFoundException.java b/core/java/android/webkit/webdriver/WebElementNotFoundException.java
deleted file mode 100644
index e66d279..0000000
--- a/core/java/android/webkit/webdriver/WebElementNotFoundException.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit.webdriver;
-
-/**
- * Thrown when a {@link android.webkit.webdriver.WebElement} is not found in the
- * DOM of the page.
- * @hide
- */
-public class WebElementNotFoundException extends RuntimeException {
-
- public WebElementNotFoundException() {
- super();
- }
-
- public WebElementNotFoundException(String reason) {
- super(reason);
- }
-
- public WebElementNotFoundException(String reason, Throwable cause) {
- super(reason, cause);
- }
-
- public WebElementNotFoundException(Throwable cause) {
- super(cause);
- }
-}
diff --git a/core/java/android/webkit/webdriver/WebElementStaleException.java b/core/java/android/webkit/webdriver/WebElementStaleException.java
deleted file mode 100644
index c59e794..0000000
--- a/core/java/android/webkit/webdriver/WebElementStaleException.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit.webdriver;
-
-/**
- * Thrown when trying to access a {@link android.webkit.webdriver.WebElement}
- * that is stale. This mean that the {@link android.webkit.webdriver.WebElement}
- * is no longer present on the DOM of the page.
- * @hide
- */
-public class WebElementStaleException extends RuntimeException {
-
- public WebElementStaleException() {
- super();
- }
-
- public WebElementStaleException(String reason) {
- super(reason);
- }
-
- public WebElementStaleException(String reason, Throwable cause) {
- super(reason, cause);
- }
-
- public WebElementStaleException(Throwable cause) {
- super(cause);
- }
-}
diff --git a/core/java/android/webkit/webdriver/WebViewClient.java b/core/java/android/webkit/webdriver/WebViewClient.java
deleted file mode 100644
index c582b24..0000000
--- a/core/java/android/webkit/webdriver/WebViewClient.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit.webdriver;
-
-import android.graphics.Bitmap;
-import android.net.http.SslError;
-import android.os.Message;
-import android.view.KeyEvent;
-import android.webkit.HttpAuthHandler;
-import android.webkit.SslErrorHandler;
-import android.webkit.WebResourceResponse;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-
-/* package */ class WebViewClientWrapper extends WebViewClient {
- private final WebViewClient mDelegate;
- private final WebDriver mDriver;
-
- public WebViewClientWrapper(WebViewClient delegate, WebDriver driver) {
- if (delegate == null) {
- mDelegate = new WebViewClient();
- } else {
- mDelegate = delegate;
- }
- this.mDriver = driver;
- }
-
- @Override
- public boolean shouldOverrideUrlLoading(WebView view, String url) {
- return mDelegate.shouldOverrideUrlLoading(view, url);
- }
-
- @Override
- public void onPageStarted(WebView view, String url, Bitmap favicon) {
- mDriver.notifyPageStartedLoading();
- mDelegate.onPageStarted(view, url, favicon);
- }
-
- @Override
- public void onPageFinished(WebView view, String url) {
- mDelegate.onPageFinished(view, url);
- }
-
- @Override
- public void onLoadResource(WebView view, String url) {
- mDelegate.onLoadResource(view, url);
- }
-
- @Override
- public WebResourceResponse shouldInterceptRequest(WebView view,
- String url) {
- return mDelegate.shouldInterceptRequest(view, url);
- }
-
- @Override
- public void onTooManyRedirects(WebView view, Message cancelMsg,
- Message continueMsg) {
- mDelegate.onTooManyRedirects(view, cancelMsg, continueMsg);
- }
-
- @Override
- public void onReceivedError(WebView view, int errorCode, String description,
- String failingUrl) {
- mDelegate.onReceivedError(view, errorCode, description, failingUrl);
- }
-
- @Override
- public void onFormResubmission(WebView view, Message dontResend,
- Message resend) {
- mDelegate.onFormResubmission(view, dontResend, resend);
- }
-
- @Override
- public void doUpdateVisitedHistory(WebView view, String url,
- boolean isReload) {
- mDelegate.doUpdateVisitedHistory(view, url, isReload);
- }
-
- @Override
- public void onReceivedSslError(WebView view, SslErrorHandler handler,
- SslError error) {
- mDelegate.onReceivedSslError(view, handler, error);
- }
-
- @Override
- public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler,
- String host, String realm) {
- mDelegate.onReceivedHttpAuthRequest(view, handler, host, realm);
- }
-
- @Override
- public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
- return mDelegate.shouldOverrideKeyEvent(view, event);
- }
-
- @Override
- public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
- mDelegate.onUnhandledKeyEvent(view, event);
- }
-
- @Override
- public void onScaleChanged(WebView view, float oldScale, float newScale) {
- mDelegate.onScaleChanged(view, oldScale, newScale);
- }
-
- @Override
- public void onReceivedLoginRequest(WebView view, String realm,
- String account, String args) {
- mDelegate.onReceivedLoginRequest(view, realm, account, args);
- }
-}
diff --git a/core/java/android/webkit/webdriver/WebchromeClientWrapper.java b/core/java/android/webkit/webdriver/WebchromeClientWrapper.java
deleted file mode 100644
index a9e5d19..0000000
--- a/core/java/android/webkit/webdriver/WebchromeClientWrapper.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit.webdriver;
-
-import android.graphics.Bitmap;
-import android.net.Uri;
-import android.os.Message;
-import android.view.View;
-import android.webkit.ConsoleMessage;
-import android.webkit.GeolocationPermissions;
-import android.webkit.JsPromptResult;
-import android.webkit.JsResult;
-import android.webkit.ValueCallback;
-import android.webkit.WebChromeClient;
-import android.webkit.WebStorage;
-import android.webkit.WebView;
-
-/* package */ class WebChromeClientWrapper extends WebChromeClient {
-
- private final WebChromeClient mDelegate;
- private final WebDriver mDriver;
-
- public WebChromeClientWrapper(WebChromeClient delegate, WebDriver driver) {
- if (delegate == null) {
- this.mDelegate = new WebChromeClient();
- } else {
- this.mDelegate = delegate;
- }
- this.mDriver = driver;
- }
-
- @Override
- public void onProgressChanged(WebView view, int newProgress) {
- if (newProgress == 100) {
- mDriver.notifyPageFinishedLoading();
- }
- mDelegate.onProgressChanged(view, newProgress);
- }
-
- @Override
- public void onReceivedTitle(WebView view, String title) {
- mDelegate.onReceivedTitle(view, title);
- }
-
- @Override
- public void onReceivedIcon(WebView view, Bitmap icon) {
- mDelegate.onReceivedIcon(view, icon);
- }
-
- @Override
- public void onReceivedTouchIconUrl(WebView view, String url,
- boolean precomposed) {
- mDelegate.onReceivedTouchIconUrl(view, url, precomposed);
- }
-
- @Override
- public void onShowCustomView(View view,
- CustomViewCallback callback) {
- mDelegate.onShowCustomView(view, callback);
- }
-
- @Override
- public void onHideCustomView() {
- mDelegate.onHideCustomView();
- }
-
- @Override
- public boolean onCreateWindow(WebView view, boolean dialog,
- boolean userGesture, Message resultMsg) {
- return mDelegate.onCreateWindow(view, dialog, userGesture, resultMsg);
- }
-
- @Override
- public void onRequestFocus(WebView view) {
- mDelegate.onRequestFocus(view);
- }
-
- @Override
- public void onCloseWindow(WebView window) {
- mDelegate.onCloseWindow(window);
- }
-
- @Override
- public boolean onJsAlert(WebView view, String url, String message,
- JsResult result) {
- return mDelegate.onJsAlert(view, url, message, result);
- }
-
- @Override
- public boolean onJsConfirm(WebView view, String url, String message,
- JsResult result) {
- return mDelegate.onJsConfirm(view, url, message, result);
- }
-
- @Override
- public boolean onJsPrompt(WebView view, String url, String message,
- String defaultValue, JsPromptResult result) {
- return mDelegate.onJsPrompt(view, url, message, defaultValue, result);
- }
-
- @Override
- public boolean onJsBeforeUnload(WebView view, String url, String message,
- JsResult result) {
- return mDelegate.onJsBeforeUnload(view, url, message, result);
- }
-
- @Override
- public void onExceededDatabaseQuota(String url, String databaseIdentifier,
- long currentQuota, long estimatedSize, long totalUsedQuota,
- WebStorage.QuotaUpdater quotaUpdater) {
- mDelegate.onExceededDatabaseQuota(url, databaseIdentifier, currentQuota,
- estimatedSize, totalUsedQuota, quotaUpdater);
- }
-
- @Override
- public void onReachedMaxAppCacheSize(long spaceNeeded, long totalUsedQuota,
- WebStorage.QuotaUpdater quotaUpdater) {
- mDelegate.onReachedMaxAppCacheSize(spaceNeeded, totalUsedQuota,
- quotaUpdater);
- }
-
- @Override
- public void onGeolocationPermissionsShowPrompt(String origin,
- GeolocationPermissions.Callback callback) {
- mDelegate.onGeolocationPermissionsShowPrompt(origin, callback);
- }
-
- @Override
- public void onGeolocationPermissionsHidePrompt() {
- mDelegate.onGeolocationPermissionsHidePrompt();
- }
-
- @Override
- public boolean onJsTimeout() {
- return mDelegate.onJsTimeout();
- }
-
- @Override
- public void onConsoleMessage(String message, int lineNumber,
- String sourceID) {
- mDelegate.onConsoleMessage(message, lineNumber, sourceID);
- }
-
- @Override
- public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
- return mDelegate.onConsoleMessage(consoleMessage);
- }
-
- @Override
- public Bitmap getDefaultVideoPoster() {
- return mDelegate.getDefaultVideoPoster();
- }
-
- @Override
- public View getVideoLoadingProgressView() {
- return mDelegate.getVideoLoadingProgressView();
- }
-
- @Override
- public void getVisitedHistory(ValueCallback<String[]> callback) {
- mDelegate.getVisitedHistory(callback);
- }
-
- @Override
- public void openFileChooser(ValueCallback<Uri> uploadFile,
- String acceptType) {
- mDelegate.openFileChooser(uploadFile, acceptType);
- }
-
- @Override
- public void setInstallableWebApp() {
- mDelegate.setInstallableWebApp();
- }
-
- @Override
- public void setupAutoFill(Message msg) {
- mDelegate.setupAutoFill(msg);
- }
-}
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 2621e64..df8eb05 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -24,6 +24,7 @@ import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.ViewConfiguration;
public abstract class AbsSeekBar extends ProgressBar {
private Drawable mThumb;
@@ -49,6 +50,10 @@ public abstract class AbsSeekBar extends ProgressBar {
private static final int NO_ALPHA = 0xFF;
private float mDisabledAlpha;
+ private int mScaledTouchSlop;
+ private float mTouchDownX;
+ private boolean mIsDragging;
+
public AbsSeekBar(Context context) {
super(context);
}
@@ -74,6 +79,8 @@ public abstract class AbsSeekBar extends ProgressBar {
com.android.internal.R.styleable.Theme, 0, 0);
mDisabledAlpha = a.getFloat(com.android.internal.R.styleable.Theme_disabledAlpha, 0.5f);
a.recycle();
+
+ mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
/**
@@ -324,20 +331,42 @@ public abstract class AbsSeekBar extends ProgressBar {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
- setPressed(true);
- onStartTrackingTouch();
- trackTouchEvent(event);
+ if (isInScrollingContainer()) {
+ mTouchDownX = event.getX();
+ } else {
+ setPressed(true);
+ onStartTrackingTouch();
+ trackTouchEvent(event);
+ attemptClaimDrag();
+ }
break;
case MotionEvent.ACTION_MOVE:
- trackTouchEvent(event);
- attemptClaimDrag();
+ if (mIsDragging) {
+ trackTouchEvent(event);
+ } else {
+ final float x = event.getX();
+ if (Math.abs(x - mTouchDownX) > mScaledTouchSlop) {
+ setPressed(true);
+ onStartTrackingTouch();
+ trackTouchEvent(event);
+ attemptClaimDrag();
+ }
+ }
break;
case MotionEvent.ACTION_UP:
- trackTouchEvent(event);
- onStopTrackingTouch();
- setPressed(false);
+ if (mIsDragging) {
+ trackTouchEvent(event);
+ onStopTrackingTouch();
+ setPressed(false);
+ } else {
+ // Touch up when we never crossed the touch slop threshold should
+ // be interpreted as a tap-seek to that location.
+ onStartTrackingTouch();
+ trackTouchEvent(event);
+ onStopTrackingTouch();
+ }
// ProgressBar doesn't know to repaint the thumb drawable
// in its inactive state when the touch stops (because the
// value has not apparently changed)
@@ -345,8 +374,10 @@ public abstract class AbsSeekBar extends ProgressBar {
break;
case MotionEvent.ACTION_CANCEL:
- onStopTrackingTouch();
- setPressed(false);
+ if (mIsDragging) {
+ onStopTrackingTouch();
+ setPressed(false);
+ }
invalidate(); // see above explanation
break;
}
@@ -388,6 +419,7 @@ public abstract class AbsSeekBar extends ProgressBar {
* This is called when the user has started touching this widget.
*/
void onStartTrackingTouch() {
+ mIsDragging = true;
}
/**
@@ -395,6 +427,7 @@ public abstract class AbsSeekBar extends ProgressBar {
* canceled.
*/
void onStopTrackingTouch() {
+ mIsDragging = false;
}
/**
diff --git a/core/java/android/widget/SeekBar.java b/core/java/android/widget/SeekBar.java
index dfee29b..c76728f 100644
--- a/core/java/android/widget/SeekBar.java
+++ b/core/java/android/widget/SeekBar.java
@@ -104,6 +104,7 @@ public class SeekBar extends AbsSeekBar {
@Override
void onStartTrackingTouch() {
+ super.onStartTrackingTouch();
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onStartTrackingTouch(this);
}
@@ -111,6 +112,7 @@ public class SeekBar extends AbsSeekBar {
@Override
void onStopTrackingTouch() {
+ super.onStopTrackingTouch();
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onStopTrackingTouch(this);
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index efcd8ab..1ab1a87 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -16,11 +16,6 @@
package android.widget;
-import com.android.internal.util.FastMath;
-import com.android.internal.widget.EditableInputConnection;
-
-import org.xmlpull.v1.XmlPullParserException;
-
import android.R;
import android.content.ClipData;
import android.content.ClipData.Item;
@@ -139,6 +134,16 @@ import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.widget.RemoteViews.RemoteView;
+import com.android.internal.util.FastMath;
+import com.android.internal.widget.EditableInputConnection;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import com.android.internal.util.FastMath;
+import com.android.internal.widget.EditableInputConnection;
+
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.text.BreakIterator;
@@ -220,6 +225,7 @@ import java.util.HashMap;
* @attr ref android.R.styleable#TextView_imeActionLabel
* @attr ref android.R.styleable#TextView_imeActionId
* @attr ref android.R.styleable#TextView_editorExtras
+ * @attr ref android.R.styleable#TextView_suggestionsEnabled
*/
@RemoteView
public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
@@ -2493,12 +2499,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @attr ref android.R.styleable#TextView_scrollHorizontally
*/
public void setHorizontallyScrolling(boolean whether) {
- mHorizontallyScrolling = whether;
+ if (mHorizontallyScrolling != whether) {
+ mHorizontallyScrolling = whether;
- if (mLayout != null) {
- nullLayouts();
- requestLayout();
- invalidate();
+ if (mLayout != null) {
+ nullLayouts();
+ requestLayout();
+ invalidate();
+ }
}
}
@@ -2699,13 +2707,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @attr ref android.R.styleable#TextView_lineSpacingMultiplier
*/
public void setLineSpacing(float add, float mult) {
- mSpacingMult = mult;
- mSpacingAdd = add;
+ if (mSpacingAdd != add || mSpacingMult != mult) {
+ mSpacingAdd = add;
+ mSpacingMult = mult;
- if (mLayout != null) {
- nullLayouts();
- requestLayout();
- invalidate();
+ if (mLayout != null) {
+ nullLayouts();
+ requestLayout();
+ invalidate();
+ }
}
}
@@ -6276,12 +6286,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @attr ref android.R.styleable#TextView_includeFontPadding
*/
public void setIncludeFontPadding(boolean includepad) {
- mIncludePad = includepad;
+ if (mIncludePad != includepad) {
+ mIncludePad = includepad;
- if (mLayout != null) {
- nullLayouts();
- requestLayout();
- invalidate();
+ if (mLayout != null) {
+ nullLayouts();
+ requestLayout();
+ invalidate();
+ }
}
}
@@ -6605,7 +6617,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
} else {
// Dynamic width, so we have no choice but to request a new
// view layout with a new text layout.
-
nullLayouts();
requestLayout();
invalidate();
@@ -7098,12 +7109,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @attr ref android.R.styleable#TextView_ellipsize
*/
public void setEllipsize(TextUtils.TruncateAt where) {
- mEllipsize = where;
+ // TruncateAt is an enum. != comparison is ok between these singleton objects.
+ if (mEllipsize != where) {
+ mEllipsize = where;
- if (mLayout != null) {
- nullLayouts();
- requestLayout();
- invalidate();
+ if (mLayout != null) {
+ nullLayouts();
+ requestLayout();
+ invalidate();
+ }
}
}
@@ -9410,7 +9424,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
void showSuggestions() {
- if (!mSuggestionsEnabled || !isTextEditable()) return;
+ if (!isSuggestionsEnabled() || !isTextEditable()) return;
if (mSuggestionsPopupWindow == null) {
mSuggestionsPopupWindow = new SuggestionsPopupWindow();
@@ -9437,18 +9451,41 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* user double taps on these parts of the text. No suggestions are displayed when this value is
* false. Use {@link #setSuggestionsEnabled(boolean)} to change this value.
*
+ * Note that suggestions are only enabled for a subset of input types. In addition to setting
+ * this flag to <code>true</code> using {@link #setSuggestionsEnabled(boolean)} or the
+ * <code>android:suggestionsEnabled</code> xml attribute, this method will return
+ * <code>true</code> only if the class of your input type is {@link InputType#TYPE_CLASS_TEXT}.
+ * In addition, the type variation must also be one of
+ * {@link InputType#TYPE_TEXT_VARIATION_NORMAL},
+ * {@link InputType#TYPE_TEXT_VARIATION_EMAIL_SUBJECT},
+ * {@link InputType#TYPE_TEXT_VARIATION_LONG_MESSAGE},
+ * {@link InputType#TYPE_TEXT_VARIATION_SHORT_MESSAGE} or
+ * {@link InputType#TYPE_TEXT_VARIATION_WEB_EDIT_TEXT}.
+ *
* @return true if the suggestions popup window is enabled.
*
* @attr ref android.R.styleable#TextView_suggestionsEnabled
*/
public boolean isSuggestionsEnabled() {
- return mSuggestionsEnabled;
+ if (!mSuggestionsEnabled) return false;
+ if ((mInputType & InputType.TYPE_MASK_CLASS) != InputType.TYPE_CLASS_TEXT) return false;
+ final int variation =
+ mInputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION);
+ if (variation == EditorInfo.TYPE_TEXT_VARIATION_NORMAL ||
+ variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_SUBJECT ||
+ variation == EditorInfo.TYPE_TEXT_VARIATION_LONG_MESSAGE ||
+ variation == EditorInfo.TYPE_TEXT_VARIATION_SHORT_MESSAGE ||
+ variation == EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT) return true;
+
+ return false;
}
/**
* Enables or disables the suggestion popup. See {@link #isSuggestionsEnabled()}.
*
* @param enabled Whether or not suggestions are enabled.
+ *
+ * @attr ref android.R.styleable#TextView_suggestionsEnabled
*/
public void setSuggestionsEnabled(boolean enabled) {
mSuggestionsEnabled = enabled;
@@ -9720,10 +9757,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@Override
public void show() {
- mPasteTextView.setVisibility(canPaste() ? View.VISIBLE : View.GONE);
- mReplaceTextView.setVisibility(mSuggestionsEnabled ? View.VISIBLE : View.GONE);
+ boolean canPaste = canPaste();
+ boolean suggestionsEnabled = isSuggestionsEnabled();
+ mPasteTextView.setVisibility(canPaste ? View.VISIBLE : View.GONE);
+ mReplaceTextView.setVisibility(suggestionsEnabled ? View.VISIBLE : View.GONE);
- if (!canPaste() && !mSuggestionsEnabled) return;
+ if (!canPaste && !suggestionsEnabled) return;
super.show();
}
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index 0df7bcc..31360e1 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -253,7 +253,7 @@ public class ActionBarImpl extends ActionBar {
@Override
public void setCustomView(int resId) {
- setCustomView(LayoutInflater.from(mContext).inflate(resId, mActionView, false));
+ setCustomView(LayoutInflater.from(getThemedContext()).inflate(resId, mActionView, false));
}
@Override
@@ -630,14 +630,14 @@ public class ActionBarImpl extends ActionBar {
public ActionModeImpl(ActionMode.Callback callback) {
mCallback = callback;
- mMenu = new MenuBuilder(mActionView.getContext())
+ mMenu = new MenuBuilder(getThemedContext())
.setDefaultShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
mMenu.setCallback(this);
}
@Override
public MenuInflater getMenuInflater() {
- return new MenuInflater(mContext);
+ return new MenuInflater(getThemedContext());
}
@Override
@@ -755,7 +755,7 @@ public class ActionBarImpl extends ActionBar {
return true;
}
- new MenuPopupHelper(mContext, subMenu).show();
+ new MenuPopupHelper(getThemedContext(), subMenu).show();
return true;
}
@@ -819,7 +819,8 @@ public class ActionBarImpl extends ActionBar {
@Override
public Tab setCustomView(int layoutResId) {
- return setCustomView(LayoutInflater.from(mContext).inflate(layoutResId, null));
+ return setCustomView(LayoutInflater.from(getThemedContext())
+ .inflate(layoutResId, null));
}
@Override
diff --git a/core/java/com/android/internal/os/ProcessStats.java b/core/java/com/android/internal/os/ProcessStats.java
index ea5ce09..e0e9a29 100644
--- a/core/java/com/android/internal/os/ProcessStats.java
+++ b/core/java/com/android/internal/os/ProcessStats.java
@@ -19,6 +19,7 @@ package com.android.internal.os;
import static android.os.Process.*;
import android.os.Process;
+import android.os.StrictMode;
import android.os.SystemClock;
import android.util.Slog;
@@ -798,6 +799,10 @@ public class ProcessStats {
}
private String readFile(String file, char endChar) {
+ // Permit disk reads here, as /proc/meminfo isn't really "on
+ // disk" and should be fast. TODO: make BlockGuard ignore
+ // /proc/ and /sys/ files perhaps?
+ StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
FileInputStream is = null;
try {
is = new FileInputStream(file);
@@ -822,6 +827,7 @@ public class ProcessStats {
} catch (java.io.IOException e) {
}
}
+ StrictMode.setThreadPolicy(savedPolicy);
}
return null;
}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
index 06f753f..aaae691 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
@@ -162,7 +162,6 @@ public class ActionMenuPresenter extends BaseMenuPresenter {
final ActionMenuView menuView = (ActionMenuView) mMenuView;
ActionMenuItemView actionItemView = (ActionMenuItemView) itemView;
actionItemView.setItemInvoker(menuView);
- if (false) actionItemView.setExpandedFormat(menuView.isExpandedFormat());
}
@Override
@@ -174,7 +173,8 @@ public class ActionMenuPresenter extends BaseMenuPresenter {
public void updateMenuView(boolean cleared) {
super.updateMenuView(cleared);
- if (mReserveOverflow && mMenu.getNonActionItems().size() > 0) {
+ final boolean hasOverflow = mReserveOverflow && mMenu.getNonActionItems().size() > 0;
+ if (hasOverflow) {
if (mOverflowButton == null) {
mOverflowButton = new OverflowMenuButton(mContext);
}
@@ -189,6 +189,8 @@ public class ActionMenuPresenter extends BaseMenuPresenter {
} else if (mOverflowButton != null && mOverflowButton.getParent() == mMenuView) {
((ViewGroup) mMenuView).removeView(mOverflowButton);
}
+
+ ((ActionMenuView) mMenuView).setOverflowReserved(mReserveOverflow);
}
@Override
diff --git a/core/java/com/android/internal/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java
index bff621c..267221b 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuView.java
@@ -115,17 +115,26 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
int maxChildHeight = 0;
int maxCellsUsed = 0;
int expandableItemCount = 0;
+ int visibleItemCount = 0;
+ boolean hasOverflow = false;
- if (mReserveOverflow) cellsRemaining--;
+ // This is used as a bitfield to locate the smallest items present. Assumes childCount < 64.
+ long smallestItemsAt = 0;
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
+ if (child.getVisibility() == GONE) continue;
+
+ visibleItemCount++;
+
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
lp.expanded = false;
lp.extraPixels = 0;
lp.cellsUsed = 0;
lp.expandable = false;
+ lp.leftMargin = 0;
+ lp.rightMargin = 0;
// Overflow always gets 1 cell. No more, no less.
final int cellsAvailable = lp.isOverflowButton ? 1 : cellsRemaining;
@@ -135,16 +144,17 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
maxCellsUsed = Math.max(maxCellsUsed, cellsUsed);
if (lp.expandable) expandableItemCount++;
+ if (lp.isOverflowButton) hasOverflow = true;
cellsRemaining -= cellsUsed;
maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight());
+ if (cellsUsed == 1) smallestItemsAt |= (1 << i);
}
// Divide space for remaining cells if we have items that can expand.
// Try distributing whole leftover cells to smaller items first.
boolean needsExpansion = false;
- long smallestExpandableItemsAt = 0;
while (expandableItemCount > 0 && cellsRemaining > 0) {
int minCells = Integer.MAX_VALUE;
long minCellsAt = 0; // Bit locations are indices of relevant child views
@@ -170,7 +180,7 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
if (minCellsItemCount > cellsRemaining) break; // Couldn't expand anything evenly. Stop.
// Items that get expanded will always be in the set of smallest items when we're done.
- smallestExpandableItemsAt |= minCellsAt;
+ smallestItemsAt |= minCellsAt;
for (int i = 0; i < childCount; i++) {
if ((minCellsAt & (1 << i)) == 0) continue;
@@ -186,22 +196,58 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
}
// Divide any space left that wouldn't divide along cell boundaries
- // evenly among the smallest multi-cell (expandable) items.
+ // evenly among the smallest items
+
+ final boolean singleItem = !hasOverflow && visibleItemCount == 1;
+ if (cellsRemaining > 0 && smallestItemsAt != 0 &&
+ (cellsRemaining < visibleItemCount - 1 || singleItem)) {
+ float expandCount = Long.bitCount(smallestItemsAt);
+
+ if (!singleItem) {
+ // The items at the far edges may only expand by half in order to pin to either side.
+ if ((smallestItemsAt & 1) != 0) {
+ expandCount -= 0.5f;
+ }
+ if ((smallestItemsAt & (1 << (childCount - 1))) != 0) {
+ expandCount -= 0.5f;
+ }
+ }
- if (cellsRemaining > 0 && smallestExpandableItemsAt != 0) {
- final int expandCount = Long.bitCount(smallestExpandableItemsAt);
- final int extraPixels = cellsRemaining * cellSize / expandCount;
+ final int extraPixels = (int) (cellsRemaining * cellSize / expandCount);
for (int i = 0; i < childCount; i++) {
- if ((smallestExpandableItemsAt & (1 << i)) == 0) continue;
+ if ((smallestItemsAt & (1 << i)) == 0) continue;
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- lp.extraPixels = extraPixels;
- lp.expanded = true;
+ if (child instanceof ActionMenuItemView) {
+ // If this is one of our views, expand and measure at the larger size.
+ lp.extraPixels = extraPixels;
+ lp.expanded = true;
+ if (i == 0) {
+ // First item gets part of its new padding pushed out of sight.
+ // The last item will get this implicitly from layout.
+ lp.leftMargin = -extraPixels / 2;
+ }
+ needsExpansion = true;
+ } else if (lp.isOverflowButton) {
+ lp.extraPixels = extraPixels;
+ lp.expanded = true;
+ lp.rightMargin = -extraPixels / 2;
+ needsExpansion = true;
+ } else {
+ // If we don't know what it is, give it some margins instead
+ // and let it center within its space. We still want to pin
+ // against the edges.
+ if (i != 0) {
+ lp.leftMargin = extraPixels / 2;
+ }
+ if (i != childCount - 1) {
+ lp.rightMargin = extraPixels / 2;
+ }
+ }
}
- needsExpansion = true;
cellsRemaining = 0;
}
@@ -301,7 +347,7 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
}
int height = v.getMeasuredHeight();
- int r = getWidth() - getPaddingRight();
+ int r = getWidth() - getPaddingRight() - p.rightMargin;
int l = r - overflowWidth;
int t = midVertical - (height / 2);
int b = t + height;
@@ -320,8 +366,20 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
}
}
+ if (childCount == 1 && !hasOverflow) {
+ // Center a single child
+ final View v = getChildAt(0);
+ final int width = v.getMeasuredWidth();
+ final int height = v.getMeasuredHeight();
+ final int midHorizontal = (right - left) / 2;
+ final int l = midHorizontal - width / 2;
+ final int t = midVertical - height / 2;
+ v.layout(l, t, l + width, t + height);
+ return;
+ }
+
final int spacerCount = nonOverflowCount - (hasOverflow ? 0 : 1);
- final int spacerSize = spacerCount > 0 ? widthRemaining / spacerCount : 0;
+ final int spacerSize = Math.max(0, spacerCount > 0 ? widthRemaining / spacerCount : 0);
int startLeft = getPaddingLeft();
for (int i = 0; i < childCount; i++) {
@@ -334,7 +392,7 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo
startLeft += lp.leftMargin;
int width = v.getMeasuredWidth();
int height = v.getMeasuredHeight();
- int t = midVertical - (height / 2);
+ int t = midVertical - height / 2;
v.layout(startLeft, t, startLeft + width, t + height);
startLeft += width + lp.rightMargin + spacerSize;
}
diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java
index b4d2d72..fd9ee08 100644
--- a/core/java/com/android/internal/widget/ActionBarContainer.java
+++ b/core/java/com/android/internal/widget/ActionBarContainer.java
@@ -25,6 +25,7 @@ import android.util.AttributeSet;
import android.view.ActionMode;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.FrameLayout;
/**
@@ -109,7 +110,9 @@ public class ActionBarContainer extends FrameLayout {
mTabContainer = tabView;
if (tabView != null) {
addView(tabView);
- tabView.getLayoutParams().width = LayoutParams.MATCH_PARENT;
+ final ViewGroup.LayoutParams lp = tabView.getLayoutParams();
+ lp.width = LayoutParams.MATCH_PARENT;
+ lp.height = LayoutParams.WRAP_CONTENT;
tabView.setAllowCollapse(false);
}
}
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index 3e3eeab..5645a6f 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -15,12 +15,18 @@
*/
package com.android.internal.widget;
+import com.android.internal.R;
+import com.android.internal.view.menu.ActionMenuPresenter;
+import com.android.internal.view.menu.ActionMenuView;
+import com.android.internal.view.menu.MenuBuilder;
+
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.ActionMode;
@@ -30,11 +36,6 @@ import android.view.animation.DecelerateInterpolator;
import android.widget.LinearLayout;
import android.widget.TextView;
-import com.android.internal.R;
-import com.android.internal.view.menu.ActionMenuPresenter;
-import com.android.internal.view.menu.ActionMenuView;
-import com.android.internal.view.menu.MenuBuilder;
-
/**
* @hide
*/
@@ -53,6 +54,7 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi
private TextView mSubtitleView;
private int mTitleStyleRes;
private int mSubtitleStyleRes;
+ private Drawable mSplitBackground;
private Animator mCurrentAnimation;
private boolean mAnimateInOnLayout;
@@ -83,6 +85,10 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi
mContentHeight = a.getLayoutDimension(
com.android.internal.R.styleable.ActionMode_height, 0);
+
+ mSplitBackground = a.getDrawable(
+ com.android.internal.R.styleable.ActionMode_backgroundSplit);
+
a.recycle();
}
@@ -175,6 +181,7 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi
if (mSplitView == null) {
menu.addMenuPresenter(mActionMenuPresenter);
mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
+ mMenuView.setBackgroundDrawable(null);
addView(mMenuView, layoutParams);
} else {
// Allow full screen width in split mode.
@@ -187,6 +194,7 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi
layoutParams.height = mContentHeight;
menu.addMenuPresenter(mActionMenuPresenter);
mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
+ mMenuView.setBackgroundDrawable(mSplitBackground);
mSplitView.addView(mMenuView, layoutParams);
}
@@ -256,7 +264,12 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi
protected LayoutParams generateDefaultLayoutParams() {
// Used by custom views if they don't supply layout params. Everything else
// added to an ActionBarContextView should have them already.
- return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+ return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new MarginLayoutParams(getContext(), attrs);
}
@Override
@@ -285,6 +298,8 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi
if (mClose != null) {
availableWidth = measureChildView(mClose, availableWidth, childSpecHeight, 0);
+ MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams();
+ availableWidth -= lp.leftMargin + lp.rightMargin;
}
if (mMenuView != null && mMenuView.getParent() == this) {
@@ -327,7 +342,8 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi
}
private Animator makeInAnimation() {
- mClose.setTranslationX(-mClose.getWidth());
+ mClose.setTranslationX(-mClose.getWidth() -
+ ((MarginLayoutParams) mClose.getLayoutParams()).leftMargin);
ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(mClose, "translationX", 0);
buttonAnimator.setDuration(200);
buttonAnimator.addListener(this);
@@ -355,7 +371,7 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi
private Animator makeOutAnimation() {
ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(mClose, "translationX",
- -mClose.getWidth());
+ -mClose.getWidth() - ((MarginLayoutParams) mClose.getLayoutParams()).leftMargin);
buttonAnimator.setDuration(200);
buttonAnimator.addListener(this);
buttonAnimator.setInterpolator(new DecelerateInterpolator());
@@ -387,7 +403,10 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi
final int contentHeight = b - t - getPaddingTop() - getPaddingBottom();
if (mClose != null && mClose.getVisibility() != GONE) {
+ MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams();
+ x += lp.leftMargin;
x += positionChild(mClose, x, y, contentHeight);
+ x += lp.rightMargin;
if (mAnimateInOnLayout) {
mAnimationMode = ANIMATE_IN;
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 181958c..83f3294 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -462,8 +462,10 @@ public class ActionBarView extends AbsActionBarView {
mTitle = title;
if (mTitleView != null) {
mTitleView.setText(title);
- mTitleLayout.setVisibility(TextUtils.isEmpty(mTitle) && TextUtils.isEmpty(mSubtitle) ?
- GONE : VISIBLE);
+ final boolean visible = mExpandedActionView == null &&
+ (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0 &&
+ (!TextUtils.isEmpty(mTitle) || !TextUtils.isEmpty(mSubtitle));
+ mTitleLayout.setVisibility(visible ? VISIBLE : GONE);
}
if (mLogoNavItem != null) {
mLogoNavItem.setTitle(title);
@@ -479,8 +481,10 @@ public class ActionBarView extends AbsActionBarView {
if (mSubtitleView != null) {
mSubtitleView.setText(subtitle);
mSubtitleView.setVisibility(subtitle != null ? VISIBLE : GONE);
- mTitleLayout.setVisibility(TextUtils.isEmpty(mTitle) && TextUtils.isEmpty(mSubtitle) ?
- GONE : VISIBLE);
+ final boolean visible = mExpandedActionView == null &&
+ (mDisplayOptions & ActionBar.DISPLAY_SHOW_TITLE) != 0 &&
+ (!TextUtils.isEmpty(mTitle) || !TextUtils.isEmpty(mSubtitle));
+ mTitleLayout.setVisibility(visible ? VISIBLE : GONE);
}
}
@@ -575,7 +579,7 @@ public class ActionBarView extends AbsActionBarView {
}
public void setIcon(int resId) {
- setIcon(mContext.getResources().getDrawableForDensity(resId, getPreferredIconDensity()));
+ setIcon(mContext.getResources().getDrawable(resId));
}
public void setLogo(Drawable logo) {
@@ -589,25 +593,6 @@ public class ActionBarView extends AbsActionBarView {
setLogo(mContext.getResources().getDrawable(resId));
}
- /**
- * @return Drawable density to load that will best fit the available height.
- */
- private int getPreferredIconDensity() {
- final Resources res = mContext.getResources();
- final int availableHeight = getLayoutParams().height -
- mHomeLayout.getVerticalIconPadding();
- int iconSize = res.getDimensionPixelSize(android.R.dimen.app_icon_size);
-
- if (iconSize * DisplayMetrics.DENSITY_LOW >= availableHeight) {
- return DisplayMetrics.DENSITY_LOW;
- } else if (iconSize * DisplayMetrics.DENSITY_MEDIUM >= availableHeight) {
- return DisplayMetrics.DENSITY_MEDIUM;
- } else if (iconSize * DisplayMetrics.DENSITY_HIGH >= availableHeight) {
- return DisplayMetrics.DENSITY_HIGH;
- }
- return DisplayMetrics.DENSITY_XHIGH;
- }
-
public void setNavigationMode(int mode) {
final int oldMode = mNavigationMode;
if (mode != oldMode) {
@@ -739,7 +724,12 @@ public class ActionBarView extends AbsActionBarView {
mTitleLayout.setEnabled(titleUp);
}
- addView(mTitleLayout);
+ addView(mTitleLayout, new LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.MATCH_PARENT));
+ if (mExpandedActionView != null) {
+ // Don't show while in expanded mode
+ mTitleLayout.setVisibility(GONE);
+ }
}
public void setContextView(ActionBarContextView view) {
@@ -1342,6 +1332,7 @@ public class ActionBarView extends AbsActionBarView {
removeView(mExpandedActionView);
removeView(mExpandedHomeLayout);
+ mExpandedActionView = null;
if ((mDisplayOptions & ActionBar.DISPLAY_SHOW_HOME) != 0) {
mHomeLayout.setVisibility(VISIBLE);
}
@@ -1361,7 +1352,6 @@ public class ActionBarView extends AbsActionBarView {
if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
mCustomNavView.setVisibility(VISIBLE);
}
- mExpandedActionView = null;
mExpandedHomeLayout.setIcon(null);
mCurrentExpandedItem = null;
requestLayout();
diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
index 0e4c9ef..71f9364 100644
--- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java
+++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
@@ -68,6 +68,11 @@ public class ScrollingTabContainerView extends HorizontalScrollView
super(context);
setHorizontalScrollBarEnabled(false);
+ TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.ActionBar,
+ com.android.internal.R.attr.actionBarStyle, 0);
+ setContentHeight(a.getLayoutDimension(R.styleable.ActionBar_height, 0));
+ a.recycle();
+
mTabLayout = createTabLayout();
addView(mTabLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.MATCH_PARENT));
@@ -91,16 +96,7 @@ public class ScrollingTabContainerView extends HorizontalScrollView
mMaxTabWidth = -1;
}
- int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- int heightSize = MeasureSpec.getSize(heightMeasureSpec);
- if (heightMode != MeasureSpec.UNSPECIFIED) {
- if (mContentHeight == 0 && heightMode == MeasureSpec.EXACTLY) {
- // Use this as our content height.
- mContentHeight = heightSize;
- }
- heightSize = Math.min(heightSize, mContentHeight);
- heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
- }
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.EXACTLY);
final boolean canCollapse = !lockedExpanded && mAllowCollapse;