diff options
41 files changed, 1713 insertions, 1592 deletions
diff --git a/api/current.xml b/api/current.xml index 00f7505e70e3..eb1c6d61e9f7 100644 --- a/api/current.xml +++ b/api/current.xml @@ -4611,7 +4611,7 @@ type="int" transient="false" volatile="false" - value="16843448" + value="16843447" static="true" final="true" deprecated="not deprecated" @@ -5773,17 +5773,6 @@ visibility="public" > </field> -<field name="neverEncrypt" - type="int" - transient="false" - volatile="false" - value="16843447" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> <field name="nextFocusDown" type="int" transient="false" @@ -5971,6 +5960,17 @@ visibility="public" > </field> +<field name="overscrollMode" + type="int" + transient="false" + volatile="false" + value="16843450" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="padding" type="int" transient="false" @@ -6855,7 +6855,7 @@ type="int" transient="false" volatile="false" - value="16843449" + value="16843448" static="true" final="true" deprecated="not deprecated" @@ -9073,6 +9073,17 @@ visibility="public" > </field> +<field name="webTextViewStyle" + type="int" + transient="false" + volatile="false" + value="16843449" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="webViewStyle" type="int" transient="false" @@ -20056,24 +20067,34 @@ </parameter> </method> </interface> -<class name="DeviceAdmin" - extends="android.content.BroadcastReceiver" +<class name="DeviceAdminInfo" + extends="java.lang.Object" abstract="false" static="false" - final="false" + final="true" deprecated="not deprecated" visibility="public" > -<constructor name="DeviceAdmin" - type="android.app.DeviceAdmin" +<implements name="android.os.Parcelable"> +</implements> +<constructor name="DeviceAdminInfo" + type="android.app.DeviceAdminInfo" static="false" final="false" deprecated="not deprecated" visibility="public" > +<parameter name="context" type="android.content.Context"> +</parameter> +<parameter name="receiver" type="android.content.pm.ResolveInfo"> +</parameter> +<exception name="IOException" type="java.io.IOException"> +</exception> +<exception name="XmlPullParserException" type="org.xmlpull.v1.XmlPullParserException"> +</exception> </constructor> -<method name="getManager" - return="android.app.DevicePolicyManager" +<method name="describeContents" + return="int" abstract="false" native="false" synchronized="false" @@ -20082,11 +20103,9 @@ deprecated="not deprecated" visibility="public" > -<parameter name="context" type="android.content.Context"> -</parameter> </method> -<method name="getWho" - return="android.content.ComponentName" +<method name="dump" + return="void" abstract="false" native="false" synchronized="false" @@ -20095,11 +20114,13 @@ deprecated="not deprecated" visibility="public" > -<parameter name="context" type="android.content.Context"> +<parameter name="pw" type="android.util.Printer"> +</parameter> +<parameter name="prefix" type="java.lang.String"> </parameter> </method> -<method name="onDisableRequested" - return="java.lang.CharSequence" +<method name="getActivityInfo" + return="android.content.pm.ActivityInfo" abstract="false" native="false" synchronized="false" @@ -20108,13 +20129,9 @@ deprecated="not deprecated" visibility="public" > -<parameter name="context" type="android.content.Context"> -</parameter> -<parameter name="intent" type="android.content.Intent"> -</parameter> </method> -<method name="onDisabled" - return="void" +<method name="getComponent" + return="android.content.ComponentName" abstract="false" native="false" synchronized="false" @@ -20123,13 +20140,9 @@ deprecated="not deprecated" visibility="public" > -<parameter name="context" type="android.content.Context"> -</parameter> -<parameter name="intent" type="android.content.Intent"> -</parameter> </method> -<method name="onEnabled" - return="void" +<method name="getPackageName" + return="java.lang.String" abstract="false" native="false" synchronized="false" @@ -20138,13 +20151,9 @@ deprecated="not deprecated" visibility="public" > -<parameter name="context" type="android.content.Context"> -</parameter> -<parameter name="intent" type="android.content.Intent"> -</parameter> </method> -<method name="onPasswordChanged" - return="void" +<method name="getReceiverName" + return="java.lang.String" abstract="false" native="false" synchronized="false" @@ -20153,13 +20162,9 @@ deprecated="not deprecated" visibility="public" > -<parameter name="context" type="android.content.Context"> -</parameter> -<parameter name="intent" type="android.content.Intent"> -</parameter> </method> -<method name="onPasswordFailed" - return="void" +<method name="getTagForPolicy" + return="java.lang.String" abstract="false" native="false" synchronized="false" @@ -20168,13 +20173,11 @@ deprecated="not deprecated" visibility="public" > -<parameter name="context" type="android.content.Context"> -</parameter> -<parameter name="intent" type="android.content.Intent"> +<parameter name="policyIdent" type="int"> </parameter> </method> -<method name="onPasswordSucceeded" - return="void" +<method name="loadDescription" + return="java.lang.CharSequence" abstract="false" native="false" synchronized="false" @@ -20183,13 +20186,13 @@ deprecated="not deprecated" visibility="public" > -<parameter name="context" type="android.content.Context"> -</parameter> -<parameter name="intent" type="android.content.Intent"> +<parameter name="pm" type="android.content.pm.PackageManager"> </parameter> +<exception name="Resources.NotFoundException" type="android.content.res.Resources.NotFoundException"> +</exception> </method> -<method name="onReceive" - return="void" +<method name="loadIcon" + return="android.graphics.drawable.Drawable" abstract="false" native="false" synchronized="false" @@ -20198,93 +20201,109 @@ deprecated="not deprecated" visibility="public" > -<parameter name="context" type="android.content.Context"> +<parameter name="pm" type="android.content.pm.PackageManager"> </parameter> -<parameter name="intent" type="android.content.Intent"> +</method> +<method name="loadLabel" + return="java.lang.CharSequence" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="pm" type="android.content.pm.PackageManager"> </parameter> </method> -<field name="ACTION_DEVICE_ADMIN_DISABLED" - type="java.lang.String" - transient="false" - volatile="false" - value=""android.app.action.DEVICE_ADMIN_DISABLED"" - static="true" - final="true" +<method name="usesPolicy" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" deprecated="not deprecated" visibility="public" > -</field> -<field name="ACTION_DEVICE_ADMIN_DISABLE_REQUESTED" - type="java.lang.String" - transient="false" - volatile="false" - value=""android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED"" - static="true" - final="true" +<parameter name="policyIdent" type="int"> +</parameter> +</method> +<method name="writeToParcel" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" deprecated="not deprecated" visibility="public" > -</field> -<field name="ACTION_DEVICE_ADMIN_ENABLED" - type="java.lang.String" +<parameter name="dest" type="android.os.Parcel"> +</parameter> +<parameter name="flags" type="int"> +</parameter> +</method> +<field name="CREATOR" + type="android.os.Parcelable.Creator" transient="false" volatile="false" - value=""android.app.action.DEVICE_ADMIN_ENABLED"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="ACTION_PASSWORD_CHANGED" - type="java.lang.String" +<field name="USES_POLICY_FORCE_LOCK" + type="int" transient="false" volatile="false" - value=""android.app.action.ACTION_PASSWORD_CHANGED"" + value="3" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="ACTION_PASSWORD_FAILED" - type="java.lang.String" +<field name="USES_POLICY_LIMIT_PASSWORD" + type="int" transient="false" volatile="false" - value=""android.app.action.ACTION_PASSWORD_FAILED"" + value="0" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="ACTION_PASSWORD_SUCCEEDED" - type="java.lang.String" +<field name="USES_POLICY_RESET_PASSWORD" + type="int" transient="false" volatile="false" - value=""android.app.action.ACTION_PASSWORD_SUCCEEDED"" + value="2" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="DEVICE_ADMIN_META_DATA" - type="java.lang.String" +<field name="USES_POLICY_WATCH_LOGIN" + type="int" transient="false" volatile="false" - value=""android.app.device_admin"" + value="1" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="EXTRA_DISABLE_WARNING" - type="java.lang.String" +<field name="USES_POLICY_WIPE_DATA" + type="int" transient="false" volatile="false" - value=""android.app.extra.DISABLE_WARNING"" + value="4" static="true" final="true" deprecated="not deprecated" @@ -20292,45 +20311,24 @@ > </field> </class> -<class name="DeviceAdminInfo" - extends="java.lang.Object" +<class name="DeviceAdminReceiver" + extends="android.content.BroadcastReceiver" abstract="false" static="false" - final="true" - deprecated="not deprecated" - visibility="public" -> -<implements name="android.os.Parcelable"> -</implements> -<constructor name="DeviceAdminInfo" - type="android.app.DeviceAdminInfo" - static="false" final="false" deprecated="not deprecated" visibility="public" > -<parameter name="context" type="android.content.Context"> -</parameter> -<parameter name="receiver" type="android.content.pm.ResolveInfo"> -</parameter> -<exception name="IOException" type="java.io.IOException"> -</exception> -<exception name="XmlPullParserException" type="org.xmlpull.v1.XmlPullParserException"> -</exception> -</constructor> -<method name="describeContents" - return="int" - abstract="false" - native="false" - synchronized="false" +<constructor name="DeviceAdminReceiver" + type="android.app.DeviceAdminReceiver" static="false" final="false" deprecated="not deprecated" visibility="public" > -</method> -<method name="dump" - return="void" +</constructor> +<method name="getManager" + return="android.app.DevicePolicyManager" abstract="false" native="false" synchronized="false" @@ -20339,13 +20337,11 @@ deprecated="not deprecated" visibility="public" > -<parameter name="pw" type="android.util.Printer"> -</parameter> -<parameter name="prefix" type="java.lang.String"> +<parameter name="context" type="android.content.Context"> </parameter> </method> -<method name="getActivityInfo" - return="android.content.pm.ActivityInfo" +<method name="getWho" + return="android.content.ComponentName" abstract="false" native="false" synchronized="false" @@ -20354,9 +20350,11 @@ deprecated="not deprecated" visibility="public" > +<parameter name="context" type="android.content.Context"> +</parameter> </method> -<method name="getComponent" - return="android.content.ComponentName" +<method name="onDisableRequested" + return="java.lang.CharSequence" abstract="false" native="false" synchronized="false" @@ -20365,9 +20363,13 @@ deprecated="not deprecated" visibility="public" > +<parameter name="context" type="android.content.Context"> +</parameter> +<parameter name="intent" type="android.content.Intent"> +</parameter> </method> -<method name="getPackageName" - return="java.lang.String" +<method name="onDisabled" + return="void" abstract="false" native="false" synchronized="false" @@ -20376,9 +20378,13 @@ deprecated="not deprecated" visibility="public" > +<parameter name="context" type="android.content.Context"> +</parameter> +<parameter name="intent" type="android.content.Intent"> +</parameter> </method> -<method name="getReceiverName" - return="java.lang.String" +<method name="onEnabled" + return="void" abstract="false" native="false" synchronized="false" @@ -20387,9 +20393,13 @@ deprecated="not deprecated" visibility="public" > +<parameter name="context" type="android.content.Context"> +</parameter> +<parameter name="intent" type="android.content.Intent"> +</parameter> </method> -<method name="getTagForPolicy" - return="java.lang.String" +<method name="onPasswordChanged" + return="void" abstract="false" native="false" synchronized="false" @@ -20398,11 +20408,13 @@ deprecated="not deprecated" visibility="public" > -<parameter name="policyIdent" type="int"> +<parameter name="context" type="android.content.Context"> +</parameter> +<parameter name="intent" type="android.content.Intent"> </parameter> </method> -<method name="loadDescription" - return="java.lang.CharSequence" +<method name="onPasswordFailed" + return="void" abstract="false" native="false" synchronized="false" @@ -20411,13 +20423,13 @@ deprecated="not deprecated" visibility="public" > -<parameter name="pm" type="android.content.pm.PackageManager"> +<parameter name="context" type="android.content.Context"> +</parameter> +<parameter name="intent" type="android.content.Intent"> </parameter> -<exception name="Resources.NotFoundException" type="android.content.res.Resources.NotFoundException"> -</exception> </method> -<method name="loadIcon" - return="android.graphics.drawable.Drawable" +<method name="onPasswordSucceeded" + return="void" abstract="false" native="false" synchronized="false" @@ -20426,11 +20438,13 @@ deprecated="not deprecated" visibility="public" > -<parameter name="pm" type="android.content.pm.PackageManager"> +<parameter name="context" type="android.content.Context"> +</parameter> +<parameter name="intent" type="android.content.Intent"> </parameter> </method> -<method name="loadLabel" - return="java.lang.CharSequence" +<method name="onReceive" + return="void" abstract="false" native="false" synchronized="false" @@ -20439,96 +20453,93 @@ deprecated="not deprecated" visibility="public" > -<parameter name="pm" type="android.content.pm.PackageManager"> +<parameter name="context" type="android.content.Context"> +</parameter> +<parameter name="intent" type="android.content.Intent"> </parameter> </method> -<method name="usesPolicy" - return="boolean" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" +<field name="ACTION_DEVICE_ADMIN_DISABLED" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.app.action.DEVICE_ADMIN_DISABLED"" + static="true" + final="true" deprecated="not deprecated" visibility="public" > -<parameter name="policyIdent" type="int"> -</parameter> -</method> -<method name="writeToParcel" - return="void" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" +</field> +<field name="ACTION_DEVICE_ADMIN_DISABLE_REQUESTED" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED"" + static="true" + final="true" deprecated="not deprecated" visibility="public" > -<parameter name="dest" type="android.os.Parcel"> -</parameter> -<parameter name="flags" type="int"> -</parameter> -</method> -<field name="CREATOR" - type="android.os.Parcelable.Creator" +</field> +<field name="ACTION_DEVICE_ADMIN_ENABLED" + type="java.lang.String" transient="false" volatile="false" + value=""android.app.action.DEVICE_ADMIN_ENABLED"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="USES_POLICY_FORCE_LOCK" - type="int" +<field name="ACTION_PASSWORD_CHANGED" + type="java.lang.String" transient="false" volatile="false" - value="3" + value=""android.app.action.ACTION_PASSWORD_CHANGED"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="USES_POLICY_LIMIT_PASSWORD" - type="int" +<field name="ACTION_PASSWORD_FAILED" + type="java.lang.String" transient="false" volatile="false" - value="0" + value=""android.app.action.ACTION_PASSWORD_FAILED"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="USES_POLICY_RESET_PASSWORD" - type="int" +<field name="ACTION_PASSWORD_SUCCEEDED" + type="java.lang.String" transient="false" volatile="false" - value="2" + value=""android.app.action.ACTION_PASSWORD_SUCCEEDED"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="USES_POLICY_WATCH_LOGIN" - type="int" +<field name="DEVICE_ADMIN_META_DATA" + type="java.lang.String" transient="false" volatile="false" - value="1" + value=""android.app.device_admin"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="USES_POLICY_WIPE_DATA" - type="int" +<field name="EXTRA_DISABLE_WARNING" + type="java.lang.String" transient="false" volatile="false" - value="4" + value=""android.app.extra.DISABLE_WARNING"" static="true" final="true" deprecated="not deprecated" @@ -150327,9 +150338,9 @@ </parameter> <parameter name="align" type="android.text.Layout.Alignment"> </parameter> -<parameter name="spacingmult" type="float"> +<parameter name="spacingMult" type="float"> </parameter> -<parameter name="spacingadd" type="float"> +<parameter name="spacingAdd" type="float"> </parameter> </constructor> <method name="draw" @@ -150359,7 +150370,7 @@ </parameter> <parameter name="highlight" type="android.graphics.Path"> </parameter> -<parameter name="highlightpaint" type="android.graphics.Paint"> +<parameter name="highlightPaint" type="android.graphics.Paint"> </parameter> <parameter name="cursorOffsetVertical" type="int"> </parameter> diff --git a/core/java/android/app/DeviceAdminInfo.java b/core/java/android/app/DeviceAdminInfo.java index 159fa75e9e75..bedf4b4eb916 100644 --- a/core/java/android/app/DeviceAdminInfo.java +++ b/core/java/android/app/DeviceAdminInfo.java @@ -60,8 +60,8 @@ public final class DeviceAdminInfo implements Parcelable { /** * A type of policy that this device admin can use: able to watch login - * attempts from the user, via {@link DeviceAdmin#ACTION_PASSWORD_FAILED}, - * {@link DeviceAdmin#ACTION_PASSWORD_SUCCEEDED}, and + * attempts from the user, via {@link DeviceAdminReceiver#ACTION_PASSWORD_FAILED}, + * {@link DeviceAdminReceiver#ACTION_PASSWORD_SUCCEEDED}, and * {@link DevicePolicyManager#getCurrentFailedPasswordAttempts}. * * <p>To control this policy, the device admin must have a "watch-login" @@ -169,10 +169,10 @@ public final class DeviceAdminInfo implements Parcelable { XmlResourceParser parser = null; try { - parser = ai.loadXmlMetaData(pm, DeviceAdmin.DEVICE_ADMIN_META_DATA); + parser = ai.loadXmlMetaData(pm, DeviceAdminReceiver.DEVICE_ADMIN_META_DATA); if (parser == null) { throw new XmlPullParserException("No " - + DeviceAdmin.DEVICE_ADMIN_META_DATA + " meta-data"); + + DeviceAdminReceiver.DEVICE_ADMIN_META_DATA + " meta-data"); } AttributeSet attrs = Xml.asAttributeSet(parser); diff --git a/core/java/android/app/DeviceAdmin.java b/core/java/android/app/DeviceAdminReceiver.java index af9c379deb19..453e0bf74c21 100644 --- a/core/java/android/app/DeviceAdmin.java +++ b/core/java/android/app/DeviceAdminReceiver.java @@ -29,6 +29,13 @@ import android.os.Bundle; * class provides a convenience for interpreting the raw intent actions * that are sent by the system. * + * <p>The callback methods, like the base + * {@link BroadcastReceiver#onReceive(Context, Intent) BroadcastReceiver.onReceive()} + * method, happen on the main thread of the process. Thus long running + * operations must be done on another thread. Note that because a receiver + * is done once returning from its receive function, such long-running operations + * should probably be done in a {@link Service}. + * * <p>When publishing your DeviceAdmin subclass as a receiver, it must * handle {@link #ACTION_DEVICE_ADMIN_ENABLED} and require the * {@link android.Manifest.permission#BIND_DEVICE_ADMIN} permission. A typical @@ -42,7 +49,7 @@ import android.os.Bundle; * * {@sample development/samples/ApiDemos/res/xml/device_admin_sample.xml meta_data} */ -public class DeviceAdmin extends BroadcastReceiver { +public class DeviceAdminReceiver extends BroadcastReceiver { private static String TAG = "DevicePolicy"; private static boolean DEBUG = false; private static boolean localLOGV = DEBUG || android.util.Config.LOGV; @@ -51,7 +58,7 @@ public class DeviceAdmin extends BroadcastReceiver { * This is the primary action that a device administrator must implement to be * allowed to manage a device. This will be set to the receiver * when the user enables it for administration. You will generally - * handle this in {@link DeviceAdmin#onEnabled(Context, Intent)}. To be + * handle this in {@link DeviceAdminReceiver#onEnabled(Context, Intent)}. To be * supported, the receiver must also require the * {@link android.Manifest.permission#BIND_DEVICE_ADMIN} permission so * that other applications can not abuse it. @@ -85,7 +92,7 @@ public class DeviceAdmin extends BroadcastReceiver { * Action sent to a device administrator when the user has disabled * it. Upon return, the application no longer has access to the * protected device policy manager APIs. You will generally - * handle this in {@link DeviceAdmin#onDisabled(Context, Intent)}. Note + * handle this in {@link DeviceAdminReceiver#onDisabled(Context, Intent)}. Note * that this action will be * sent the receiver regardless of whether it is explicitly listed in * its intent filter. @@ -100,7 +107,7 @@ public class DeviceAdmin extends BroadcastReceiver { * of the new password with {@link DevicePolicyManager#isActivePasswordSufficient() * DevicePolicyManager.isActivePasswordSufficient()}. * You will generally - * handle this in {@link DeviceAdmin#onPasswordChanged}. + * handle this in {@link DeviceAdminReceiver#onPasswordChanged}. * * <p>The calling device admin must have requested * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to receive @@ -116,7 +123,7 @@ public class DeviceAdmin extends BroadcastReceiver { * number of failed password attempts there have been with * {@link DevicePolicyManager#getCurrentFailedPasswordAttempts * DevicePolicyManager.getCurrentFailedPasswordAttempts()}. You will generally - * handle this in {@link DeviceAdmin#onPasswordFailed}. + * handle this in {@link DeviceAdminReceiver#onPasswordFailed}. * * <p>The calling device admin must have requested * {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} to receive diff --git a/core/java/android/app/DevicePolicyManager.java b/core/java/android/app/DevicePolicyManager.java index 82c43dc18ae9..d611807c74dc 100644 --- a/core/java/android/app/DevicePolicyManager.java +++ b/core/java/android/app/DevicePolicyManager.java @@ -36,7 +36,7 @@ import java.util.List; /** * Public interface for managing policies enforced on a device. Most clients - * of this class must have published a {@link DeviceAdmin} that the user + * of this class must have published a {@link DeviceAdminReceiver} that the user * has currently enabled. */ public class DevicePolicyManager { @@ -195,7 +195,7 @@ public class DevicePolicyManager { * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call * this method; if it has not, a security exception will be thrown. * - * @param admin Which {@link DeviceAdmin} this request is associated with. + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param quality The new desired quality. One of * {@link #PASSWORD_QUALITY_UNSPECIFIED}, {@link #PASSWORD_QUALITY_SOMETHING}, * {@link #PASSWORD_QUALITY_NUMERIC}, or {@link #PASSWORD_QUALITY_ALPHANUMERIC}. @@ -243,7 +243,7 @@ public class DevicePolicyManager { * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call * this method; if it has not, a security exception will be thrown. * - * @param admin Which {@link DeviceAdmin} this request is associated with. + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param length The new desired minimum password length. A value of 0 * means there is no restriction. */ @@ -338,11 +338,11 @@ public class DevicePolicyManager { * <p>To implement any other policy (e.g. wiping data for a particular * application only, erasing or revoking credentials, or reporting the * failure to a server), you should implement - * {@link DeviceAdmin#onPasswordFailed(Context, android.content.Intent)} + * {@link DeviceAdminReceiver#onPasswordFailed(Context, android.content.Intent)} * instead. Do not use this API, because if the maximum count is reached, * the device will be wiped immediately, and your callback will not be invoked. * - * @param admin Which {@link DeviceAdmin} this request is associated with. + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param num The number of failed password attempts at which point the * device will wipe its data. */ @@ -375,7 +375,9 @@ public class DevicePolicyManager { } /** - * Force a new password on the user. This takes effect immediately. + * Force a new device unlock password (the password needed to access the + * entire device, not for individual accounts) on the user. This takes + * effect immediately. * The given password must be sufficient for the * current password quality and length constraints as returned by * {@link #getPasswordQuality(ComponentName)} and @@ -413,7 +415,7 @@ public class DevicePolicyManager { * {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK} to be able to call * this method; if it has not, a security exception will be thrown. * - * @param admin Which {@link DeviceAdmin} this request is associated with. + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param timeMs The new desired maximum time to lock in milliseconds. * A value of 0 means there is no restriction. */ diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index ed38240b92b3..4598bb562ff6 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -822,11 +822,6 @@ public class Dialog implements DialogInterface, Window.Callback, final SearchManager searchManager = (SearchManager) mContext .getSystemService(Context.SEARCH_SERVICE); - // can't start search without an associated activity (e.g a system dialog) - if (!searchManager.hasIdent()) { - return false; - } - // associate search with owner activity if possible (otherwise it will default to // global search). final ComponentName appName = getAssociatedActivity(); diff --git a/core/java/android/app/FullBackupAgent.java b/core/java/android/app/FullBackupAgent.java index d89db9667473..db198ad17bc5 100644 --- a/core/java/android/app/FullBackupAgent.java +++ b/core/java/android/app/FullBackupAgent.java @@ -48,7 +48,8 @@ public class FullBackupAgent extends BackupAgent { } // That's the file set; now back it all up - FileBackupHelper helper = new FileBackupHelper(this, (String[])allFiles.toArray()); + FileBackupHelper helper = new FileBackupHelper(this, + allFiles.toArray(new String[allFiles.size()])); helper.performBackup(oldState, data, newState); } diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index e4c1ba6e851f..581b436fb407 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -16,6 +16,9 @@ package android.app; +import com.android.common.Patterns; +import com.android.common.speech.Recognition; + import static android.app.SuggestionsAdapter.getColumnString; import android.content.ActivityNotFoundException; @@ -67,9 +70,6 @@ import android.widget.TextView; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemSelectedListener; -import com.android.common.Patterns; -import com.android.common.speech.Recognition; - import java.util.ArrayList; import java.util.WeakHashMap; import java.util.concurrent.atomic.AtomicLong; @@ -89,10 +89,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS private static final String INSTANCE_KEY_COMPONENT = "comp"; private static final String INSTANCE_KEY_APPDATA = "data"; - private static final String INSTANCE_KEY_GLOBALSEARCH = "glob"; - private static final String INSTANCE_KEY_STORED_COMPONENT = "sComp"; private static final String INSTANCE_KEY_STORED_APPDATA = "sData"; - private static final String INSTANCE_KEY_PREVIOUS_COMPONENTS = "sPrev"; private static final String INSTANCE_KEY_USER_QUERY = "uQry"; // The string used for privateImeOptions to identify to the IME that it should not show @@ -115,19 +112,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS private SearchableInfo mSearchable; private ComponentName mLaunchComponent; private Bundle mAppSearchData; - private boolean mGlobalSearchMode; private Context mActivityContext; private SearchManager mSearchManager; - - // Values we store to allow user to toggle between in-app search and global search. - private ComponentName mStoredComponentName; - private Bundle mStoredAppSearchData; - - // stack of previous searchables, to support the BACK key after - // SearchManager.INTENT_ACTION_CHANGE_SEARCH_SOURCE. - // The top of the stack (= previous searchable) is the last element of the list, - // since adding and removing is efficient at the end of an ArrayList. - private ArrayList<ComponentName> mPreviousComponents; // For voice searching private final Intent mVoiceWebSearchIntent; @@ -158,7 +144,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS * @param context Application Context we can use for system acess */ public SearchDialog(Context context, SearchManager searchManager) { - super(context, com.android.internal.R.style.Theme_GlobalSearchBar); + super(context, com.android.internal.R.style.Theme_SearchBar); // Save voice intent for later queries/launching mVoiceWebSearchIntent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH); @@ -241,14 +227,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS * @return true if search dialog launched, false if not */ public boolean show(String initialQuery, boolean selectInitialQuery, - ComponentName componentName, Bundle appSearchData, boolean globalSearch) { - - // Reset any stored values from last time dialog was shown. - mStoredComponentName = null; - mStoredAppSearchData = null; - - boolean success = doShow(initialQuery, selectInitialQuery, componentName, appSearchData, - globalSearch); + ComponentName componentName, Bundle appSearchData) { + boolean success = doShow(initialQuery, selectInitialQuery, componentName, appSearchData); if (success) { // Display the drop down as soon as possible instead of waiting for the rest of the // pending UI stuff to get done, so that things appear faster to the user. @@ -257,67 +237,16 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS return success; } - private boolean isInRealAppSearch() { - return !mGlobalSearchMode - && (mPreviousComponents == null || mPreviousComponents.isEmpty()); - } - /** - * Called in response to a press of the hard search button in - * {@link #onKeyDown(int, KeyEvent)}, this method toggles between in-app - * search and global search when relevant. - * - * If pressed within an in-app search context, this switches the search dialog out to - * global search. If pressed within a global search context that was originally an in-app - * search context, this switches back to the in-app search context. If pressed within a - * global search context that has no original in-app search context (e.g., global search - * from Home), this does nothing. - * - * @return false if we wanted to toggle context but could not do so successfully, true - * in all other cases - */ - private boolean toggleGlobalSearch() { - String currentSearchText = mSearchAutoComplete.getText().toString(); - if (!mGlobalSearchMode) { - mStoredComponentName = mLaunchComponent; - mStoredAppSearchData = mAppSearchData; - - // If this is the browser, we have a special case to not show the icon to the left - // of the text field, for extra space for url entry (this should be reconciled in - // Eclair). So special case a second tap of the search button to remove any - // already-entered text so that we can be sure to show the "Quick Search Box" hint - // text to still make it clear to the user that we've jumped out to global search. - // - // TODO: When the browser icon issue is reconciled in Eclair, remove this special case. - if (isBrowserSearch()) currentSearchText = ""; - - cancel(); - mSearchManager.startGlobalSearch(currentSearchText, false, mStoredAppSearchData); - return true; - } else { - if (mStoredComponentName != null) { - // This means we should toggle *back* to an in-app search context from - // global search. - return doShow(currentSearchText, false, mStoredComponentName, - mStoredAppSearchData, false); - } else { - return true; - } - } - } - - /** - * Does the rest of the work required to show the search dialog. Called by both - * {@link #show(String, boolean, ComponentName, Bundle, boolean)} and - * {@link #toggleGlobalSearch()}. - * + * Does the rest of the work required to show the search dialog. Called by + * {@link #show(String, boolean, ComponentName, Bundle)} and + * * @return true if search dialog showed, false if not */ private boolean doShow(String initialQuery, boolean selectInitialQuery, - ComponentName componentName, Bundle appSearchData, - boolean globalSearch) { + ComponentName componentName, Bundle appSearchData) { // set up the searchable and show the dialog - if (!show(componentName, appSearchData, globalSearch)) { + if (!show(componentName, appSearchData)) { return false; } @@ -335,38 +264,24 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS * * @return <code>true</code> if search dialog launched */ - private boolean show(ComponentName componentName, Bundle appSearchData, - boolean globalSearch) { + private boolean show(ComponentName componentName, Bundle appSearchData) { if (DBG) { Log.d(LOG_TAG, "show(" + componentName + ", " - + appSearchData + ", " + globalSearch + ")"); + + appSearchData + ")"); } SearchManager searchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); - // Try to get the searchable info for the provided component (or for global search, - // if globalSearch == true). - mSearchable = searchManager.getSearchableInfo(componentName, globalSearch); - - // If we got back nothing, and it wasn't a request for global search, then try again - // for global search, as we'll try to launch that in lieu of any component-specific search. - if (!globalSearch && mSearchable == null) { - globalSearch = true; - mSearchable = searchManager.getSearchableInfo(componentName, globalSearch); - } + // Try to get the searchable info for the provided component. + mSearchable = searchManager.getSearchableInfo(componentName, false); - // If there's not even a searchable info available for global search, then really give up. if (mSearchable == null) { - Log.w(LOG_TAG, "No global search provider."); return false; } mLaunchComponent = componentName; mAppSearchData = appSearchData; - // Using globalSearch here is just an optimization, just calling - // isDefaultSearchable() should always give the same result. - mGlobalSearchMode = globalSearch || searchManager.isDefaultSearchable(mSearchable); mActivityContext = mSearchable.getActivityContext(getContext()); // show the dialog. this will call onStart(). @@ -375,20 +290,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // of any bad state in the AutoCompleteTextView etc createContentView(); - // The Dialog uses a ContextThemeWrapper for the context; use this to change the - // theme out from underneath us, between the global search theme and the in-app - // search theme. They are identical except that the global search theme does not - // dim the background of the window (because global search is full screen so it's - // not needed and this should save a little bit of time on global search invocation). - Object context = getContext(); - if (context instanceof ContextThemeWrapper) { - ContextThemeWrapper wrapper = (ContextThemeWrapper) context; - if (globalSearch) { - wrapper.setTheme(com.android.internal.R.style.Theme_GlobalSearchBar); - } else { - wrapper.setTheme(com.android.internal.R.style.Theme_SearchBar); - } - } show(); } updateUI(); @@ -414,7 +315,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mSearchable = null; mActivityContext = null; mUserQuery = null; - mPreviousComponents = null; } /** @@ -428,7 +328,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mWorkingSpinner.setVisible(working, false); mWorkingSpinner.invalidateSelf(); } - + /** * Closes and gets rid of the suggestions adapter. */ @@ -442,7 +342,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } mSuggestionsAdapter = null; } - + /** * Save the minimal set of data necessary to recreate the search * @@ -458,10 +358,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // setup info so I can recreate this particular search bundle.putParcelable(INSTANCE_KEY_COMPONENT, mLaunchComponent); bundle.putBundle(INSTANCE_KEY_APPDATA, mAppSearchData); - bundle.putBoolean(INSTANCE_KEY_GLOBALSEARCH, mGlobalSearchMode); - bundle.putParcelable(INSTANCE_KEY_STORED_COMPONENT, mStoredComponentName); - bundle.putBundle(INSTANCE_KEY_STORED_APPDATA, mStoredAppSearchData); - bundle.putParcelableArrayList(INSTANCE_KEY_PREVIOUS_COMPONENTS, mPreviousComponents); bundle.putString(INSTANCE_KEY_USER_QUERY, mUserQuery); return bundle; @@ -481,22 +377,10 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS ComponentName launchComponent = savedInstanceState.getParcelable(INSTANCE_KEY_COMPONENT); Bundle appSearchData = savedInstanceState.getBundle(INSTANCE_KEY_APPDATA); - boolean globalSearch = savedInstanceState.getBoolean(INSTANCE_KEY_GLOBALSEARCH); - ComponentName storedComponentName = - savedInstanceState.getParcelable(INSTANCE_KEY_STORED_COMPONENT); - Bundle storedAppSearchData = - savedInstanceState.getBundle(INSTANCE_KEY_STORED_APPDATA); - ArrayList<ComponentName> previousComponents = - savedInstanceState.getParcelableArrayList(INSTANCE_KEY_PREVIOUS_COMPONENTS); String userQuery = savedInstanceState.getString(INSTANCE_KEY_USER_QUERY); - // Set stored state - mStoredComponentName = storedComponentName; - mStoredAppSearchData = storedAppSearchData; - mPreviousComponents = previousComponents; - // show the dialog. - if (!doShow(userQuery, false, launchComponent, appSearchData, globalSearch)) { + if (!doShow(userQuery, false, launchComponent, appSearchData)) { // for some reason, we couldn't re-instantiate return; } @@ -506,7 +390,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS * Called after resources have changed, e.g. after screen rotation or locale change. */ public void onConfigurationChanged() { - if (isShowing()) { + if (mSearchable != null && isShowing()) { // Redraw (resources may have changed) updateSearchButton(); updateSearchAppIcon(); @@ -571,19 +455,13 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // we dismiss the entire dialog instead mSearchAutoComplete.setDropDownDismissedOnCompletion(false); - if (!isInRealAppSearch()) { - mSearchAutoComplete.setDropDownAlwaysVisible(true); // fill space until results come in - } else { - mSearchAutoComplete.setDropDownAlwaysVisible(false); - } - mSearchAutoComplete.setForceIgnoreOutsideTouch(true); // attach the suggestions adapter, if suggestions are available // The existence of a suggestions authority is the proxy for "suggestions available here" if (mSearchable.getSuggestAuthority() != null) { mSuggestionsAdapter = new SuggestionsAdapter(getContext(), this, mSearchable, - mOutsideDrawablesCache, mGlobalSearchMode); + mOutsideDrawablesCache); mSearchAutoComplete.setAdapter(mSuggestionsAdapter); } } @@ -611,7 +489,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // global search, for extra space for url entry. // // TODO: Remove this special case once the issue has been reconciled in Eclair. - if (mGlobalSearchMode || isBrowserSearch()) { + if (isBrowserSearch()) { mAppIcon.setImageResource(0); mAppIcon.setVisibility(View.GONE); mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_GLOBAL, @@ -705,15 +583,13 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS /** * Hack to determine whether this is the browser, so we can remove the browser icon - * to the left of the search field, as a special requirement for Donut. - * - * TODO: For Eclair, reconcile this with the rest of the global search UI. + * to the left of the search field. */ private boolean isBrowserSearch() { return mLaunchComponent.flattenToShortString().startsWith("com.android.browser/"); } - /** + /* * Listeners of various types */ @@ -769,7 +645,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS return super.onKeyDown(keyCode, event); } - + /** * Callback to watch the textedit field for empty/non-empty */ @@ -799,12 +675,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS if (mSearchable.autoUrlDetect() && !mSearchAutoComplete.isPerformingCompletion()) { // The user changed the query, check if it is a URL and if so change the search // button in the soft keyboard to the 'Go' button. - int options = (mSearchAutoComplete.getImeOptions() & (~EditorInfo.IME_MASK_ACTION)); - if (Patterns.WEB_URL.matcher(mUserQuery).matches()) { - options = options | EditorInfo.IME_ACTION_GO; - } else { - options = options | EditorInfo.IME_ACTION_SEARCH; - } + int options = (mSearchAutoComplete.getImeOptions() & (~EditorInfo.IME_MASK_ACTION)) + | EditorInfo.IME_ACTION_GO; if (options != mSearchAutoCompleteImeOptions) { mSearchAutoCompleteImeOptions = options; mSearchAutoComplete.setImeOptions(options); @@ -881,7 +753,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS if (searchable.getVoiceSearchLaunchWebSearch()) { getContext().startActivity(mVoiceWebSearchIntent); } else if (searchable.getVoiceSearchLaunchRecognizer()) { - Intent appSearchIntent = createVoiceAppSearchIntent(mVoiceAppSearchIntent); + Intent appSearchIntent = createVoiceAppSearchIntent(mVoiceAppSearchIntent, + searchable); getContext().startActivity(appSearchIntent); } } catch (ActivityNotFoundException e) { @@ -899,8 +772,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS * @param baseIntent The voice app search intent to start from * @return A completely-configured intent ready to send to the voice search activity */ - private Intent createVoiceAppSearchIntent(Intent baseIntent) { - ComponentName searchActivity = mSearchable.getSearchActivity(); + private Intent createVoiceAppSearchIntent(Intent baseIntent, SearchableInfo searchable) { + ComponentName searchActivity = searchable.getSearchActivity(); // create the necessary intent to set up a search-and-forward operation // in the voice search system. We have to keep the bundle separate, @@ -930,17 +803,17 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS String language = null; int maxResults = 1; Resources resources = mActivityContext.getResources(); - if (mSearchable.getVoiceLanguageModeId() != 0) { - languageModel = resources.getString(mSearchable.getVoiceLanguageModeId()); + if (searchable.getVoiceLanguageModeId() != 0) { + languageModel = resources.getString(searchable.getVoiceLanguageModeId()); } - if (mSearchable.getVoicePromptTextId() != 0) { - prompt = resources.getString(mSearchable.getVoicePromptTextId()); + if (searchable.getVoicePromptTextId() != 0) { + prompt = resources.getString(searchable.getVoicePromptTextId()); } - if (mSearchable.getVoiceLanguageId() != 0) { - language = resources.getString(mSearchable.getVoiceLanguageId()); + if (searchable.getVoiceLanguageId() != 0) { + language = resources.getString(searchable.getVoiceLanguageId()); } - if (mSearchable.getVoiceMaxResults() != 0) { - maxResults = mSearchable.getVoiceMaxResults(); + if (searchable.getVoiceMaxResults() != 0) { + maxResults = searchable.getVoiceMaxResults(); } voiceIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, languageModel); voiceIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt); @@ -1140,14 +1013,9 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS */ protected void launchQuerySearch(int actionKey, String actionMsg) { String query = mSearchAutoComplete.getText().toString(); - String action = mGlobalSearchMode ? Intent.ACTION_WEB_SEARCH : Intent.ACTION_SEARCH; + String action = Intent.ACTION_SEARCH; Intent intent = createIntent(action, null, null, query, null, - actionKey, actionMsg, null); - // Allow GlobalSearch to log and create shortcut for searches launched by - // the search button, enter key or an action key. - if (mGlobalSearchMode) { - mSuggestionsAdapter.reportSearch(query); - } + actionKey, actionMsg); launchIntent(intent); } @@ -1160,7 +1028,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS protected boolean launchSuggestion(int position) { return launchSuggestion(position, KeyEvent.KEYCODE_UNKNOWN, null); } - + /** * Launches an intent based on a suggestion. * @@ -1177,20 +1045,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS Intent intent = createIntentFromSuggestion(c, actionKey, actionMsg); - // report back about the click - if (mGlobalSearchMode) { - // in global search mode, do it via cursor - mSuggestionsAdapter.callCursorOnClick(c, position, actionKey, actionMsg); - } else if (intent != null - && mPreviousComponents != null - && !mPreviousComponents.isEmpty()) { - // in-app search (and we have pivoted in as told by mPreviousComponents, - // which is used for keeping track of what we pop back to when we are pivoting into - // in app search.) - reportInAppClickToGlobalSearch(c, intent); - } - - // launch the intent + // launch the intent launchIntent(intent); return true; @@ -1199,163 +1054,28 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } /** - * Report a click from an in app search result back to global search for shortcutting porpoises. - * - * @param c The cursor that is pointing to the clicked position. - * @param intent The intent that will be launched for the click. - */ - private void reportInAppClickToGlobalSearch(Cursor c, Intent intent) { - // for in app search, still tell global search via content provider - Uri uri = getClickReportingUri(); - final ContentValues cv = new ContentValues(); - cv.put(SearchManager.SEARCH_CLICK_REPORT_COLUMN_QUERY, mUserQuery); - final ComponentName source = mSearchable.getSearchActivity(); - cv.put(SearchManager.SEARCH_CLICK_REPORT_COLUMN_COMPONENT, source.flattenToShortString()); - - // grab the intent columns from the intent we created since it has additional - // logic for falling back on the searchable default - cv.put(SearchManager.SUGGEST_COLUMN_INTENT_ACTION, intent.getAction()); - cv.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA, intent.getDataString()); - cv.put(SearchManager.SUGGEST_COLUMN_INTENT_COMPONENT_NAME, - intent.getComponent().flattenToShortString()); - - // ensure the icons will work for global search - cv.put(SearchManager.SUGGEST_COLUMN_ICON_1, - wrapIconForPackage( - mSearchable.getSuggestPackage(), - getColumnString(c, SearchManager.SUGGEST_COLUMN_ICON_1))); - cv.put(SearchManager.SUGGEST_COLUMN_ICON_2, - wrapIconForPackage( - mSearchable.getSuggestPackage(), - getColumnString(c, SearchManager.SUGGEST_COLUMN_ICON_2))); - - // the rest can be passed through directly - cv.put(SearchManager.SUGGEST_COLUMN_FORMAT, - getColumnString(c, SearchManager.SUGGEST_COLUMN_FORMAT)); - cv.put(SearchManager.SUGGEST_COLUMN_TEXT_1, - getColumnString(c, SearchManager.SUGGEST_COLUMN_TEXT_1)); - cv.put(SearchManager.SUGGEST_COLUMN_TEXT_2, - getColumnString(c, SearchManager.SUGGEST_COLUMN_TEXT_2)); - cv.put(SearchManager.SUGGEST_COLUMN_QUERY, - getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY)); - cv.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID, - getColumnString(c, SearchManager.SUGGEST_COLUMN_SHORTCUT_ID)); - // note: deliberately omitting background color since it is only for global search - // "more results" entries - mContext.getContentResolver().insert(uri, cv); - } - - /** - * @return A URI appropriate for reporting a click. - */ - private Uri getClickReportingUri() { - Uri.Builder uriBuilder = new Uri.Builder() - .scheme(ContentResolver.SCHEME_CONTENT) - .authority(SearchManager.SEARCH_CLICK_REPORT_AUTHORITY); - - uriBuilder.appendPath(SearchManager.SEARCH_CLICK_REPORT_URI_PATH); - - return uriBuilder - .query("") // TODO: Remove, workaround for a bug in Uri.writeToParcel() - .fragment("") // TODO: Remove, workaround for a bug in Uri.writeToParcel() - .build(); - } - - /** - * Wraps an icon for a particular package. If the icon is a resource id, it is converted into - * an android.resource:// URI. - * - * @param packageName The source of the icon - * @param icon The icon retrieved from a suggestion column - * @return An icon string appropriate for the package. - */ - private String wrapIconForPackage(String packageName, String icon) { - if (icon == null || icon.length() == 0 || "0".equals(icon)) { - // SearchManager specifies that null or zero can be returned to indicate - // no icon. We also allow empty string. - return null; - } else if (!Character.isDigit(icon.charAt(0))){ - return icon; - } else { - return new Uri.Builder() - .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) - .authority(packageName) - .encodedPath(icon) - .toString(); - } - } - - /** * Launches an intent, including any special intent handling. */ private void launchIntent(Intent intent) { if (intent == null) { return; } - if (handleSpecialIntent(intent)){ - return; - } Log.d(LOG_TAG, "launching " + intent); try { - // in global search mode, we send the activity straight to the original suggestion - // source. this is because GlobalSearch may not have permission to launch the - // intent, and to avoid the extra step of going through GlobalSearch. - if (mGlobalSearchMode) { - launchGlobalSearchIntent(intent); - if (mStoredComponentName != null) { - // If we're embedded in an application, dismiss the dialog. - // This ensures that if the intent is handled by the current - // activity, it's not obscured by the dialog. - dismiss(); - } - } else { - // If the intent was created from a suggestion, it will always have an explicit - // component here. - Log.i(LOG_TAG, "Starting (as ourselves) " + intent.toURI()); - getContext().startActivity(intent); - // If the search switches to a different activity, - // SearchDialogWrapper#performActivityResuming - // will handle hiding the dialog when the next activity starts, but for - // real in-app search, we still need to dismiss the dialog. - if (isInRealAppSearch()) { - dismiss(); - } - } + // If the intent was created from a suggestion, it will always have an explicit + // component here. + Log.i(LOG_TAG, "Starting (as ourselves) " + intent.toURI()); + getContext().startActivity(intent); + // If the search switches to a different activity, + // SearchDialogWrapper#performActivityResuming + // will handle hiding the dialog when the next activity starts, but for + // real in-app search, we still need to dismiss the dialog. + dismiss(); } catch (RuntimeException ex) { Log.e(LOG_TAG, "Failed launch activity: " + intent, ex); } } - private void launchGlobalSearchIntent(Intent intent) { - final String packageName; - // GlobalSearch puts the original source of the suggestion in the - // 'component name' column. If set, we send the intent to that activity. - // We trust GlobalSearch to always set this to the suggestion source. - String intentComponent = intent.getStringExtra(SearchManager.COMPONENT_NAME_KEY); - if (intentComponent != null) { - ComponentName componentName = ComponentName.unflattenFromString(intentComponent); - intent.setComponent(componentName); - intent.removeExtra(SearchManager.COMPONENT_NAME_KEY); - // Launch the intent as the suggestion source. - // This prevents sources from using the search dialog to launch - // intents that they don't have permission for themselves. - packageName = componentName.getPackageName(); - } else { - // If there is no component in the suggestion, it must be a built-in suggestion - // from GlobalSearch (e.g. "Search the web for") or the intent - // launched when pressing the search/go button in the search dialog. - // Launch the intent with the permissions of GlobalSearch. - packageName = mSearchable.getSearchActivity().getPackageName(); - } - - // Launch all global search suggestions as new tasks, since they don't relate - // to the current task. - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - setBrowserApplicationId(intent); - - startActivityInPackage(intent, packageName); - } - /** * If the intent is to open an HTTP or HTTPS URL, we set * {@link Browser#EXTRA_APPLICATION_ID} so that any existing browser window that @@ -1372,106 +1092,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } /** - * Starts an activity as if it had been started by the given package. - * - * @param intent The description of the activity to start. - * @param packageName - * @throws ActivityNotFoundException If the intent could not be resolved to - * and existing activity. - * @throws SecurityException If the package does not have permission to start - * start the activity. - * @throws AndroidRuntimeException If some other error occurs. - */ - private void startActivityInPackage(Intent intent, String packageName) { - try { - int uid = ActivityThread.getPackageManager().getPackageUid(packageName); - if (uid < 0) { - throw new AndroidRuntimeException("Package UID not found " + packageName); - } - String resolvedType = intent.resolveTypeIfNeeded(getContext().getContentResolver()); - IBinder resultTo = null; - String resultWho = null; - int requestCode = -1; - boolean onlyIfNeeded = false; - Log.i(LOG_TAG, "Starting (uid " + uid + ", " + packageName + ") " + intent.toURI()); - int result = ActivityManagerNative.getDefault().startActivityInPackage( - uid, intent, resolvedType, resultTo, resultWho, requestCode, onlyIfNeeded); - checkStartActivityResult(result, intent); - } catch (RemoteException ex) { - throw new AndroidRuntimeException(ex); - } - } - - // Stolen from Instrumentation.checkStartActivityResult() - private static void checkStartActivityResult(int res, Intent intent) { - if (res >= IActivityManager.START_SUCCESS) { - return; - } - switch (res) { - case IActivityManager.START_INTENT_NOT_RESOLVED: - case IActivityManager.START_CLASS_NOT_FOUND: - if (intent.getComponent() != null) - throw new ActivityNotFoundException( - "Unable to find explicit activity class " - + intent.getComponent().toShortString() - + "; have you declared this activity in your AndroidManifest.xml?"); - throw new ActivityNotFoundException( - "No Activity found to handle " + intent); - case IActivityManager.START_PERMISSION_DENIED: - throw new SecurityException("Not allowed to start activity " - + intent); - case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT: - throw new AndroidRuntimeException( - "FORWARD_RESULT_FLAG used while also requesting a result"); - default: - throw new AndroidRuntimeException("Unknown error code " - + res + " when starting " + intent); - } - } - - /** - * Handles the special intent actions declared in {@link SearchManager}. - * - * @return <code>true</code> if the intent was handled. - */ - private boolean handleSpecialIntent(Intent intent) { - String action = intent.getAction(); - if (SearchManager.INTENT_ACTION_CHANGE_SEARCH_SOURCE.equals(action)) { - handleChangeSourceIntent(intent); - return true; - } - return false; - } - - /** - * Handles {@link SearchManager#INTENT_ACTION_CHANGE_SEARCH_SOURCE}. - */ - private void handleChangeSourceIntent(Intent intent) { - Uri dataUri = intent.getData(); - if (dataUri == null) { - Log.w(LOG_TAG, "SearchManager.INTENT_ACTION_CHANGE_SOURCE without intent data."); - return; - } - ComponentName componentName = ComponentName.unflattenFromString(dataUri.toString()); - if (componentName == null) { - Log.w(LOG_TAG, "Invalid ComponentName: " + dataUri); - return; - } - if (DBG) Log.d(LOG_TAG, "Switching to " + componentName); - - pushPreviousComponent(mLaunchComponent); - if (!show(componentName, mAppSearchData, false)) { - Log.w(LOG_TAG, "Failed to switch to source " + componentName); - popPreviousComponent(); - return; - } - - String query = intent.getStringExtra(SearchManager.QUERY); - setUserQuery(query); - mSearchAutoComplete.showDropDown(); - } - - /** * Sets the list item selection in the AutoCompleteTextView's ListView. */ public void setListSelection(int index) { @@ -1479,61 +1099,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } /** - * Checks if there are any previous searchable components in the history stack. - */ - private boolean hasPreviousComponent() { - return mPreviousComponents != null && !mPreviousComponents.isEmpty(); - } - - /** - * Saves the previous component that was searched, so that we can go - * back to it. - */ - private void pushPreviousComponent(ComponentName componentName) { - if (mPreviousComponents == null) { - mPreviousComponents = new ArrayList<ComponentName>(); - } - mPreviousComponents.add(componentName); - } - - /** - * Pops the previous component off the stack and returns it. - * - * @return The component name, or <code>null</code> if there was - * no previous component. - */ - private ComponentName popPreviousComponent() { - if (!hasPreviousComponent()) { - return null; - } - return mPreviousComponents.remove(mPreviousComponents.size() - 1); - } - - /** - * Goes back to the previous component that was searched, if any. - * - * @return <code>true</code> if there was a previous component that we could go back to. - */ - private boolean backToPreviousComponent() { - ComponentName previous = popPreviousComponent(); - if (previous == null) { - return false; - } - - if (!show(previous, mAppSearchData, false)) { - Log.w(LOG_TAG, "Failed to switch to source " + previous); - return false; - } - - // must touch text to trigger suggestions - // TODO: should this be the text as it was when the user left - // the source that we are now going back to? - String query = mSearchAutoComplete.getText().toString(); - setUserQuery(query); - return true; - } - - /** * When a particular suggestion has been selected, perform the various lookups required * to use the suggestion. This includes checking the cursor for suggestion-specific data, * and/or falling back to the XML for defaults; It also creates REST style Uri data when @@ -1582,10 +1147,9 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS String query = getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY); String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA); - String mode = mGlobalSearchMode ? SearchManager.MODE_GLOBAL_SEARCH_SUGGESTION : null; return createIntent(action, dataUri, extraData, query, componentName, actionKey, - actionMsg, mode); + actionMsg); } catch (RuntimeException e ) { int rowNum; try { // be really paranoid now @@ -1616,16 +1180,13 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS * @return The intent. */ private Intent createIntent(String action, Uri data, String extraData, String query, - String componentName, int actionKey, String actionMsg, String mode) { + String componentName, int actionKey, String actionMsg) { // Now build the Intent Intent intent = new Intent(action); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // We need CLEAR_TOP to avoid reusing an old task that has other activities // on top of the one we want. We don't want to do this in in-app search though, // as it can be destructive to the activity stack. - if (mGlobalSearchMode) { - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - } if (data != null) { intent.setData(data); } @@ -1636,9 +1197,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS if (extraData != null) { intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData); } - if (componentName != null) { - intent.putExtra(SearchManager.COMPONENT_NAME_KEY, componentName); - } if (mAppSearchData != null) { intent.putExtra(SearchManager.APP_DATA, mAppSearchData); } @@ -1646,16 +1204,10 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS intent.putExtra(SearchManager.ACTION_KEY, actionKey); intent.putExtra(SearchManager.ACTION_MSG, actionMsg); } - if (mode != null) { - intent.putExtra(SearchManager.SEARCH_MODE, mode); - } - // Only allow 3rd-party intents from GlobalSearch - if (!mGlobalSearchMode) { - intent.setComponent(mSearchable.getSearchActivity()); - } + intent.setComponent(mSearchable.getSearchActivity()); return intent; } - + /** * For a given suggestion and a given cursor row, get the action message. If not provided * by the specific row/column, also check for a single definition (for the action key). @@ -1812,12 +1364,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS imm.hideSoftInputFromWindow(getWindow().getDecorView().getWindowToken(), 0)) { return; } - // Otherwise, go back to any previous source (e.g. back to QSB when - // pivoted into a source. - if (!backToPreviousComponent()) { - // If no previous source, close search dialog - cancel(); - } + // Close search dialog + cancel(); } /** diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index 5a9a675f62e2..52cdc748a818 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -350,7 +350,7 @@ import java.util.List; * <p><b>Configuring your Content Provider to Receive Suggestion Queries.</b> The basic job of * a search suggestions {@link android.content.ContentProvider Content Provider} is to provide * "live" (while-you-type) conversion of the user's query text into a set of zero or more - * suggestions. Each application is free to define the conversion, and as described above there are + * suggestions. Each application is free to define the conversion, and as described above there are * many possible solutions. This section simply defines how to communicate with the suggestion * provider. * @@ -360,7 +360,8 @@ import java.util.List; * * <p>Every query includes a Uri, and the Search Manager will format the Uri as shown: * <p><pre class="prettyprint"> - * content:// your.suggest.authority / your.suggest.path / SearchManager.SUGGEST_URI_PATH_QUERY</pre> + * content:// your.suggest.authority / your.suggest.path / SearchManager.SUGGEST_URI_PATH_QUERY + * </pre> * * <p>Your Content Provider can receive the query text in one of two ways. * <ul> @@ -379,7 +380,7 @@ import java.util.List; * <p><b>Providing access to Content Providers that require permissions.</b> If your content * provider declares an android:readPermission in your application's manifest, you must provide * access to the search infrastructure to the search suggestion path by including a path-permission - * that grants android:readPermission access to "android.permission.GLOBAL_SEARCH". Granting access + * that grants android:readPermission access to "android.permission.GLOBAL_SEARCH". Granting access * explicitly to the search infrastructure ensures it will be able to access the search suggestions * without needing to know ahead of time any other details of the permissions protecting your * provider. Content providers that require no permissions are already available to the search @@ -546,7 +547,8 @@ import java.util.List; * taken directly to a specific result. * <ul> * <li><b>Action:</b> {@link android.content.Intent#ACTION_VIEW ACTION_VIEW}</li> - * <li><b>Data:</b> a complete Uri, supplied by the cursor, that identifies the desired data.</li> + * <li><b>Data:</b> a complete Uri, supplied by the cursor, that identifies the desired data. + * </li> * <li><b>Query:</b> query text supplied with the suggestion (probably ignored)</li> * </ul> * </li> @@ -570,7 +572,7 @@ import java.util.List; * * <p><b>Suggestion Rewriting.</b> If the user navigates through the suggestions list, the UI * may temporarily rewrite the user's query with a query that matches the currently selected - * suggestion. This enables the user to see what query is being suggested, and also allows the user + * suggestion. This enables the user to see what query is being suggested, and also allows the user * to click or touch in the entry EditText element and make further edits to the query before * dispatching it. In order to perform this correctly, the Search UI needs to know exactly what * text to rewrite the query with. @@ -1289,15 +1291,6 @@ public class SearchManager public final static String SEARCH_MODE = "search_mode"; /** - * Value for the {@link #SEARCH_MODE} key. - * This is used if the intent was launched by clicking a suggestion in global search - * mode (Quick Search Box). - * - * @hide - */ - public static final String MODE_GLOBAL_SEARCH_SUGGESTION = "global_search_suggestion"; - - /** * Intent extra data key: Use this key with Intent.ACTION_SEARCH and * {@link android.content.Intent#getIntExtra content.Intent.getIntExtra()} * to obtain the keycode that the user used to trigger this query. It will be zero if the @@ -1308,14 +1301,6 @@ public class SearchManager public final static String ACTION_KEY = "action_key"; /** - * Intent component name key: This key will be used for the extra populated by the - * {@link #SUGGEST_COLUMN_INTENT_COMPONENT_NAME} column. - * - * {@hide} - */ - public final static String COMPONENT_NAME_KEY = "intent_component_name_key"; - - /** * Intent extra data key: This key will be used for the extra populated by the * {@link #SUGGEST_COLUMN_INTENT_EXTRA_DATA} column. */ @@ -1329,58 +1314,6 @@ public class SearchManager public final static String EXTRA_SELECT_QUERY = "select_query"; /** - * Defines the constants used in the communication between {@link android.app.SearchDialog} and - * the global search provider via {@link Cursor#respond(android.os.Bundle)}. - * - * @hide - */ - public static class DialogCursorProtocol { - - /** - * The sent bundle will contain this integer key, with a value set to one of the events - * below. - */ - public final static String METHOD = "DialogCursorProtocol.method"; - - /** - * After data has been refreshed. - */ - public final static int POST_REFRESH = 0; - public final static String POST_REFRESH_RECEIVE_ISPENDING - = "DialogCursorProtocol.POST_REFRESH.isPending"; - public final static String POST_REFRESH_RECEIVE_DISPLAY_NOTIFY - = "DialogCursorProtocol.POST_REFRESH.displayNotify"; - - /** - * When a position has been clicked. - */ - public final static int CLICK = 2; - public final static String CLICK_SEND_POSITION - = "DialogCursorProtocol.CLICK.sendPosition"; - public final static String CLICK_SEND_MAX_DISPLAY_POS - = "DialogCursorProtocol.CLICK.sendDisplayPosition"; - public final static String CLICK_SEND_ACTION_KEY - = "DialogCursorProtocol.CLICK.sendActionKey"; - public final static String CLICK_SEND_ACTION_MSG - = "DialogCursorProtocol.CLICK.sendActionMsg"; - public final static String CLICK_RECEIVE_SELECTED_POS - = "DialogCursorProtocol.CLICK.receiveSelectedPosition"; - - /** - * When the threshold received in {@link #POST_REFRESH_RECEIVE_DISPLAY_NOTIFY} is displayed. - */ - public final static int THRESH_HIT = 3; - - /** - * When a search is started without using a suggestion. - */ - public final static int SEARCH = 4; - public final static String SEARCH_SEND_MAX_DISPLAY_POS - = "DialogCursorProtocol.SEARCH.sendDisplayPosition"; - public final static String SEARCH_SEND_QUERY = "DialogCursorProtocol.SEARCH.query"; - } - - /** * Intent extra data key: Use this key with Intent.ACTION_SEARCH and * {@link android.content.Intent#getStringExtra content.Intent.getStringExtra()} * to obtain the action message that was defined for a particular search action key and/or @@ -1421,40 +1354,6 @@ public class SearchManager public final static String SHORTCUT_MIME_TYPE = "vnd.android.cursor.item/vnd.android.search.suggest"; - - /** - * The authority of the provider to report clicks to when a click is detected after pivoting - * into a specific app's search from global search. - * - * In addition to the columns below, the suggestion columns are used to pass along the full - * suggestion so it can be shortcutted. - * - * @hide - */ - public final static String SEARCH_CLICK_REPORT_AUTHORITY = - "com.android.globalsearch.stats"; - - /** - * The path the write goes to. - * - * @hide - */ - public final static String SEARCH_CLICK_REPORT_URI_PATH = "click"; - - /** - * The column storing the query for the click. - * - * @hide - */ - public final static String SEARCH_CLICK_REPORT_COLUMN_QUERY = "query"; - - /** - * The column storing the component name of the application that was pivoted into. - * - * @hide - */ - public final static String SEARCH_CLICK_REPORT_COLUMN_COMPONENT = "component"; - /** * Column name for suggestions cursor. <i>Unused - can be null or column can be omitted.</i> */ @@ -1531,10 +1430,7 @@ public class SearchManager */ public final static String SUGGEST_COLUMN_INTENT_EXTRA_DATA = "suggest_intent_extra_data"; /** - * Column name for suggestions cursor. <i>Optional.</i> This column allows suggestions - * to provide additional arbitrary data which will be included as an extra under the key - * {@link #COMPONENT_NAME_KEY}. For use by the global search system only - if other providers - * attempt to use this column, the value will be overwritten by global search. + * TODO: Remove * * @hide */ @@ -1594,27 +1490,6 @@ public class SearchManager public final static String SUGGEST_PARAMETER_LIMIT = "limit"; /** - * If a suggestion has this value in {@link #SUGGEST_COLUMN_INTENT_ACTION}, - * the search dialog will switch to a different suggestion source when the - * suggestion is clicked. - * - * {@link #SUGGEST_COLUMN_INTENT_DATA} must contain - * the flattened {@link ComponentName} of the activity which is to be searched. - * - * TODO: Should {@link #SUGGEST_COLUMN_INTENT_DATA} instead contain a URI in the format - * used by {@link android.provider.Applications}? - * - * TODO: This intent should be protected by the same permission that we use - * for replacing the global search provider. - * - * The query text field will be set to the value of {@link #SUGGEST_COLUMN_QUERY}. - * - * @hide Pending API council approval. - */ - public final static String INTENT_ACTION_CHANGE_SEARCH_SOURCE - = "android.search.action.CHANGE_SEARCH_SOURCE"; - - /** * Intent action for starting the global search activity. * The global search provider should handle this intent. * @@ -1663,7 +1538,7 @@ public class SearchManager * @hide */ public final static String INTENT_ACTION_NONE = "android.search.action.ZILCH"; - + /** * Reference to the shared system search service. */ @@ -1672,13 +1547,6 @@ public class SearchManager private final Context mContext; /** - * compact representation of the activity associated with this search manager so - * we can say who we are when starting search. the search managerservice, in turn, - * uses this to properly handle the back stack. - */ - private int mIdent; - - /** * The package associated with this seach manager. */ private String mAssociatedPackage; @@ -1696,21 +1564,6 @@ public class SearchManager mService = ISearchManager.Stub.asInterface( ServiceManager.getService(Context.SEARCH_SERVICE)); } - - /*package*/ boolean hasIdent() { - return mIdent != 0; - } - - /*package*/ void setIdent(int ident, ComponentName component) { - if (mIdent != 0) { - throw new IllegalStateException("mIdent already set"); - } - if (component == null) { - throw new IllegalArgumentException("component must be non-null"); - } - mIdent = ident; - mAssociatedPackage = component.getPackageName(); - } /** * Launch search UI. @@ -1757,15 +1610,14 @@ public class SearchManager ComponentName launchActivity, Bundle appSearchData, boolean globalSearch) { - ensureSearchDialog(); - if (globalSearch) { startGlobalSearch(initialQuery, selectInitialQuery, appSearchData); return; } - mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData, - globalSearch); + ensureSearchDialog(); + + mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData); } private void ensureSearchDialog() { @@ -1994,17 +1846,6 @@ public class SearchManager return null; } } - - /** - * Checks whether the given searchable is the default searchable. - * - * @hide because SearchableInfo is not part of the API. - */ - public boolean isDefaultSearchable(SearchableInfo searchable) { - SearchableInfo defaultSearchable = getSearchableInfo(null, true); - return defaultSearchable != null - && defaultSearchable.getSearchActivity().equals(searchable.getSearchActivity()); - } /** * Gets a cursor with search suggestions. @@ -2091,56 +1932,4 @@ public class SearchManager } } - /** - * Returns a list of the searchable activities that handle web searches. - * - * @return a list of all searchable activities that handle - * {@link android.content.Intent#ACTION_WEB_SEARCH}. - * - * @hide because SearchableInfo is not part of the API. - */ - public List<SearchableInfo> getSearchablesForWebSearch() { - try { - return mService.getSearchablesForWebSearch(); - } catch (RemoteException e) { - Log.e(TAG, "getSearchablesForWebSearch() failed: " + e); - return null; - } - } - - /** - * Returns the default searchable activity for web searches. - * - * @return searchable information for the activity handling web searches by default. - * - * @hide because SearchableInfo is not part of the API. - */ - public SearchableInfo getDefaultSearchableForWebSearch() { - try { - return mService.getDefaultSearchableForWebSearch(); - } catch (RemoteException e) { - Log.e(TAG, "getDefaultSearchableForWebSearch() failed: " + e); - return null; - } - } - - /** - * Sets the default searchable activity for web searches. - * - * @param component Name of the component to set as default activity for web searches. - * - * @hide - */ - public void setDefaultWebSearch(ComponentName component) { - try { - mService.setDefaultWebSearch(component); - } catch (RemoteException e) { - Log.e(TAG, "setDefaultWebSearch() failed: " + e); - } - } - - private static void debug(String msg) { - Thread thread = Thread.currentThread(); - Log.d(TAG, msg + " (" + thread.getName() + "-" + thread.getId() + ")"); - } } diff --git a/core/java/android/app/SuggestionsAdapter.java b/core/java/android/app/SuggestionsAdapter.java index 173c3e1907a0..57795d11b912 100644 --- a/core/java/android/app/SuggestionsAdapter.java +++ b/core/java/android/app/SuggestionsAdapter.java @@ -16,7 +16,6 @@ package android.app; -import android.app.SearchManager.DialogCursorProtocol; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; @@ -30,12 +29,10 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.StateListDrawable; import android.net.Uri; -import android.os.Bundle; import android.text.Html; import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; -import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; import android.widget.Filter; @@ -65,7 +62,6 @@ class SuggestionsAdapter extends ResourceCursorAdapter { private Context mProviderContext; private WeakHashMap<String, Drawable.ConstantState> mOutsideDrawablesCache; private SparseArray<Drawable.ConstantState> mBackgroundsCache; - private boolean mGlobalSearchMode; private boolean mClosed = false; // Cached column indexes, updated when the cursor changes. @@ -76,29 +72,8 @@ class SuggestionsAdapter extends ResourceCursorAdapter { private int mIconName2Col; private int mBackgroundColorCol; - // The extra used to tell a cursor to close itself. This is a hack, see the description by - // its use later in this file. - private static final String EXTRA_CURSOR_RESPOND_CLOSE_CURSOR = "cursor_respond_close_cursor"; - - // The bundle which contains {EXTRA_CURSOR_RESPOND_CLOSE_CURSOR=true}, just cached once - // so we don't bother recreating it a bunch. - private final Bundle mCursorRespondCloseCursorBundle; - - // This value is stored in SuggestionsAdapter by the SearchDialog to indicate whether - // a particular list item should be selected upon the next call to notifyDataSetChanged. - // This is used to indicate the index of the "More results..." list item so that when - // the data set changes after a click of "More results...", we can correctly tell the - // ListView to scroll to the right line item. It gets reset to NONE every time it - // is consumed. - private int mListItemToSelect = NONE; static final int NONE = -1; - // holds the maximum position that has been displayed to the user - int mMaxDisplayed = NONE; - - // holds the position that, when displayed, should result in notifying the cursor - int mDisplayNotifyPos = NONE; - private final Runnable mStartSpinnerRunnable; private final Runnable mStopSpinnerRunnable; @@ -110,8 +85,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter { public SuggestionsAdapter(Context context, SearchDialog searchDialog, SearchableInfo searchable, - WeakHashMap<String, Drawable.ConstantState> outsideDrawablesCache, - boolean globalSearchMode) { + WeakHashMap<String, Drawable.ConstantState> outsideDrawablesCache) { super(context, com.android.internal.R.layout.search_dropdown_item_icons_2line, null, // no initial cursor @@ -126,7 +100,6 @@ class SuggestionsAdapter extends ResourceCursorAdapter { mOutsideDrawablesCache = outsideDrawablesCache; mBackgroundsCache = new SparseArray<Drawable.ConstantState>(); - mGlobalSearchMode = globalSearchMode; mStartSpinnerRunnable = new Runnable() { public void run() { @@ -140,10 +113,6 @@ class SuggestionsAdapter extends ResourceCursorAdapter { } }; - // Create this once because we'll reuse it a bunch. - mCursorRespondCloseCursorBundle = new Bundle(); - mCursorRespondCloseCursorBundle.putBoolean(EXTRA_CURSOR_RESPOND_CLOSE_CURSOR, true); - // delay 500ms when deleting getFilter().setDelayer(new Filter.Delayer() { @@ -177,27 +146,22 @@ class SuggestionsAdapter extends ResourceCursorAdapter { public Cursor runQueryOnBackgroundThread(CharSequence constraint) { if (DBG) Log.d(LOG_TAG, "runQueryOnBackgroundThread(" + constraint + ")"); String query = (constraint == null) ? "" : constraint.toString(); - if (!mGlobalSearchMode) { - /** - * for in app search we show the progress spinner until the cursor is returned with - * the results. for global search we manage the progress bar using - * {@link DialogCursorProtocol#POST_REFRESH_RECEIVE_ISPENDING}. - */ - mSearchDialog.getWindow().getDecorView().post(mStartSpinnerRunnable); - } + /** + * for in app search we show the progress spinner until the cursor is returned with + * the results. + */ + mSearchDialog.getWindow().getDecorView().post(mStartSpinnerRunnable); try { final Cursor cursor = mSearchManager.getSuggestions(mSearchable, query, QUERY_LIMIT); // trigger fill window so the spinner stays up until the results are copied over and // closer to being ready - if (!mGlobalSearchMode && cursor != null) cursor.getCount(); + if (cursor != null) cursor.getCount(); return cursor; } catch (RuntimeException e) { Log.w(LOG_TAG, "Search suggestions query threw an exception.", e); return null; } finally { - if (!mGlobalSearchMode) { - mSearchDialog.getWindow().getDecorView().post(mStopSpinnerRunnable); - } + mSearchDialog.getWindow().getDecorView().post(mStopSpinnerRunnable); } } @@ -221,21 +185,8 @@ class SuggestionsAdapter extends ResourceCursorAdapter { } try { - Cursor oldCursor = getCursor(); super.changeCursor(c); - - // We send a special respond to the cursor to tell it to close itself directly because - // it may not happen correctly for some cursors currently. This was originally - // included as a fix to http://b/2036290, in which the search dialog was holding - // on to references to the web search provider unnecessarily. This is being caused by - // the fact that the cursor is not being correctly closed in - // BulkCursorToCursorAdapter#close, which remains unfixed (see http://b/2015069). - // - // TODO: Remove this hack once http://b/2015069 is fixed. - if (oldCursor != null && oldCursor != c) { - oldCursor.respond(mCursorRespondCloseCursorBundle); - } - + if (c != null) { mFormatCol = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_FORMAT); mText1Col = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1); @@ -249,79 +200,6 @@ class SuggestionsAdapter extends ResourceCursorAdapter { } } - @Override - public void notifyDataSetChanged() { - if (DBG) Log.d(LOG_TAG, "notifyDataSetChanged"); - super.notifyDataSetChanged(); - - callCursorPostRefresh(mCursor); - - // look out for the pending item we are supposed to scroll to - if (mListItemToSelect != NONE) { - mSearchDialog.setListSelection(mListItemToSelect); - mListItemToSelect = NONE; - } - } - - /** - * Handle sending and receiving information associated with - * {@link DialogCursorProtocol#POST_REFRESH}. - * - * @param cursor The cursor to call. - */ - private void callCursorPostRefresh(Cursor cursor) { - if (!mGlobalSearchMode) return; - final Bundle request = new Bundle(); - request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.POST_REFRESH); - final Bundle response = cursor.respond(request); - - mSearchDialog.setWorking( - response.getBoolean(DialogCursorProtocol.POST_REFRESH_RECEIVE_ISPENDING, false)); - - mDisplayNotifyPos = - response.getInt(DialogCursorProtocol.POST_REFRESH_RECEIVE_DISPLAY_NOTIFY, -1); - } - - /** - * Tell the cursor which position was clicked, handling sending and receiving information - * associated with {@link DialogCursorProtocol#CLICK}. - * - * @param cursor The cursor - * @param position The position that was clicked. - */ - void callCursorOnClick(Cursor cursor, int position, int actionKey, String actionMsg) { - if (!mGlobalSearchMode) return; - final Bundle request = new Bundle(5); - request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.CLICK); - request.putInt(DialogCursorProtocol.CLICK_SEND_POSITION, position); - request.putInt(DialogCursorProtocol.CLICK_SEND_MAX_DISPLAY_POS, mMaxDisplayed); - if (actionKey != KeyEvent.KEYCODE_UNKNOWN) { - request.putInt(DialogCursorProtocol.CLICK_SEND_ACTION_KEY, actionKey); - request.putString(DialogCursorProtocol.CLICK_SEND_ACTION_MSG, actionMsg); - } - final Bundle response = cursor.respond(request); - mMaxDisplayed = -1; - mListItemToSelect = response.getInt( - DialogCursorProtocol.CLICK_RECEIVE_SELECTED_POS, SuggestionsAdapter.NONE); - } - - /** - * Tell the cursor that a search was started without using a suggestion. - * - * @param query The search query. - */ - void reportSearch(String query) { - if (!mGlobalSearchMode) return; - Cursor cursor = getCursor(); - if (cursor == null) return; - final Bundle request = new Bundle(3); - request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.SEARCH); - request.putString(DialogCursorProtocol.SEARCH_SEND_QUERY, query); - request.putInt(DialogCursorProtocol.SEARCH_SEND_MAX_DISPLAY_POS, mMaxDisplayed); - // the response is always empty - cursor.respond(request); - } - /** * Tags the view with cached child view look-ups. */ @@ -353,20 +231,6 @@ class SuggestionsAdapter extends ResourceCursorAdapter { @Override public void bindView(View view, Context context, Cursor cursor) { ChildViewCache views = (ChildViewCache) view.getTag(); - final int pos = cursor.getPosition(); - - // update the maximum position displayed since last refresh - if (pos > mMaxDisplayed) { - mMaxDisplayed = pos; - } - - // if the cursor wishes to be notified about this position, send it - if (mGlobalSearchMode && mDisplayNotifyPos != NONE && pos == mDisplayNotifyPos) { - final Bundle request = new Bundle(); - request.putInt(DialogCursorProtocol.METHOD, DialogCursorProtocol.THRESH_HIT); - mCursor.respond(request); - mDisplayNotifyPos = NONE; // only notify the first time - } int backgroundColor = 0; if (mBackgroundColorCol != -1) { diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 544e39963da7..acb84737d8bd 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -608,6 +608,45 @@ public final class ContactsContract { } /** + * URI parameter and cursor extras that return counts of rows grouped by the + * address book index, which is usually the first letter of the sort key. + * When this parameter is supplied, the row counts are returned in the + * cursor extras bundle. + * + * @hide + */ + public final static class ContactCounts { + + /** + * Add this query parameter to a URI to get back row counts grouped by + * the address book index as cursor extras. For most languages it is the + * first letter of the sort key. This parameter does not affect the main + * content of the cursor. + * + * @hide + */ + public static final String ADDRESS_BOOK_INDEX_EXTRAS = "address_book_index_extras"; + + /** + * The array of address book index titles, which are returned in the + * same order as the data in the cursor. + * <p>TYPE: String[]</p> + * + * @hide + */ + public static final String EXTRA_ADDRESS_BOOK_INDEX_TITLES = "address_book_index_titles"; + + /** + * The array of group counts for the corresponding group. Contains the same number + * of elements as the EXTRA_ADDRESS_BOOK_INDEX_TITLES array. + * <p>TYPE: int[]</p> + * + * @hide + */ + public static final String EXTRA_ADDRESS_BOOK_INDEX_COUNTS = "address_book_index_counts"; + } + + /** * Constants for the contacts table, which contains a record per aggregate * of raw contacts representing the same person. * <h3>Operations</h3> @@ -5695,5 +5734,4 @@ public final class ContactsContract { public static final String IM_ISPRIMARY = "im_isprimary"; } } - } diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java index 22bb43c72bee..096ad391752d 100644 --- a/core/java/android/server/BluetoothA2dpService.java +++ b/core/java/android/server/BluetoothA2dpService.java @@ -121,10 +121,44 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { handleSinkStateChange(device, state, BluetoothA2dp.STATE_DISCONNECTED); } } + } else if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) { + int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); + if (streamType == AudioManager.STREAM_MUSIC) { + BluetoothDevice sinks[] = getConnectedSinks(); + if (sinks.length != 0 && isPhoneDocked(sinks[0])) { + String address = sinks[0].getAddress(); + int newVolLevel = + intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0); + int oldVolLevel = + intent.getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0); + String path = mBluetoothService.getObjectPathFromAddress(address); + if (newVolLevel > oldVolLevel) { + avrcpVolumeUpNative(path); + } else if (newVolLevel < oldVolLevel) { + avrcpVolumeDownNative(path); + } + } + } } } }; + + private boolean isPhoneDocked(BluetoothDevice device) { + // This works only because these broadcast intents are "sticky" + Intent i = mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT)); + if (i != null) { + int state = i.getIntExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED); + if (state != Intent.EXTRA_DOCK_STATE_UNDOCKED) { + BluetoothDevice dockDevice = i.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + if (dockDevice != null && device.equals(dockDevice)) { + return true; + } + } + } + return false; + } + public BluetoothA2dpService(Context context, BluetoothService bluetoothService) { mContext = context; @@ -145,6 +179,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { mIntentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); mIntentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); mIntentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); + mIntentFilter.addAction(AudioManager.VOLUME_CHANGED_ACTION); mContext.registerReceiver(mReceiver, mIntentFilter); mAudioDevices = new HashMap<BluetoothDevice, Integer>(); @@ -551,4 +586,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { private synchronized native boolean suspendSinkNative(String path); private synchronized native boolean resumeSinkNative(String path); private synchronized native Object []getSinkPropertiesNative(String path); + private synchronized native boolean avrcpVolumeUpNative(String path); + private synchronized native boolean avrcpVolumeDownNative(String path); } diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index dfb775f15ba2..aa20ac481cdf 100644 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -136,6 +136,14 @@ public class BluetoothService extends IBluetooth.Stub { } return false; } + + @Override + public int hashCode() { + int hash = 1; + hash = hash * 31 + (address == null ? 0 : address.hashCode()); + hash = hash * 31 + (uuid == null ? 0 : uuid.hashCode()); + return hash; + } } static { diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index afc686401656..102303610cc5 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -39,6 +39,8 @@ import android.view.KeyEvent; */ public abstract class Layout { private static final boolean DEBUG = false; + private static final ParagraphStyle[] NO_PARA_SPANS = + ArrayUtils.emptyArray(ParagraphStyle.class); /* package */ static final EmojiFactory EMOJI_FACTORY = EmojiFactory.newAvailableInstance(); @@ -57,7 +59,7 @@ public abstract class Layout { private RectF mEmojiRect; /** - * Return how wide a layout would be necessary to display the + * Return how wide a layout must be in order to display the * specified text with one line per paragraph. */ public static float getDesiredWidth(CharSequence source, @@ -66,7 +68,7 @@ public abstract class Layout { } /** - * Return how wide a layout would be necessary to display the + * Return how wide a layout must be in order to display the * specified text slice with one line per paragraph. */ public static float getDesiredWidth(CharSequence source, @@ -82,6 +84,7 @@ public abstract class Layout { if (next < 0) next = end; + // note, omits trailing paragraph char float w = measureText(paint, workPaint, source, i, next, null, true, null); @@ -97,10 +100,19 @@ public abstract class Layout { /** * Subclasses of Layout use this constructor to set the display text, * width, and other standard properties. + * @param text the text to render + * @param paint the default paint for the layout. Styles can override + * various attributes of the paint. + * @param width the wrapping width for the text. + * @param align whether to left, right, or center the text. Styles can + * override the alignment. + * @param spacingMult factor by which to scale the font size to get the + * default line spacing + * @param spacingAdd amount to add to the default line spacing */ protected Layout(CharSequence text, TextPaint paint, int width, Alignment align, - float spacingmult, float spacingadd) { + float spacingMult, float spacingAdd) { if (width < 0) throw new IllegalArgumentException("Layout: " + width + " < 0"); @@ -109,8 +121,8 @@ public abstract class Layout { mWorkPaint = new TextPaint(); mWidth = width; mAlignment = align; - mSpacingMult = spacingmult; - mSpacingAdd = spacingadd; + mSpacingMult = spacingMult; + mSpacingAdd = spacingAdd; mSpannedText = text instanceof Spanned; } @@ -141,10 +153,16 @@ public abstract class Layout { } /** - * Draw the specified rectangle from this Layout on the specified Canvas, - * with the specified path drawn between the background and the text. + * Draw this Layout on the specified canvas, with the highlight path drawn + * between the background and the text. + * + * @param c the canvas + * @param highlight the path of the highlight or cursor; can be null + * @param highlightPaint the paint for the highlight + * @param cursorOffsetVertical the amount to temporarily translate the + * canvas while rendering the highlight */ - public void draw(Canvas c, Path highlight, Paint highlightpaint, + public void draw(Canvas c, Path highlight, Paint highlightPaint, int cursorOffsetVertical) { int dtop, dbottom; @@ -157,13 +175,10 @@ public abstract class Layout { dbottom = sTempRect.bottom; } - TextPaint paint = mPaint; int top = 0; - // getLineBottom(getLineCount() -1) just calls getLineTop(getLineCount) int bottom = getLineTop(getLineCount()); - if (dtop > top) { top = dtop; } @@ -177,16 +192,19 @@ public abstract class Layout { int previousLineBottom = getLineTop(first); int previousLineEnd = getLineStart(first); + TextPaint paint = mPaint; CharSequence buf = mText; + int width = mWidth; + boolean spannedText = mSpannedText; - ParagraphStyle[] nospans = ArrayUtils.emptyArray(ParagraphStyle.class); - ParagraphStyle[] spans = nospans; + ParagraphStyle[] spans = NO_PARA_SPANS; int spanend = 0; int textLength = 0; - boolean spannedText = mSpannedText; + // First, draw LineBackgroundSpans. + // LineBackgroundSpans know nothing about the alignment or direction of + // the layout or line. XXX: Should they? if (spannedText) { - spanend = 0; textLength = buf.length(); for (int i = first; i <= last; i++) { int start = previousLineEnd; @@ -209,7 +227,7 @@ public abstract class Layout { for (int n = 0; n < spans.length; n++) { LineBackgroundSpan back = (LineBackgroundSpan) spans[n]; - back.drawBackground(c, paint, 0, mWidth, + back.drawBackground(c, paint, 0, width, ltop, lbaseline, lbottom, buf, start, end, i); @@ -219,7 +237,7 @@ public abstract class Layout { spanend = 0; previousLineBottom = getLineTop(first); previousLineEnd = getLineStart(first); - spans = nospans; + spans = NO_PARA_SPANS; } // There can be a highlight even without spans if we are drawing @@ -229,7 +247,7 @@ public abstract class Layout { c.translate(0, cursorOffsetVertical); } - c.drawPath(highlight, highlightpaint); + c.drawPath(highlight, highlightPaint); if (cursorOffsetVertical != 0) { c.translate(0, -cursorOffsetVertical); @@ -238,6 +256,9 @@ public abstract class Layout { Alignment align = mAlignment; + // Next draw the lines, one at a time. + // the baseline is the top of the following line minus the current + // line's descent. for (int i = first; i <= last; i++) { int start = previousLineEnd; @@ -249,21 +270,20 @@ public abstract class Layout { previousLineBottom = lbottom; int lbaseline = lbottom - getLineDescent(i); - boolean par = false; + boolean isFirstParaLine = false; if (spannedText) { if (start == 0 || buf.charAt(start - 1) == '\n') { - par = true; + isFirstParaLine = true; } + // New batch of paragraph styles, compute the alignment. + // Last alignment style wins. if (start >= spanend) { - Spanned sp = (Spanned) buf; - spanend = sp.nextSpanTransition(start, textLength, ParagraphStyle.class); spans = sp.getSpans(start, spanend, ParagraphStyle.class); align = mAlignment; - for (int n = spans.length-1; n >= 0; n--) { if (spans[n] instanceof AlignmentSpan) { align = ((AlignmentSpan) spans[n]).getAlignment(); @@ -277,6 +297,8 @@ public abstract class Layout { int left = 0; int right = mWidth; + // Draw all leading margin spans. Adjust left or right according + // to the paragraph direction of the line. if (spannedText) { final int length = spans.length; for (int n = 0; n < length; n++) { @@ -286,15 +308,15 @@ public abstract class Layout { if (dir == DIR_RIGHT_TO_LEFT) { margin.drawLeadingMargin(c, paint, right, dir, ltop, lbaseline, lbottom, buf, - start, end, par, this); + start, end, isFirstParaLine, this); - right -= margin.getLeadingMargin(par); + right -= margin.getLeadingMargin(isFirstParaLine); } else { margin.drawLeadingMargin(c, paint, left, dir, ltop, lbaseline, lbottom, buf, - start, end, par, this); + start, end, isFirstParaLine, this); - boolean useMargin = par; + boolean useMargin = isFirstParaLine; if (margin instanceof LeadingMarginSpan.LeadingMarginSpan2) { int count = ((LeadingMarginSpan.LeadingMarginSpan2)margin).getLeadingMarginLineCount(); useMargin = count > i; @@ -305,6 +327,8 @@ public abstract class Layout { } } + // Adjust the point at which to start rendering depending on the + // alignment of the paragraph. int x; if (align == Alignment.ALIGN_NORMAL) { if (dir == DIR_LEFT_TO_RIGHT) { @@ -340,6 +364,7 @@ public abstract class Layout { Assert.assertTrue(dir == DIR_LEFT_TO_RIGHT); Assert.assertNotNull(c); } + // XXX: assumes there's nothing additional to be done c.drawText(buf, start, end, x, lbaseline, paint); } else { drawText(c, buf, start, end, dir, directions, @@ -382,7 +407,7 @@ public abstract class Layout { /** * Increase the width of this layout to the specified width. - * Be careful to use this only when you know it is appropriate -- + * Be careful to use this only when you know it is appropriate— * it does not cause the text to reflow to use the full new width. */ public final void increaseWidthTo(int wid) { @@ -397,7 +422,7 @@ public abstract class Layout { * Return the total height of this layout. */ public int getHeight() { - return getLineTop(getLineCount()); // same as getLineBottom(getLineCount() - 1); + return getLineTop(getLineCount()); } /** @@ -439,33 +464,35 @@ public abstract class Layout { bounds.left = 0; // ??? bounds.top = getLineTop(line); bounds.right = mWidth; // ??? - bounds.bottom = getLineBottom(line); + bounds.bottom = getLineTop(line + 1); } return getLineBaseline(line); } /** - * Return the vertical position of the top of the specified line. - * If the specified line is one beyond the last line, returns the + * Return the vertical position of the top of the specified line + * (0…getLineCount()). + * If the specified line is equal to the line count, returns the * bottom of the last line. */ public abstract int getLineTop(int line); /** - * Return the descent of the specified line. + * Return the descent of the specified line(0…getLineCount() - 1). */ public abstract int getLineDescent(int line); /** - * Return the text offset of the beginning of the specified line. - * If the specified line is one beyond the last line, returns the - * end of the last line. + * Return the text offset of the beginning of the specified line ( + * 0…getLineCount()). If the specified line is equal to the line + * count, returns the length of the text. */ public abstract int getLineStart(int line); /** - * Returns the primary directionality of the paragraph containing - * the specified line. + * Returns the primary directionality of the paragraph containing the + * specified line, either 1 for left-to-right lines, or -1 for right-to-left + * lines (see {@link #DIR_LEFT_TO_RIGHT}, {@link #DIR_RIGHT_TO_LEFT}). */ public abstract int getParagraphDirection(int line); @@ -477,9 +504,11 @@ public abstract class Layout { public abstract boolean getLineContainsTab(int line); /** - * Returns an array of directionalities for the specified line. + * Returns the directional run information for the specified line. * The array alternates counts of characters in left-to-right * and right-to-left segments of the line. + * + * <p>NOTE: this is inadequate to support bidirectional text, and will change. */ public abstract Directions getLineDirections(int line); @@ -1565,6 +1594,21 @@ public abstract class Layout { return h; } + /** + * Measure width of a run of text on a single line that is known to all be + * in the same direction as the paragraph base direction. Returns the width, + * and the line metrics in fm if fm is not null. + * + * @param paint the paint for the text; will not be modified + * @param workPaint paint available for modification + * @param text text + * @param start start of the line + * @param end limit of the line + * @param fm object to return integer metrics in, can be null + * @param hasTabs true if it is known that the line has tabs + * @param tabs tab position information + * @return the width of the text from start to end + */ /* package */ static float measureText(TextPaint paint, TextPaint workPaint, CharSequence text, @@ -1580,37 +1624,36 @@ public abstract class Layout { int len = end - start; - int here = 0; - float h = 0; - int ab = 0, be = 0; - int top = 0, bot = 0; + int lastPos = 0; + float width = 0; + int ascent = 0, descent = 0, top = 0, bottom = 0; if (fm != null) { fm.ascent = 0; fm.descent = 0; } - for (int i = hasTabs ? 0 : len; i <= len; i++) { + for (int pos = hasTabs ? 0 : len; pos <= len; pos++) { int codept = 0; Bitmap bm = null; - if (hasTabs && i < len) { - codept = buf[i]; + if (hasTabs && pos < len) { + codept = buf[pos]; } - if (codept >= 0xD800 && codept <= 0xDFFF && i < len) { - codept = Character.codePointAt(buf, i); + if (codept >= 0xD800 && codept <= 0xDFFF && pos < len) { + codept = Character.codePointAt(buf, pos); if (codept >= MIN_EMOJI && codept <= MAX_EMOJI) { bm = EMOJI_FACTORY.getBitmapFromAndroidPua(codept); } } - if (i == len || codept == '\t' || bm != null) { + if (pos == len || codept == '\t' || bm != null) { workPaint.baselineShift = 0; - h += Styled.measureText(paint, workPaint, text, - start + here, start + i, + width += Styled.measureText(paint, workPaint, text, + start + lastPos, start + pos, fm); if (fm != null) { @@ -1623,60 +1666,80 @@ public abstract class Layout { } } - if (i != len) { + if (pos != len) { if (bm == null) { - h = nextTab(text, start, end, h, tabs); + // no emoji, must have hit a tab + width = nextTab(text, start, end, width, tabs); } else { + // This sets up workPaint with the font on the emoji + // text, so that we can extract the ascent and scale. + + // We can't use the result of the previous call to + // measureText because the emoji might have its own style. + // We have to initialize workPaint here because if the + // text is unstyled measureText might not use workPaint + // at all. workPaint.set(paint); Styled.measureText(paint, workPaint, text, - start + i, start + i + 1, null); + start + pos, start + pos + 1, null); - float wid = (float) bm.getWidth() * + width += (float) bm.getWidth() * -workPaint.ascent() / bm.getHeight(); - h += wid; - i++; + // Since we had an emoji, we bump past the second half + // of the surrogate pair. + pos++; } } if (fm != null) { - if (fm.ascent < ab) { - ab = fm.ascent; + if (fm.ascent < ascent) { + ascent = fm.ascent; } - if (fm.descent > be) { - be = fm.descent; + if (fm.descent > descent) { + descent = fm.descent; } if (fm.top < top) { top = fm.top; } - if (fm.bottom > bot) { - bot = fm.bottom; + if (fm.bottom > bottom) { + bottom = fm.bottom; } - /* - * No need to take bitmap height into account here, - * since it is scaled to match the text height. - */ + // No need to take bitmap height into account here, + // since it is scaled to match the text height. } - here = i + 1; + lastPos = pos + 1; } } if (fm != null) { - fm.ascent = ab; - fm.descent = be; + fm.ascent = ascent; + fm.descent = descent; fm.top = top; - fm.bottom = bot; + fm.bottom = bottom; } if (hasTabs) TextUtils.recycle(buf); - return h; + return width; } + /** + * Returns the position of the next tab stop after h on the line. + * + * @param text the text + * @param start start of the line + * @param end limit of the line + * @param h the current horizontal offset + * @param tabs the tabs, can be null. If it is null, any tabs in effect + * on the line will be used. If there are no tabs, a default offset + * will be used to compute the tab stop. + * @return the offset of the next tab stop. + */ /* package */ static float nextTab(CharSequence text, int start, int end, float h, Object[] tabs) { float nh = Float.MAX_VALUE; @@ -1747,6 +1810,16 @@ public abstract class Layout { public static class Directions { private short[] mDirections; + // The values in mDirections are the offsets from the first character + // in the line to the next flip in direction. Runs at even indices + // are left-to-right, the others are right-to-left. So, for example, + // a line that starts with a right-to-left run has 0 at mDirections[0], + // since the 'first' (ltr) run is zero length. + // + // The code currently assumes that each run is adjacent to the previous + // one, progressing in the base line direction. This isn't sufficient + // to handle nested runs, for example numeric text in an rtl context + // in an ltr paragraph. /* package */ Directions(short[] dirs) { mDirections = dirs; } diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index fbf12613b0d4..6c89f92f1bcd 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -1012,6 +1012,10 @@ extends Layout int extra; if (needMultiply) { + // XXX: this looks like it is using the +0.5 and the cast to int + // to do rounding, but this I expect this isn't doing the intended + // thing when spacingmult < 1. An intended extra of, say, -1.2 + // will get 'rounded' to -.7 and then truncated to 0. extra = (int) ((below - above) * (spacingmult - 1) + spacingadd + 0.5); } else { diff --git a/core/java/android/text/Styled.java b/core/java/android/text/Styled.java index 0aa2004fa258..513b2cd44437 100644 --- a/core/java/android/text/Styled.java +++ b/core/java/android/text/Styled.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package android.text; import android.graphics.Canvas; @@ -23,27 +22,49 @@ import android.text.style.MetricAffectingSpan; import android.text.style.ReplacementSpan; /** - * This class provides static methods for drawing and measuring styled texts, like - * {@link android.text.Spanned} object with {@link android.text.style.ReplacementSpan}. + * This class provides static methods for drawing and measuring styled text, + * like {@link android.text.Spanned} object with + * {@link android.text.style.ReplacementSpan}. + * * @hide */ public class Styled { - private static float each(Canvas canvas, + /** + * Draws and/or measures a uniform run of text on a single line. No span of + * interest should start or end in the middle of this run (if not + * drawing, character spans that don't affect metrics can be ignored). + * Neither should the run direction change in the middle of the run. + * + * <p>The x position is the leading edge of the text. In a right-to-left + * paragraph, this will be to the right of the text to be drawn. Paint + * should not have an Align value other than LEFT or positioning will get + * confused. + * + * <p>On return, workPaint will reflect the original paint plus any + * modifications made by character styles on the run. + * + * <p>The returned width is signed and will be < 0 if the paragraph + * direction is right-to-left. + */ + private static float drawUniformRun(Canvas canvas, Spanned text, int start, int end, - int dir, boolean reverse, + int dir, boolean runIsRtl, float x, int top, int y, int bottom, Paint.FontMetricsInt fmi, TextPaint paint, TextPaint workPaint, - boolean needwid) { + boolean needWidth) { - boolean havewid = false; + boolean haveWidth = false; float ret = 0; CharacterStyle[] spans = text.getSpans(start, end, CharacterStyle.class); ReplacementSpan replacement = null; + // XXX: This shouldn't be modifying paint, only workPaint. + // However, the members belonging to TextPaint should have default + // values anyway. Better to ensure this in the Layout constructor. paint.bgColor = 0; paint.baselineShift = 0; workPaint.set(paint); @@ -65,9 +86,10 @@ public class Styled CharSequence tmp; int tmpstart, tmpend; - if (reverse) { + if (runIsRtl) { tmp = TextUtils.getReverse(text, start, end); tmpstart = 0; + // XXX: assumes getReverse doesn't change the length of the text tmpend = end - start; } else { tmp = text; @@ -86,9 +108,9 @@ public class Styled workPaint.setColor(workPaint.bgColor); workPaint.setStyle(Paint.Style.FILL); - if (!havewid) { + if (!haveWidth) { ret = workPaint.measureText(tmp, tmpstart, tmpend); - havewid = true; + haveWidth = true; } if (dir == Layout.DIR_RIGHT_TO_LEFT) @@ -101,18 +123,18 @@ public class Styled } if (dir == Layout.DIR_RIGHT_TO_LEFT) { - if (!havewid) { + if (!haveWidth) { ret = workPaint.measureText(tmp, tmpstart, tmpend); - havewid = true; + haveWidth = true; } canvas.drawText(tmp, tmpstart, tmpend, x - ret, y + workPaint.baselineShift, workPaint); } else { - if (needwid) { - if (!havewid) { + if (needWidth) { + if (!haveWidth) { ret = workPaint.measureText(tmp, tmpstart, tmpend); - havewid = true; + haveWidth = true; } } @@ -120,9 +142,9 @@ public class Styled x, y + workPaint.baselineShift, workPaint); } } else { - if (needwid && !havewid) { + if (needWidth && !haveWidth) { ret = workPaint.measureText(tmp, tmpstart, tmpend); - havewid = true; + haveWidth = true; } } } else { @@ -145,25 +167,28 @@ public class Styled } /** - * Return the advance widths for the characters in the string. - * See also {@link android.graphics.Paint#getTextWidths(CharSequence, int, int, float[])}. + * Returns the advance widths for a uniform left-to-right run of text with + * no style changes in the middle of the run. If any style is replacement + * text, the first character will get the width of the replacement and the + * remaining characters will get a width of 0. * - * @param paint The main {@link TextPaint} object. - * @param workPaint The {@link TextPaint} object used for temporal workspace. - * @param text The text to measure - * @param start The index of the first char to to measure - * @param end The end of the text slice to measure - * @param widths Array to receive the advance widths of the characters. - * Must be at least a large as (end - start). - * @param fmi FontMetrics information. Can be null. - * @return The actual number of widths returned. + * @param paint the paint, will not be modified + * @param workPaint a paint to modify; on return will reflect the original + * paint plus the effect of all spans on the run + * @param text the text + * @param start the start of the run + * @param end the limit of the run + * @param widths array to receive the advance widths of the characters. Must + * be at least a large as (end - start). + * @param fmi FontMetrics information; can be null + * @return the actual number of widths returned */ public static int getTextWidths(TextPaint paint, TextPaint workPaint, Spanned text, int start, int end, float[] widths, Paint.FontMetricsInt fmi) { - // Keep workPaint as is so that developers reuse the workspace. - MetricAffectingSpan[] spans = text.getSpans(start, end, MetricAffectingSpan.class); + MetricAffectingSpan[] spans = + text.getSpans(start, end, MetricAffectingSpan.class); ReplacementSpan replacement = null; workPaint.set(paint); @@ -186,7 +211,6 @@ public class Styled if (end > start) { widths[0] = wid; - for (int i = start + 1; i < end; i++) widths[i - start] = 0; } @@ -194,19 +218,42 @@ public class Styled return end - start; } - private static float foreach(Canvas canvas, + /** + * Renders and/or measures a directional run of text on a single line. + * Unlike {@link #drawUniformRun}, this can render runs that cross style + * boundaries. Returns the signed advance width, if requested. + * + * <p>The x position is the leading edge of the text. In a right-to-left + * paragraph, this will be to the right of the text to be drawn. Paint + * should not have an Align value other than LEFT or positioning will get + * confused. + * + * <p>This optimizes for unstyled text and so workPaint might not be + * modified by this call. + * + * <p>The returned advance width will be < 0 if the paragraph + * direction is right-to-left. + */ + private static float drawDirectionalRun(Canvas canvas, CharSequence text, int start, int end, - int dir, boolean reverse, + int dir, boolean runIsRtl, float x, int top, int y, int bottom, Paint.FontMetricsInt fmi, TextPaint paint, TextPaint workPaint, boolean needWidth) { - if (! (text instanceof Spanned)) { + + // XXX: It looks like all calls to this API match dir and runIsRtl, so + // having both parameters is redundant and confusing. + + // fast path for unstyled text + if (!(text instanceof Spanned)) { float ret = 0; - if (reverse) { + if (runIsRtl) { CharSequence tmp = TextUtils.getReverse(text, start, end); + // XXX: this assumes getReverse doesn't tweak the length of + // the text int tmpend = end - start; if (canvas != null || needWidth) @@ -227,15 +274,14 @@ public class Styled paint.getFontMetricsInt(fmi); } - return ret * dir; //Layout.DIR_RIGHT_TO_LEFT == -1 + return ret * dir; // Layout.DIR_RIGHT_TO_LEFT == -1 } float ox = x; - int asc = 0, desc = 0; - int ftop = 0, fbot = 0; + int minAscent = 0, maxDescent = 0, minTop = 0, maxBottom = 0; Spanned sp = (Spanned) text; - Class division; + Class<?> division; if (canvas == null) division = MetricAffectingSpan.class; @@ -246,20 +292,23 @@ public class Styled for (int i = start; i < end; i = next) { next = sp.nextSpanTransition(i, end, division); - x += each(canvas, sp, i, next, dir, reverse, + // XXX: if dir and runIsRtl were not the same, this would draw + // spans in the wrong order, but no one appears to call it this + // way. + x += drawUniformRun(canvas, sp, i, next, dir, runIsRtl, x, top, y, bottom, fmi, paint, workPaint, needWidth || next != end); if (fmi != null) { - if (fmi.ascent < asc) - asc = fmi.ascent; - if (fmi.descent > desc) - desc = fmi.descent; - - if (fmi.top < ftop) - ftop = fmi.top; - if (fmi.bottom > fbot) - fbot = fmi.bottom; + if (fmi.ascent < minAscent) + minAscent = fmi.ascent; + if (fmi.descent > maxDescent) + maxDescent = fmi.descent; + + if (fmi.top < minTop) + minTop = fmi.top; + if (fmi.bottom > maxBottom) + maxBottom = fmi.bottom; } } @@ -267,71 +316,78 @@ public class Styled if (start == end) { paint.getFontMetricsInt(fmi); } else { - fmi.ascent = asc; - fmi.descent = desc; - fmi.top = ftop; - fmi.bottom = fbot; + fmi.ascent = minAscent; + fmi.descent = maxDescent; + fmi.top = minTop; + fmi.bottom = maxBottom; } } return x - ox; } - + /** + * Draws a unidirectional run of text on a single line, and optionally + * returns the signed advance. Unlike drawDirectionalRun, the paragraph + * direction and run direction can be different. + */ /* package */ static float drawText(Canvas canvas, CharSequence text, int start, int end, - int direction, boolean reverse, + int dir, boolean runIsRtl, float x, int top, int y, int bottom, TextPaint paint, TextPaint workPaint, boolean needWidth) { - if ((direction == Layout.DIR_RIGHT_TO_LEFT && !reverse) || - (reverse && direction == Layout.DIR_LEFT_TO_RIGHT)) { - float ch = foreach(null, text, start, end, Layout.DIR_LEFT_TO_RIGHT, - false, 0, 0, 0, 0, null, paint, workPaint, - true); - - ch *= direction; // DIR_RIGHT_TO_LEFT == -1 - foreach(canvas, text, start, end, -direction, - reverse, x + ch, top, y, bottom, null, paint, + // XXX this logic is (dir == DIR_LEFT_TO_RIGHT) == runIsRtl + if ((dir == Layout.DIR_RIGHT_TO_LEFT && !runIsRtl) || + (runIsRtl && dir == Layout.DIR_LEFT_TO_RIGHT)) { + // TODO: this needs the real direction + float ch = drawDirectionalRun(null, text, start, end, + Layout.DIR_LEFT_TO_RIGHT, false, 0, 0, 0, 0, null, paint, + workPaint, true); + + ch *= dir; // DIR_RIGHT_TO_LEFT == -1 + drawDirectionalRun(canvas, text, start, end, -dir, + runIsRtl, x + ch, top, y, bottom, null, paint, workPaint, true); return ch; } - return foreach(canvas, text, start, end, direction, reverse, + return drawDirectionalRun(canvas, text, start, end, dir, runIsRtl, x, top, y, bottom, null, paint, workPaint, needWidth); } /** - * Draw the specified range of text, specified by start/end, with its origin at (x,y), - * in the specified Paint. The origin is interpreted based on the Align setting in the - * Paint. - * - * This method considers style information in the text - * (e.g. Even when text is an instance of {@link android.text.Spanned}, this method - * correctly draws the text). - * See also - * {@link android.graphics.Canvas#drawText(CharSequence, int, int, float, float, Paint)} - * and - * {@link android.graphics.Canvas#drawRect(float, float, float, float, Paint)}. + * Draws a run of text on a single line, with its + * origin at (x,y), in the specified Paint. The origin is interpreted based + * on the Align setting in the Paint. + * + * This method considers style information in the text (e.g. even when text + * is an instance of {@link android.text.Spanned}, this method correctly + * draws the text). See also + * {@link android.graphics.Canvas#drawText(CharSequence, int, int, float, + * float, Paint)} and + * {@link android.graphics.Canvas#drawRect(float, float, float, float, + * Paint)}. * - * @param canvas The target canvas. + * @param canvas The target canvas * @param text The text to be drawn * @param start The index of the first character in text to draw * @param end (end - 1) is the index of the last character in text to draw * @param direction The direction of the text. This must be - * {@link android.text.Layout#DIR_LEFT_TO_RIGHT} or - * {@link android.text.Layout#DIR_RIGHT_TO_LEFT}. + * {@link android.text.Layout#DIR_LEFT_TO_RIGHT} or + * {@link android.text.Layout#DIR_RIGHT_TO_LEFT}. * @param x The x-coordinate of origin for where to draw the text * @param top The top side of the rectangle to be drawn * @param y The y-coordinate of origin for where to draw the text * @param bottom The bottom side of the rectangle to be drawn * @param paint The main {@link TextPaint} object. - * @param workPaint The {@link TextPaint} object used for temporal workspace. - * @param needWidth If true, this method returns the width of drawn text. - * @return Width of the drawn text if needWidth is true. + * @param workPaint The {@link TextPaint} object used for temporal + * workspace. + * @param needWidth If true, this method returns the width of drawn text + * @return Width of the drawn text if needWidth is true */ public static float drawText(Canvas canvas, CharSequence text, int start, int end, @@ -341,34 +397,37 @@ public class Styled TextPaint workPaint, boolean needWidth) { // For safety. - direction = direction >= 0 ? Layout.DIR_LEFT_TO_RIGHT : Layout.DIR_RIGHT_TO_LEFT; - /* - * Hided "reverse" parameter since it is meaningless for external developers. - * Kept workPaint as is so that developers reuse the workspace. - */ + direction = direction >= 0 ? Layout.DIR_LEFT_TO_RIGHT + : Layout.DIR_RIGHT_TO_LEFT; + + // Hide runIsRtl parameter since it is meaningless for external + // developers. + // XXX: the runIsRtl probably ought to be the same as direction, then + // this could draw rtl text. return drawText(canvas, text, start, end, direction, false, x, top, y, bottom, paint, workPaint, needWidth); } /** - * Return the width of the text, considering style information in the text - * (e.g. Even when text is an instance of {@link android.text.Spanned}, this method - * correctly mesures the width of the text). + * Returns the width of a run of left-to-right text on a single line, + * considering style information in the text (e.g. even when text is an + * instance of {@link android.text.Spanned}, this method correctly measures + * the width of the text). * - * @param paint The main {@link TextPaint} object. - * @param workPaint The {@link TextPaint} object used for temporal workspace. - * @param text The text to measure - * @param start The index of the first character to start measuring + * @param paint the main {@link TextPaint} object; will not be modified + * @param workPaint the {@link TextPaint} object available for modification; + * will not necessarily be used + * @param text the text to measure + * @param start the index of the first character to start measuring * @param end 1 beyond the index of the last character to measure - * @param fmi FontMetrics information. Can be null - * @return The width of the text + * @param fmi FontMetrics information; can be null + * @return The width of the text */ public static float measureText(TextPaint paint, TextPaint workPaint, CharSequence text, int start, int end, Paint.FontMetricsInt fmi) { - // Keep workPaint as is so that developers reuse the workspace. - return foreach(null, text, start, end, + return drawDirectionalRun(null, text, start, end, Layout.DIR_LEFT_TO_RIGHT, false, 0, 0, 0, 0, fmi, paint, workPaint, true); } diff --git a/core/java/android/text/style/LeadingMarginSpan.java b/core/java/android/text/style/LeadingMarginSpan.java index cb55329d6642..6635ddb85d01 100644 --- a/core/java/android/text/style/LeadingMarginSpan.java +++ b/core/java/android/text/style/LeadingMarginSpan.java @@ -23,10 +23,44 @@ import android.text.Layout; import android.text.ParcelableSpan; import android.text.TextUtils; +/** + * A paragraph style affecting the leading margin. There can be multiple leading + * margin spans on a single paragraph; they will be rendered in order, each + * adding its margin to the ones before it. The leading margin is on the right + * for lines in a right-to-left paragraph. + */ public interface LeadingMarginSpan extends ParagraphStyle { + /** + * Returns the amount by which to adjust the leading margin. Positive values + * move away from the leading edge of the paragraph, negative values move + * towards it. + * + * @param first true if the request is for the first line of a paragraph, + * false for subsequent lines + * @return the offset for the margin. + */ public int getLeadingMargin(boolean first); + + /** + * Renders the leading margin. This is called before the margin has been + * adjusted by the value returned by {@link getLeadingMargin(boolean)}. + * + * @param c the canvas + * @param p the paint. The this should be left unchanged on exit. + * @param x the current position of the margin + * @param dir the base direction of the paragraph; if negative, the margin + * is to the right of the text, otherwise it is to the left. + * @param top the top of the line + * @param baseline the baseline of the line + * @param bottom the bottom of the line + * @param text the text + * @param start the start of the line + * @param end the end of the line + * @param first true if this is the first line of its paragraph + * @param layout the layout containing this line + */ public void drawLeadingMargin(Canvas c, Paint p, int x, int dir, int top, int baseline, int bottom, @@ -38,14 +72,29 @@ extends ParagraphStyle public int getLeadingMarginLineCount(); }; + /** + * The standard implementation of LeadingMarginSpan, which adjusts the + * margin but does not do any rendering. + */ public static class Standard implements LeadingMarginSpan, ParcelableSpan { private final int mFirst, mRest; + /** + * Constructor taking separate indents for the first and subsequent + * lines. + * + * @param first the indent for the first line of the paragraph + * @param rest the indent for the remaining lines of the paragraph + */ public Standard(int first, int rest) { mFirst = first; mRest = rest; } + /** + * Constructor taking an indent for all lines. + * @param every the indent of each line + */ public Standard(int every) { this(every, every); } diff --git a/core/java/android/text/style/TabStopSpan.java b/core/java/android/text/style/TabStopSpan.java index e5b76449a553..056642841815 100644 --- a/core/java/android/text/style/TabStopSpan.java +++ b/core/java/android/text/style/TabStopSpan.java @@ -16,14 +16,31 @@ package android.text.style; +/** + * Represents a single tab stop on a line. + */ public interface TabStopSpan extends ParagraphStyle { + /** + * Returns the offset of the tab stop from the leading margin of the + * line. + * @return the offset + */ public int getTabStop(); + /** + * The default implementation of TabStopSpan. + */ public static class Standard implements TabStopSpan { + /** + * Constructor. + * + * @param where the offset of the tab stop from the leading margin of + * the line + */ public Standard(int where) { mTab = where; } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 57fdd8b0660b..e5db12097fe5 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -1504,6 +1504,31 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * @hide */ private static final int PREPRESSED = 0x02000000; + + /** + * Always allow a user to overscroll this view, provided it is a + * view that can scroll. + */ + private static final int OVERSCROLL_ALWAYS = 0; + + /** + * Allow a user to overscroll this view only if the content is large + * enough to meaningfully scroll, provided it is a view that can scroll. + */ + private static final int OVERSCROLL_IF_CONTENT_SCROLLS = 1; + + /** + * Never allow a user to overscroll this view. + */ + private static final int OVERSCROLL_NEVER = 2; + + /** + * Controls the overscroll mode for this view. + * See {@link #overscrollBy(int, int, int, int, int, int, int, int)}, + * {@link #OVERSCROLL_ALWAYS}, {@link #OVERSCROLL_IF_CONTENT_SCROLLS}, + * and {@link #OVERSCROLL_NEVER}. + */ + private int mOverscrollMode = OVERSCROLL_ALWAYS; /** * The parent this view is attached to. @@ -2053,6 +2078,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility }); } break; + case R.styleable.View_overscrollMode: + mOverscrollMode = a.getInt(attr, OVERSCROLL_ALWAYS); + break; } } @@ -8573,43 +8601,59 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverscrollX, int maxOverscrollY) { - // Scale the scroll amount if we're in the dropoff zone - final int dropoffX = maxOverscrollX / 2; - final int dropoffLeft = -dropoffX; - final int dropoffRight = dropoffX + scrollRangeX; - int newScrollX; - if ((scrollX < dropoffLeft && deltaX < 0) || - (scrollX > dropoffRight && deltaX > 0)) { - newScrollX = scrollX + deltaX / 2; - } else { - newScrollX = scrollX + deltaX; - if (newScrollX > dropoffRight && deltaX > 0) { - int extra = newScrollX - dropoffRight; - newScrollX = dropoffRight + extra / 2; - } else if (newScrollX < dropoffLeft && deltaX < 0) { - int extra = newScrollX - dropoffLeft; - newScrollX = dropoffLeft + extra / 2; - } - } + final int overscrollMode = mOverscrollMode; + final boolean canScrollHorizontal = + computeHorizontalScrollRange() > computeHorizontalScrollExtent(); + final boolean canScrollVertical = + computeVerticalScrollRange() > computeVerticalScrollExtent(); + final boolean overscrollHorizontal = overscrollMode == OVERSCROLL_ALWAYS || + (overscrollMode == OVERSCROLL_IF_CONTENT_SCROLLS && canScrollHorizontal); + final boolean overscrollVertical = overscrollMode == OVERSCROLL_ALWAYS || + (overscrollMode == OVERSCROLL_IF_CONTENT_SCROLLS && canScrollVertical); - final int dropoffY = maxOverscrollY / 2; - final int dropoffTop = -dropoffY; - final int dropoffBottom = dropoffY + scrollRangeY; - int newScrollY; - if ((scrollY < dropoffTop && deltaY < 0) || - (scrollY > dropoffBottom && deltaY > 0)) { - newScrollY = scrollY + deltaY / 2; - } else { - newScrollY = scrollY + deltaY; - if (newScrollY > dropoffBottom && deltaY > 0) { - int extra = newScrollY - dropoffBottom; - newScrollY = dropoffBottom + extra / 2; - } else if (newScrollY < dropoffTop && deltaY < 0) { - int extra = newScrollY - dropoffTop; - newScrollY = dropoffTop + extra / 2; + int newScrollX = scrollX + deltaX; + if (overscrollHorizontal) { + // Scale the scroll amount if we're in the dropoff zone + final int dropoffX = maxOverscrollX / 2; + final int dropoffLeft = -dropoffX; + final int dropoffRight = dropoffX + scrollRangeX; + if ((scrollX < dropoffLeft && deltaX < 0) || + (scrollX > dropoffRight && deltaX > 0)) { + newScrollX = scrollX + deltaX / 2; + } else { + if (newScrollX > dropoffRight && deltaX > 0) { + int extra = newScrollX - dropoffRight; + newScrollX = dropoffRight + extra / 2; + } else if (newScrollX < dropoffLeft && deltaX < 0) { + int extra = newScrollX - dropoffLeft; + newScrollX = dropoffLeft + extra / 2; + } } + } else { + maxOverscrollX = 0; } + int newScrollY = scrollY + deltaY; + if (overscrollVertical) { + final int dropoffY = maxOverscrollY / 2; + final int dropoffTop = -dropoffY; + final int dropoffBottom = dropoffY + scrollRangeY; + if ((scrollY < dropoffTop && deltaY < 0) || + (scrollY > dropoffBottom && deltaY > 0)) { + newScrollY = scrollY + deltaY / 2; + } else { + if (newScrollY > dropoffBottom && deltaY > 0) { + int extra = newScrollY - dropoffBottom; + newScrollY = dropoffBottom + extra / 2; + } else if (newScrollY < dropoffTop && deltaY < 0) { + int extra = newScrollY - dropoffTop; + newScrollY = dropoffTop + extra / 2; + } + } + } else { + maxOverscrollY = 0; + } + // Clamp values if at the limits and record final int left = -maxOverscrollX; final int right = maxOverscrollX + scrollRangeX; @@ -8636,8 +8680,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility // Bump the device with some haptic feedback if we're at the edge // and didn't start there. - if ((clampedX && scrollX != left && scrollX != right) || - (clampedY && scrollY != top && scrollY != bottom)) { + if ((overscrollHorizontal && clampedX && scrollX != left && scrollX != right) || + (overscrollVertical && clampedY && scrollY != top && scrollY != bottom)) { performHapticFeedback(HapticFeedbackConstants.SCROLL_BARRIER); } diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java index 9e9cc7e508eb..d1ad61fa9bdc 100644 --- a/core/java/android/webkit/WebTextView.java +++ b/core/java/android/webkit/WebTextView.java @@ -116,7 +116,7 @@ import java.util.ArrayList; * @param webView The WebView that created this. */ /* package */ WebTextView(Context context, WebView webView) { - super(context); + super(context, null, com.android.internal.R.attr.webTextViewStyle); mWebView = webView; mMaxLength = -1; } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index ae6c6660d80c..967289247ba8 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -1102,7 +1102,7 @@ public class WebView extends AbsoluteLayout * methods may be called on a WebView after destroy. */ public void destroy() { - clearTextEntry(); + clearTextEntry(false); if (mWebViewCore != null) { // Set the handlers to null before destroying WebViewCore so no // more messages will be posted. @@ -1388,7 +1388,7 @@ public class WebView extends AbsoluteLayout arg.mUrl = url; arg.mExtraHeaders = extraHeaders; mWebViewCore.sendMessage(EventHub.LOAD_URL, arg); - clearTextEntry(); + clearTextEntry(false); } /** @@ -1417,7 +1417,7 @@ public class WebView extends AbsoluteLayout arg.mUrl = url; arg.mPostData = postData; mWebViewCore.sendMessage(EventHub.POST_URL, arg); - clearTextEntry(); + clearTextEntry(false); } else { loadUrl(url); } @@ -1473,7 +1473,7 @@ public class WebView extends AbsoluteLayout arg.mEncoding = encoding; arg.mFailUrl = failUrl; mWebViewCore.sendMessage(EventHub.LOAD_DATA, arg); - clearTextEntry(); + clearTextEntry(false); } /** @@ -1490,7 +1490,7 @@ public class WebView extends AbsoluteLayout * Reload the current url. */ public void reload() { - clearTextEntry(); + clearTextEntry(false); switchOutDrawHistory(); mWebViewCore.sendMessage(EventHub.RELOAD); } @@ -1577,7 +1577,7 @@ public class WebView extends AbsoluteLayout // null, and that will be the case mCertificate = null; if (steps != 0) { - clearTextEntry(); + clearTextEntry(false); mWebViewCore.sendMessage(EventHub.GO_BACK_FORWARD, steps, ignoreSnapshot ? 1 : 0); } @@ -1677,9 +1677,17 @@ public class WebView extends AbsoluteLayout && mWebTextView.hasFocus(); } - private void clearTextEntry() { + /** + * Remove the WebTextView. + * @param disableFocusController If true, send a message to webkit + * disabling the focus controller, so the caret stops blinking. + */ + private void clearTextEntry(boolean disableFocusController) { if (inEditingMode()) { mWebTextView.remove(); + if (disableFocusController) { + setFocusControllerInactive(); + } } } @@ -1713,7 +1721,7 @@ public class WebView extends AbsoluteLayout Log.w(LOGTAG, "This WebView doesn't support zoom."); return; } - clearTextEntry(); + clearTextEntry(false); if (getSettings().getBuiltInZoomControls()) { mZoomButtonsController.setVisible(true); } else { @@ -3105,11 +3113,26 @@ public class WebView extends AbsoluteLayout } } + private static class Metrics { + int mScrollX; + int mScrollY; + int mWidth; + int mHeight; + float mScale; + } + + private Metrics getViewMetrics() { + Metrics metrics = new Metrics(); + metrics.mScrollX = mScrollX; + metrics.mScrollY = computeVerticalScrollOffset(); + metrics.mWidth = getWidth(); + metrics.mHeight = getHeight() - getVisibleTitleHeight(); + metrics.mScale = mActualScale; + return metrics; + } + private void drawLayers(Canvas canvas) { if (mRootLayer != 0) { - int scrollY = computeVerticalScrollOffset(); - int viewHeight = getHeight() - getVisibleTitleHeight(); - // Currently for each draw we compute the animation values; // We may in the future decide to do that independently. if (nativeEvaluateLayersAnimations(mRootLayer)) { @@ -3119,9 +3142,7 @@ public class WebView extends AbsoluteLayout } // We can now draw the layers. - nativeDrawLayers(mRootLayer, mScrollX, scrollY, - getWidth(), viewHeight, - mActualScale, canvas); + nativeDrawLayers(mRootLayer, canvas); } } @@ -3209,7 +3230,15 @@ public class WebView extends AbsoluteLayout mWebViewCore.drawContentPicture(canvas, color, (animateZoom || mPreviewZoomOnly), animateScroll); - + boolean cursorIsInLayer = nativeCursorIsInLayer(); + if (drawCursorRing && !cursorIsInLayer) { + nativeDrawCursorRing(canvas); + } + // When the FindDialog is up, only draw the matches if we are not in + // the process of scrolling them into view. + if (mFindIsUp && !animateScroll) { + nativeDrawMatches(canvas); + } drawLayers(canvas); if (mNativeClass == 0) return; @@ -3232,12 +3261,7 @@ public class WebView extends AbsoluteLayout LONG_PRESS_TIMEOUT); } } - nativeDrawCursorRing(canvas); - } - // When the FindDialog is up, only draw the matches if we are not in - // the process of scrolling them into view. - if (mFindIsUp && !animateScroll) { - nativeDrawMatches(canvas); + if (cursorIsInLayer) nativeDrawCursorRing(canvas); } if (mFocusSizeChanged) { mFocusSizeChanged = false; @@ -3743,6 +3767,7 @@ public class WebView extends AbsoluteLayout } return true; } + clearTextEntry(true); nativeSetFollowedLink(true); if (!mCallbackProxy.uiOverrideUrlLoading(nativeCursorText())) { mWebViewCore.sendMessage(EventHub.CLICK, data.mFrame, @@ -3823,7 +3848,7 @@ public class WebView extends AbsoluteLayout @Override protected void onDetachedFromWindow() { - clearTextEntry(); + clearTextEntry(false); super.onDetachedFromWindow(); // Clean up the zoom controller mZoomButtonsController.setVisible(false); @@ -5810,7 +5835,7 @@ public class WebView extends AbsoluteLayout // is necessary for page loads driven by webkit, and in // particular when the user was on a password field, so // the WebTextView was visible. - clearTextEntry(); + clearTextEntry(false); // update the zoom buttons as the scale can be changed if (getSettings().getBuiltInZoomControls()) { updateZoomButtonsEnabled(); @@ -5932,7 +5957,7 @@ public class WebView extends AbsoluteLayout } break; case CLEAR_TEXT_ENTRY: - clearTextEntry(); + clearTextEntry(false); break; case INVAL_RECT_MSG_ID: { Rect r = (Rect)msg.obj; @@ -5952,6 +5977,7 @@ public class WebView extends AbsoluteLayout case SET_ROOT_LAYER_MSG_ID: { int oldLayer = mRootLayer; mRootLayer = msg.arg1; + nativeSetRootLayer(mRootLayer); if (oldLayer > 0) { nativeDestroyLayer(oldLayer); } @@ -6538,8 +6564,7 @@ public class WebView extends AbsoluteLayout */ private void sendMoveMouseIfLatest(boolean removeFocus) { if (removeFocus) { - clearTextEntry(); - setFocusControllerInactive(); + clearTextEntry(true); } mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE_IF_LATEST, cursorData()); @@ -6715,6 +6740,7 @@ public class WebView extends AbsoluteLayout /* package */ native boolean nativeCursorMatchesFocus(); private native boolean nativeCursorIntersects(Rect visibleRect); private native boolean nativeCursorIsAnchor(); + private native boolean nativeCursorIsInLayer(); private native boolean nativeCursorIsTextInput(); private native Point nativeCursorPosition(); private native String nativeCursorText(); @@ -6728,10 +6754,7 @@ public class WebView extends AbsoluteLayout private native void nativeDrawCursorRing(Canvas content); private native void nativeDestroyLayer(int layer); private native boolean nativeEvaluateLayersAnimations(int layer); - private native void nativeDrawLayers(int layer, - int scrollX, int scrollY, - int width, int height, - float scale, Canvas canvas); + private native void nativeDrawLayers(int layer, Canvas canvas); private native void nativeDrawMatches(Canvas canvas); private native void nativeDrawSelectionPointer(Canvas content, float scale, int x, int y, boolean extendSelection); @@ -6781,6 +6804,7 @@ public class WebView extends AbsoluteLayout private native void nativeSetFindIsUp(); private native void nativeSetFollowedLink(boolean followed); private native void nativeSetHeightCanMeasure(boolean measure); + private native void nativeSetRootLayer(int layer); private native int nativeTextGeneration(); // Never call this version except by updateCachedTextfield(String) - // we always want to pass in our generation number. diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 4c77bdc928a8..254efe792180 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -2589,7 +2589,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (trackMotionScroll(delta, delta)) { if (motionView != null) { // Tweak the scroll for how far we overshot - mScrollY -= delta - (motionView.getTop() - oldTop); + int overshoot = -(delta - (motionView.getTop() - oldTop)); + overscrollBy(0, overshoot, 0, mScrollY, 0, 0, 0, getOverscrollMax()); } float vel = scroller.getCurrVelocity(); if (delta > 0) { diff --git a/core/jni/android_server_BluetoothA2dpService.cpp b/core/jni/android_server_BluetoothA2dpService.cpp index 4eab4b396dd5..cf53a066e523 100644 --- a/core/jni/android_server_BluetoothA2dpService.cpp +++ b/core/jni/android_server_BluetoothA2dpService.cpp @@ -200,6 +200,38 @@ static jboolean resumeSinkNative(JNIEnv *env, jobject object, return JNI_FALSE; } +static jboolean avrcpVolumeUpNative(JNIEnv *env, jobject object, + jstring path) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + if (nat) { + const char *c_path = env->GetStringUTFChars(path, NULL); + bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat, + c_path, "org.bluez.Control", "VolumeUp", + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(path, c_path); + return ret ? JNI_TRUE : JNI_FALSE; + } +#endif + return JNI_FALSE; +} + +static jboolean avrcpVolumeDownNative(JNIEnv *env, jobject object, + jstring path) { +#ifdef HAVE_BLUETOOTH + LOGV(__FUNCTION__); + if (nat) { + const char *c_path = env->GetStringUTFChars(path, NULL); + bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat, + c_path, "org.bluez.Control", "VolumeDown", + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(path, c_path); + return ret ? JNI_TRUE : JNI_FALSE; + } +#endif + return JNI_FALSE; +} + #ifdef HAVE_BLUETOOTH DBusHandlerResult a2dp_event_filter(DBusMessage *msg, JNIEnv *env) { DBusError err; @@ -267,6 +299,7 @@ void onConnectSinkResult(DBusMessage *msg, void *user, void *n) { free(user); } + #endif @@ -281,6 +314,8 @@ static JNINativeMethod sMethods[] = { {"resumeSinkNative", "(Ljava/lang/String;)Z", (void*)resumeSinkNative}, {"getSinkPropertiesNative", "(Ljava/lang/String;)[Ljava/lang/Object;", (void *)getSinkPropertiesNative}, + {"avrcpVolumeUpNative", "(Ljava/lang/String;)Z", (void*)avrcpVolumeUpNative}, + {"avrcpVolumeDownNative", "(Ljava/lang/String;)Z", (void*)avrcpVolumeDownNative}, }; int register_android_server_BluetoothA2dpService(JNIEnv *env) { diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index ff8cdc9d392b..ff9398417b20 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -396,6 +396,8 @@ <attr name="tabWidgetStyle" format="reference" /> <!-- Default TextView style. --> <attr name="textViewStyle" format="reference" /> + <!-- Default WebTextView style. --> + <attr name="webTextViewStyle" format="reference" /> <!-- Default WebView style. --> <attr name="webViewStyle" format="reference" /> <!-- Default style for drop down items. --> @@ -1305,6 +1307,19 @@ <code>public void sayHello(View v)</code> method of your context (typically, your Activity). --> <attr name="onClick" format="string" /> + + <!-- Defines overscrolling behavior. This property is used only if the + View is scrollable. Overscrolling is the ability for the user to + scroll a View beyond its content boundaries into empty space. --> + <attr name="overscrollMode"> + <!-- Always allow the user to overscroll the content. --> + <enum name="always" value="0" /> + <!-- Only allow the user to overscroll content if the content is large + enough to meaningfully scroll. --> + <enum name="ifContentScrolls" value="1" /> + <!-- Never overscroll. --> + <enum name="never" value="2" /> + </attr> </declare-styleable> <!-- Attributes that can be used with a {@link android.view.ViewGroup} or any diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 70bc0000fbcd..33b509bd1fc7 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -83,7 +83,7 @@ never be encrypted if an Encrypted File System solution is enabled. Specifically, this is an "opt-out" feature, meaning that, by default, user data will be encrypted if the EFS feature - is enabled.--> + is enabled. --> <attr name="neverEncrypt" format="boolean" /> <!-- Option to indicate this application is only for testing purposes. diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 3c6338ee141a..b3343375e90f 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1227,9 +1227,10 @@ Resources proposed for Froyo. =============================================================== --> <eat-comment /> - <public type="attr" name="neverEncrypt" id="0x010102b7" /> - <public type="attr" name="installLocation" id="0x010102b8" /> - <public type="attr" name="safeMode" id="0x010102b9" /> + <public type="attr" name="installLocation" id="0x010102b7" /> + <public type="attr" name="safeMode" id="0x010102b8" /> + <public type="attr" name="webTextViewStyle" id="0x010102b9" /> + <public type="attr" name="overscrollMode" id="0x010102ba" /> <public type="anim" name="cycle_interpolator" id="0x010a000c" /> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 58e9f4590ab1..128766975fd0 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -503,6 +503,17 @@ <item name="android:scrollbars">horizontal|vertical</item> </style> + <style name="Widget.WebTextView"> + <item name="android:focusable">true</item> + <item name="android:focusableInTouchMode">true</item> + <item name="android:clickable">true</item> + <item name="android:completionHintView">@android:layout/simple_dropdown_item_1line</item> + <item name="android:textAppearance">?android:attr/textAppearanceLargeInverse</item> + <item name="android:completionThreshold">2</item> + <item name="android:dropDownSelector">@android:drawable/list_selector_background</item> + <item name="android:popupBackground">@android:drawable/spinner_dropdown_background</item> + </style> + <style name="Widget.TabWidget"> <item name="android:textAppearance">@style/TextAppearance.Widget.TabWidget</item> <item name="ellipsize">marquee</item> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index d7a3be384578..9e19c57c7949 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -170,6 +170,7 @@ <item name="starStyle">@android:style/Widget.CompoundButton.Star</item> <item name="tabWidgetStyle">@android:style/Widget.TabWidget</item> <item name="textViewStyle">@android:style/Widget.TextView</item> + <item name="webTextViewStyle">@android:style/Widget.WebTextView</item> <item name="webViewStyle">@android:style/Widget.WebView</item> <item name="dropDownItemStyle">@android:style/Widget.DropDownItem</item> <item name="spinnerDropDownItemStyle">@android:style/Widget.DropDownItem.Spinner</item> diff --git a/core/tests/coretests/src/android/app/SearchManagerTest.java b/core/tests/coretests/src/android/app/SearchManagerTest.java index fc7e78717788..08b7f60c368f 100644 --- a/core/tests/coretests/src/android/app/SearchManagerTest.java +++ b/core/tests/coretests/src/android/app/SearchManagerTest.java @@ -120,23 +120,6 @@ public class SearchManagerTest extends ActivityInstrumentationTestCase2<LocalAct assertSame(searchManager1, searchManager2 ); } - @MediumTest - public void testSearchables() { - SearchManager searchManager = (SearchManager) - mContext.getSystemService(Context.SEARCH_SERVICE); - SearchableInfo si; - - si = searchManager.getSearchableInfo(SEARCHABLE_ACTIVITY, false); - assertNotNull(si); - assertFalse(searchManager.isDefaultSearchable(si)); - si = searchManager.getSearchableInfo(SEARCHABLE_ACTIVITY, true); - assertNotNull(si); - assertTrue(searchManager.isDefaultSearchable(si)); - si = searchManager.getSearchableInfo(null, true); - assertNotNull(si); - assertTrue(searchManager.isDefaultSearchable(si)); - } - /** * Tests that startSearch() can be called multiple times without stopSearch() * in between. diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 70c27c25a1f6..32c5c23fd976 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -87,10 +87,11 @@ public class AudioManager { /** * @hide Broadcast intent when the volume for a particular stream type changes. - * Includes the stream and the new volume + * Includes the stream, the new volume and previous volumes * * @see #EXTRA_VOLUME_STREAM_TYPE * @see #EXTRA_VOLUME_STREAM_VALUE + * @see #EXTRA_PREV_VOLUME_STREAM_VALUE */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String VOLUME_CHANGED_ACTION = "android.media.VOLUME_CHANGED_ACTION"; @@ -126,6 +127,12 @@ public class AudioManager { public static final String EXTRA_VOLUME_STREAM_VALUE = "android.media.EXTRA_VOLUME_STREAM_VALUE"; + /** + * @hide The previous volume associated with the stream for the volume changed intent. + */ + public static final String EXTRA_PREV_VOLUME_STREAM_VALUE = + "android.media.EXTRA_PREV_VOLUME_STREAM_VALUE"; + /** The audio stream for phone calls */ public static final int STREAM_VOICE_CALL = AudioSystem.STREAM_VOICE_CALL; /** The audio stream for system sounds */ diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index bde8a473cfce..668917e6ef8d 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -403,30 +403,34 @@ public class AudioService extends IAudioService.Stub { // UI mVolumePanel.postVolumeChanged(streamType, flags); // Broadcast Intent - sendVolumeUpdate(streamType); + sendVolumeUpdate(streamType, oldIndex, streamState.mIndex); } /** @see AudioManager#setStreamVolume(int, int, int) */ public void setStreamVolume(int streamType, int index, int flags) { ensureValidStreamType(streamType); + + final int oldIndex = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex; + index = rescaleIndex(index * 10, streamType, STREAM_VOLUME_ALIAS[streamType]); setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true); // UI, etc. mVolumePanel.postVolumeChanged(streamType, flags); // Broadcast Intent - sendVolumeUpdate(streamType); + sendVolumeUpdate(streamType, oldIndex, index); } - private void sendVolumeUpdate(int streamType) { + private void sendVolumeUpdate(int streamType, int oldIndex, int index) { + oldIndex = (oldIndex + 5) / 10; + index = (index + 5) / 10; + Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION); intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType); - intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, getStreamVolume(streamType)); + intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index); + intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex); - // Currently, sending the intent only when the stream is BLUETOOTH_SCO - if (streamType == AudioSystem.STREAM_BLUETOOTH_SCO) { - mContext.sendBroadcast(intent); - } + mContext.sendBroadcast(intent); } /** diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp index 2a65b0d5c7f2..2012b3dedd25 100644 --- a/media/jni/soundpool/SoundPool.cpp +++ b/media/jni/soundpool/SoundPool.cpp @@ -296,7 +296,7 @@ void SoundPool::pause(int channelID) void SoundPool::autoPause() { - LOGV("pauseAll()"); + LOGV("autoPause()"); Mutex::Autolock lock(&mLock); for (int i = 0; i < mMaxChannels; ++i) { SoundChannel* channel = &mChannelPool[i]; @@ -316,7 +316,7 @@ void SoundPool::resume(int channelID) void SoundPool::autoResume() { - LOGV("pauseAll()"); + LOGV("autoResume()"); Mutex::Autolock lock(&mLock); for (int i = 0; i < mMaxChannels; ++i) { SoundChannel* channel = &mChannelPool[i]; diff --git a/packages/SettingsProvider/AndroidManifest.xml b/packages/SettingsProvider/AndroidManifest.xml index c92b9d7fb564..dd0d064ceeef 100644 --- a/packages/SettingsProvider/AndroidManifest.xml +++ b/packages/SettingsProvider/AndroidManifest.xml @@ -7,8 +7,9 @@ android:process="system" android:backupAgent="SettingsBackupAgent" android:killAfterRestore="false" - android:icon="@drawable/ic_launcher_settings" - android:neverEncrypt="true"> + android:icon="@drawable/ic_launcher_settings"> + + <!-- todo add: android:neverEncrypt="true" --> <provider android:name="SettingsProvider" android:authorities="settings" android:multiprocess="false" diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index 0a6c72e0dab3..a267e0fa3ef6 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -24,7 +24,7 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import android.app.Activity; -import android.app.DeviceAdmin; +import android.app.DeviceAdminReceiver; import android.app.DeviceAdminInfo; import android.app.DevicePolicyManager; import android.app.IDevicePolicyManager; @@ -216,7 +216,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver); if (admin != null) { sendAdminCommandLocked(admin, - DeviceAdmin.ACTION_DEVICE_ADMIN_DISABLED); + DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED); // XXX need to wait for it to complete. mAdminList.remove(admin); mAdminMap.remove(adminReceiver); @@ -393,7 +393,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mAdminList.add(admin); saveSettingsLocked(); sendAdminCommandLocked(admin, - DeviceAdmin.ACTION_DEVICE_ADMIN_ENABLED); + DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED); } finally { Binder.restoreCallingIdentity(ident); } @@ -709,7 +709,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } return; } - Intent intent = new Intent(DeviceAdmin.ACTION_DEVICE_ADMIN_DISABLE_REQUESTED); + Intent intent = new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLE_REQUESTED); intent.setComponent(admin.info.getComponent()); mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() { @Override @@ -738,7 +738,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mFailedPasswordAttempts = 0; saveSettingsLocked(); } - sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_CHANGED, + sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); } finally { Binder.restoreCallingIdentity(ident); @@ -760,7 +760,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (max > 0 && mFailedPasswordAttempts >= max) { wipeDataLocked(0); } - sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_FAILED, + sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_FAILED, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN); } finally { Binder.restoreCallingIdentity(ident); @@ -778,7 +778,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { try { mFailedPasswordAttempts = 0; saveSettingsLocked(); - sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_SUCCEEDED, + sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_SUCCEEDED, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN); } finally { Binder.restoreCallingIdentity(ident); diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java index d2804750059b..b566fb7eb8a5 100644 --- a/services/java/com/android/server/DockObserver.java +++ b/services/java/com/android/server/DockObserver.java @@ -18,10 +18,12 @@ package com.android.server; import android.app.Activity; import android.app.ActivityManagerNative; +import android.app.AlarmManager; import android.app.IActivityManager; import android.app.IUiModeManager; import android.app.KeyguardManager; import android.app.StatusBarManager; +import android.app.PendingIntent; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.ActivityNotFoundException; @@ -29,11 +31,18 @@ import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.res.Configuration; +import android.location.Criteria; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.location.LocationProvider; import android.os.Binder; import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; +import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.RemoteException; @@ -42,6 +51,8 @@ import android.os.SystemClock; import android.os.UEventObserver; import android.provider.Settings; import android.server.BluetoothService; +import android.text.format.DateUtils; +import android.text.format.Time; import android.util.Log; import com.android.internal.widget.LockPatternUtils; @@ -59,10 +70,26 @@ class DockObserver extends UEventObserver { private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock"; private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state"; + private static final String KEY_LAST_UPDATE_INTERVAL = "LAST_UPDATE_INTERVAL"; + + private static final int MSG_DOCK_STATE = 0; + private static final int MSG_UPDATE_TWILIGHT = 1; + private static final int MSG_ENABLE_LOCATION_UPDATES = 2; + public static final int MODE_NIGHT_AUTO = Configuration.UI_MODE_NIGHT_MASK >> 4; public static final int MODE_NIGHT_NO = Configuration.UI_MODE_NIGHT_NO >> 4; public static final int MODE_NIGHT_YES = Configuration.UI_MODE_NIGHT_YES >> 4; + private static final long LOCATION_UPDATE_MS = 30 * DateUtils.MINUTE_IN_MILLIS; + private static final float LOCATION_UPDATE_DISTANCE_METER = 1000 * 20; + private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MIN = 5000; + private static final long LOCATION_UPDATE_ENABLE_INTERVAL_MAX = 5 * DateUtils.MINUTE_IN_MILLIS; + // velocity for estimating a potential movement: 150km/h + private static final float MAX_VELOCITY_M_MS = 150 / 3600; + private static final double FACTOR_GMT_OFFSET_LONGITUDE = 1000.0 * 360.0 / DateUtils.DAY_IN_MILLIS; + + private static final String ACTION_UPDATE_NIGHT_MODE = "com.android.server.action.UPDATE_NIGHT_MODE"; + private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; private int mPreviousDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; @@ -79,7 +106,11 @@ class DockObserver extends UEventObserver { private boolean mKeyguardDisabled; private LockPatternUtils mLockPatternUtils; - private StatusBarManager mStatusBarManager; + private AlarmManager mAlarmManager; + + private LocationManager mLocationManager; + private Location mLocation; + private StatusBarManager mStatusBarManager; // The broadcast receiver which receives the result of the ordered broadcast sent when // the dock state changes. The original ordered broadcast is sent with an initial result @@ -115,6 +146,81 @@ class DockObserver extends UEventObserver { } }; + private final BroadcastReceiver mTwilightUpdateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (mCarModeEnabled && mNightMode == MODE_NIGHT_AUTO) { + mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT); + } + } + }; + + private final LocationListener mLocationListener = new LocationListener() { + + public void onLocationChanged(Location location) { + updateLocation(location); + } + + public void onProviderDisabled(String provider) { + } + + public void onProviderEnabled(String provider) { + } + + public void onStatusChanged(String provider, int status, Bundle extras) { + // If the network location is no longer available check for a GPS fix + // and try to update the location. + if (provider == LocationManager.NETWORK_PROVIDER && + status != LocationProvider.AVAILABLE) { + updateLocation(mLocation); + } + } + + private void updateLocation(Location location) { + location = DockObserver.chooseBestLocation(location, + mLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER)); + if (hasMoved(location)) { + synchronized (this) { + mLocation = location; + } + if (mCarModeEnabled && mNightMode == MODE_NIGHT_AUTO) { + mHandler.sendEmptyMessage(MSG_UPDATE_TWILIGHT); + } + } + } + + /* + * The user has moved if the accuracy circles of the two locations + * don't overlap. + */ + private boolean hasMoved(Location location) { + if (location == null) { + return false; + } + if (mLocation == null) { + return true; + } + + /* if new location is older than the current one, the devices hasn't + * moved. + */ + if (location.getTime() < mLocation.getTime()) { + return false; + } + + /* Get the distance between the two points */ + float distance = mLocation.distanceTo(location); + + /* Get the total accuracy radius for both locations */ + float totalAccuracy = mLocation.getAccuracy() + location.getAccuracy(); + + /* If the distance is greater than the combined accuracy of the two + * points then they can't overlap and hence the user has moved. + */ + return distance > totalAccuracy; + } + }; + public DockObserver(Context context, PowerManagerService pm) { mContext = context; mPowerManager = pm; @@ -123,6 +229,13 @@ class DockObserver extends UEventObserver { ServiceManager.addService("uimode", mBinder); + mAlarmManager = + (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); + mLocationManager = + (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE); + mContext.registerReceiver(mTwilightUpdateReceiver, + new IntentFilter(ACTION_UPDATE_NIGHT_MODE)); + startObserving(DOCK_UEVENT_MATCH); } @@ -190,83 +303,161 @@ class DockObserver extends UEventObserver { update(); } mSystemReady = true; + mHandler.sendEmptyMessage(MSG_ENABLE_LOCATION_UPDATES); } } private final void update() { - mHandler.sendEmptyMessage(0); + mHandler.sendEmptyMessage(MSG_DOCK_STATE); } private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { - synchronized (this) { - Log.i(TAG, "Dock state changed: " + mDockState); + switch (msg.what) { + case MSG_DOCK_STATE: + synchronized (this) { + Log.i(TAG, "Dock state changed: " + mDockState); - final ContentResolver cr = mContext.getContentResolver(); + final ContentResolver cr = mContext.getContentResolver(); - if (Settings.Secure.getInt(cr, - Settings.Secure.DEVICE_PROVISIONED, 0) == 0) { - Log.i(TAG, "Device not provisioned, skipping dock broadcast"); - return; - } - // Pack up the values and broadcast them to everyone - Intent intent = new Intent(Intent.ACTION_DOCK_EVENT); - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); - if (mCarModeEnabled && mDockState != Intent.EXTRA_DOCK_STATE_CAR) { - // Pretend to be in DOCK_STATE_CAR. - intent.putExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_CAR); - } else { - intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState); - } - intent.putExtra(Intent.EXTRA_CAR_MODE_ENABLED, mCarModeEnabled); - - // Check if this is Bluetooth Dock - String address = BluetoothService.readDockBluetoothAddress(); - if (address != null) - intent.putExtra(BluetoothDevice.EXTRA_DEVICE, - BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address)); - - // User feedback to confirm dock connection. Particularly - // useful for flaky contact pins... - if (Settings.System.getInt(cr, - Settings.System.DOCK_SOUNDS_ENABLED, 1) == 1) - { - String whichSound = null; - if (mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) { - if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) { - whichSound = Settings.System.DESK_UNDOCK_SOUND; - } else if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_CAR) { - whichSound = Settings.System.CAR_UNDOCK_SOUND; + if (Settings.Secure.getInt(cr, + Settings.Secure.DEVICE_PROVISIONED, 0) == 0) { + Log.i(TAG, "Device not provisioned, skipping dock broadcast"); + return; } - } else { - if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) { - whichSound = Settings.System.DESK_DOCK_SOUND; - } else if (mDockState == Intent.EXTRA_DOCK_STATE_CAR) { - whichSound = Settings.System.CAR_DOCK_SOUND; + // Pack up the values and broadcast them to everyone + Intent intent = new Intent(Intent.ACTION_DOCK_EVENT); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); + if (mCarModeEnabled && mDockState != Intent.EXTRA_DOCK_STATE_CAR) { + // Pretend to be in DOCK_STATE_CAR. + intent.putExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_CAR); + } else { + intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState); } - } + intent.putExtra(Intent.EXTRA_CAR_MODE_ENABLED, mCarModeEnabled); + + // Check if this is Bluetooth Dock + String address = BluetoothService.readDockBluetoothAddress(); + if (address != null) + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, + BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address)); + + // User feedback to confirm dock connection. Particularly + // useful for flaky contact pins... + if (Settings.System.getInt(cr, + Settings.System.DOCK_SOUNDS_ENABLED, 1) == 1) + { + String whichSound = null; + if (mDockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) { + if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_DESK) { + whichSound = Settings.System.DESK_UNDOCK_SOUND; + } else if (mPreviousDockState == Intent.EXTRA_DOCK_STATE_CAR) { + whichSound = Settings.System.CAR_UNDOCK_SOUND; + } + } else { + if (mDockState == Intent.EXTRA_DOCK_STATE_DESK) { + whichSound = Settings.System.DESK_DOCK_SOUND; + } else if (mDockState == Intent.EXTRA_DOCK_STATE_CAR) { + whichSound = Settings.System.CAR_DOCK_SOUND; + } + } - if (whichSound != null) { - final String soundPath = Settings.System.getString(cr, whichSound); - if (soundPath != null) { - final Uri soundUri = Uri.parse("file://" + soundPath); - if (soundUri != null) { - final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri); - if (sfx != null) sfx.play(); + if (whichSound != null) { + final String soundPath = Settings.System.getString(cr, whichSound); + if (soundPath != null) { + final Uri soundUri = Uri.parse("file://" + soundPath); + if (soundUri != null) { + final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri); + if (sfx != null) sfx.play(); + } + } } } + + // Send the ordered broadcast; the result receiver will receive after all + // broadcasts have been sent. If any broadcast receiver changes the result + // code from the initial value of RESULT_OK, then the result receiver will + // not launch the corresponding dock application. This gives apps a chance + // to override the behavior and stay in their app even when the device is + // placed into a dock. + mContext.sendStickyOrderedBroadcast( + intent, mResultReceiver, null, Activity.RESULT_OK, null, null); + } - } + break; + case MSG_UPDATE_TWILIGHT: + synchronized (this) { + if (mCarModeEnabled && mLocation != null && mNightMode == MODE_NIGHT_AUTO) { + try { + DockObserver.this.updateTwilight(); + } catch (RemoteException e) { + Log.w(TAG, "Unable to change night mode.", e); + } + } + } + break; + case MSG_ENABLE_LOCATION_UPDATES: + if (mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) { + mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, + LOCATION_UPDATE_MS, LOCATION_UPDATE_DISTANCE_METER, mLocationListener); + retrieveLocation(); + if (mLocation != null) { + try { + DockObserver.this.updateTwilight(); + } catch (RemoteException e) { + Log.w(TAG, "Unable to change night mode.", e); + } + } + } else { + long interval = msg.getData().getLong(KEY_LAST_UPDATE_INTERVAL); + interval *= 1.5; + if (interval == 0) { + interval = LOCATION_UPDATE_ENABLE_INTERVAL_MIN; + } else if (interval > LOCATION_UPDATE_ENABLE_INTERVAL_MAX) { + interval = LOCATION_UPDATE_ENABLE_INTERVAL_MAX; + } + Bundle bundle = new Bundle(); + bundle.putLong(KEY_LAST_UPDATE_INTERVAL, interval); + Message newMsg = mHandler.obtainMessage(MSG_ENABLE_LOCATION_UPDATES); + newMsg.setData(bundle); + mHandler.sendMessageDelayed(newMsg, interval); + } + break; + } + } - // Send the ordered broadcast; the result receiver will receive after all - // broadcasts have been sent. If any broadcast receiver changes the result - // code from the initial value of RESULT_OK, then the result receiver will - // not launch the corresponding dock application. This gives apps a chance - // to override the behavior and stay in their app even when the device is - // placed into a dock. - mContext.sendStickyOrderedBroadcast( - intent, mResultReceiver, null, Activity.RESULT_OK, null, null); + private void retrieveLocation() { + final Location gpsLocation = + mLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); + Location location; + Criteria criteria = new Criteria(); + criteria.setSpeedRequired(false); + criteria.setAltitudeRequired(false); + criteria.setBearingRequired(false); + final String bestProvider = mLocationManager.getBestProvider(criteria, true); + if (LocationManager.GPS_PROVIDER.equals(bestProvider)) { + location = gpsLocation; + } else { + location = DockObserver.chooseBestLocation(gpsLocation, + mLocationManager.getLastKnownLocation(bestProvider)); + } + // In the case there is no location available (e.g. GPS fix or network location + // is not available yet), the longitude of the location is estimated using the timezone, + // latitude and accuracy are set to get a good average. + if (location == null) { + Time currentTime = new Time(); + currentTime.set(System.currentTimeMillis()); + double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE * currentTime.gmtoff + - (currentTime.isDst > 0 ? 3600 : 0); + location = new Location("fake"); + location.setLongitude(lngOffset); + location.setLatitude(59.95); + location.setAccuracy(417000.0f); + location.setTime(System.currentTimeMillis()); + } + synchronized (this) { + mLocation = location; } } }; @@ -274,10 +465,15 @@ class DockObserver extends UEventObserver { private void setCarMode(boolean enabled) throws RemoteException { mCarModeEnabled = enabled; if (enabled) { - setMode(Configuration.UI_MODE_TYPE_CAR, mNightMode); + if (mNightMode == MODE_NIGHT_AUTO) { + updateTwilight(); + } else { + setMode(Configuration.UI_MODE_TYPE_CAR, mNightMode << 4); + } } else { // Disabling the car mode clears the night mode. - setMode(Configuration.UI_MODE_TYPE_NORMAL, MODE_NIGHT_NO); + setMode(Configuration.UI_MODE_TYPE_NORMAL, + Configuration.UI_MODE_NIGHT_UNDEFINED); } if (mStatusBarManager == null) { @@ -290,38 +486,114 @@ class DockObserver extends UEventObserver { // the status bar should be totally disabled, the calls below will // have no effect until the device is unlocked. if (mStatusBarManager != null) { + long ident = Binder.clearCallingIdentity(); mStatusBarManager.disable(enabled ? StatusBarManager.DISABLE_NOTIFICATION_TICKER : StatusBarManager.DISABLE_NONE); + Binder.restoreCallingIdentity(ident); } } private void setMode(int modeType, int modeNight) throws RemoteException { + long ident = Binder.clearCallingIdentity(); final IActivityManager am = ActivityManagerNative.getDefault(); Configuration config = am.getConfiguration(); - if (config.uiMode != (modeType | modeNight)) { config.uiMode = modeType | modeNight; - long ident = Binder.clearCallingIdentity(); am.updateConfiguration(config); - Binder.restoreCallingIdentity(ident); } + Binder.restoreCallingIdentity(ident); } private void setNightMode(int mode) throws RemoteException { - mNightMode = mode; - switch (mode) { - case MODE_NIGHT_NO: - case MODE_NIGHT_YES: - setMode(Configuration.UI_MODE_TYPE_CAR, mode << 4); - break; - case MODE_NIGHT_AUTO: - // FIXME: not yet supported, this functionality will be - // added in a separate change. - break; - default: - setMode(Configuration.UI_MODE_TYPE_CAR, MODE_NIGHT_NO << 4); - break; + if (mNightMode != mode) { + mNightMode = mode; + switch (mode) { + case MODE_NIGHT_NO: + case MODE_NIGHT_YES: + setMode(Configuration.UI_MODE_TYPE_CAR, mode << 4); + break; + case MODE_NIGHT_AUTO: + long ident = Binder.clearCallingIdentity(); + updateTwilight(); + Binder.restoreCallingIdentity(ident); + break; + default: + setMode(Configuration.UI_MODE_TYPE_CAR, MODE_NIGHT_NO << 4); + break; + } + } + } + + private void updateTwilight() throws RemoteException { + synchronized (this) { + if (mLocation == null) { + return; + } + final long currentTime = System.currentTimeMillis(); + int nightMode; + // calculate current twilight + TwilightCalculator tw = new TwilightCalculator(); + tw.calculateTwilight(currentTime, + mLocation.getLatitude(), mLocation.getLongitude()); + if (tw.mState == TwilightCalculator.DAY) { + nightMode = MODE_NIGHT_NO; + } else { + nightMode = MODE_NIGHT_YES; + } + + // schedule next update + final int mLastTwilightState = tw.mState; + // add some extra time to be on the save side. + long nextUpdate = DateUtils.MINUTE_IN_MILLIS; + if (currentTime > tw.mSunset) { + // next update should be on the following day + tw.calculateTwilight(currentTime + + DateUtils.DAY_IN_MILLIS, mLocation.getLatitude(), + mLocation.getLongitude()); + } + + if (mLastTwilightState == TwilightCalculator.NIGHT) { + nextUpdate += tw.mSunrise; + } else { + nextUpdate += tw.mSunset; + } + + Intent updateIntent = new Intent(ACTION_UPDATE_NIGHT_MODE); + PendingIntent pendingIntent = + PendingIntent.getBroadcast(mContext, 0, updateIntent, 0); + mAlarmManager.cancel(pendingIntent); + mAlarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent); + + // set current mode + setMode(Configuration.UI_MODE_TYPE_CAR, nightMode << 4); + } + } + + /** + * Check which of two locations is better by comparing the distance a device + * could have cover since the last timestamp of the location. + * + * @param location first location + * @param otherLocation second location + * @return one of the two locations + */ + protected static Location chooseBestLocation(Location location, Location otherLocation) { + if (location == null) { + return otherLocation; + } + if (otherLocation == null) { + return location; + } + final long currentTime = System.currentTimeMillis(); + float gpsPotentialMove = MAX_VELOCITY_M_MS * (currentTime - location.getTime()) + + location.getAccuracy(); + float otherPotentialMove = MAX_VELOCITY_M_MS * (currentTime - otherLocation.getTime()) + + otherLocation.getAccuracy(); + if (gpsPotentialMove < otherPotentialMove) { + return location; + } else { + return otherLocation; } } diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java index cf083c04f5f5..ceae973199e1 100644 --- a/services/java/com/android/server/NativeDaemonConnector.java +++ b/services/java/com/android/server/NativeDaemonConnector.java @@ -259,8 +259,12 @@ final class NativeDaemonConnector implements Runnable { rdata[idx++] = line.substring(tok[0].length() + 1); } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) { if (LOCAL_LOGD) Log.d(TAG, String.format("List terminated with {%s}", line)); - if (i != rsp.size()) { - Log.w(TAG, String.format("Recv'd %d lines after list term", (rsp.size()-i))); + int last = rsp.size() -1; + if (i != last) { + Log.w(TAG, String.format("Recv'd %d lines after end of list {%s}", (last-i), cmd)); + for (int j = i; j <= last ; j++) { + Log.w(TAG, String.format("ExtraData <%s>", rsp.get(i))); + } } return rdata; } else { diff --git a/services/java/com/android/server/TwilightCalculator.java b/services/java/com/android/server/TwilightCalculator.java new file mode 100644 index 000000000000..a8f67d88bbbd --- /dev/null +++ b/services/java/com/android/server/TwilightCalculator.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2010 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 com.android.server; + +import android.text.format.DateUtils; +import android.util.FloatMath; + +/** @hide */ +public class TwilightCalculator { + + /** Value of {@link #mState} if it is currently day */ + public static final int DAY = 0; + + /** Value of {@link #mState} if it is currently night */ + public static final int NIGHT = 1; + + private static final float DEGREES_TO_RADIANS = (float) (Math.PI / 180.0f); + + // element for calculating solar transit. + private static final float J0 = 0.0009f; + + // correction for civil twilight + private static final float ALTIDUTE_CORRECTION_CIVIL_TWILIGHT = -0.104719755f; + + // coefficients for calculating Equation of Center. + private static final float C1 = 0.0334196f; + private static final float C2 = 0.000349066f; + private static final float C3 = 0.000005236f; + + private static final float OBLIQUITY = 0.40927971f; + + // Java time on Jan 1, 2000 12:00 UTC. + private static final long UTC_2000 = 946728000000L; + + /** Time of sunset (civil twilight) in milliseconds. */ + public long mSunset; + + /** Time of sunrise (civil twilight) in milliseconds. */ + public long mSunrise; + + /** Current state */ + public int mState; + + /** + * calculates the civil twilight bases on time and geo-coordinates. + * + * @param time time in milliseconds. + * @param latiude latitude in degrees. + * @param longitude latitude in degrees. + */ + public void calculateTwilight(long time, double latiude, double longitude) { + final float daysSince2000 = (float) (time - UTC_2000) / DateUtils.DAY_IN_MILLIS; + + // mean anomaly + final float meanAnomaly = 6.240059968f + daysSince2000 * 0.01720197f; + + // true anomaly + final float trueAnomaly = meanAnomaly + C1 * FloatMath.sin(meanAnomaly) + C2 + * FloatMath.sin(2 * meanAnomaly) + C3 * FloatMath.sin(3 * meanAnomaly); + + // ecliptic longitude + final float solarLng = trueAnomaly + 1.796593063f + (float) Math.PI; + + // solar transit in days since 2000 + final double arcLongitude = -longitude / 360; + float n = Math.round(daysSince2000 - J0 - arcLongitude); + double solarTransitJ2000 = n + J0 + arcLongitude + 0.0053f * FloatMath.sin(meanAnomaly) + + -0.0069f * FloatMath.sin(2 * solarLng); + + // declination of sun + double solarDec = Math.asin(FloatMath.sin(solarLng) * FloatMath.sin(OBLIQUITY)); + + final double latRad = latiude * DEGREES_TO_RADIANS; + float hourAngle = (float) (Math + .acos((FloatMath.sin(ALTIDUTE_CORRECTION_CIVIL_TWILIGHT) - Math.sin(latRad) + * Math.sin(solarDec)) + / (Math.cos(latRad) * Math.cos(solarDec))) / (2 * Math.PI)); + + mSunset = Math.round((solarTransitJ2000 + hourAngle) * DateUtils.DAY_IN_MILLIS) + UTC_2000; + mSunrise = Math.round((solarTransitJ2000 - hourAngle) * DateUtils.DAY_IN_MILLIS) + UTC_2000; + + if (mSunrise < time && mSunset > time) { + mState = DAY; + } else { + mState = NIGHT; + } + } + +} diff --git a/telephony/java/com/android/internal/telephony/IccUtils.java b/telephony/java/com/android/internal/telephony/IccUtils.java index 3e7094e606cb..71936f15936d 100644 --- a/telephony/java/com/android/internal/telephony/IccUtils.java +++ b/telephony/java/com/android/internal/telephony/IccUtils.java @@ -397,7 +397,7 @@ public class IccUtils { int bits = data[valueIndex++] & 0xFF; int colorNumber = data[valueIndex++] & 0xFF; int clutOffset = ((data[valueIndex++] & 0xFF) << 8) - | data[valueIndex++]; + | (data[valueIndex++] & 0xFF); length = length - 6; int[] colorIndexArray = getCLUT(data, clutOffset, colorNumber); diff --git a/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java b/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java index 9caae3d6cbc4..41e527cec05a 100644 --- a/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java +++ b/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java @@ -409,7 +409,7 @@ public class UsimPhoneBookManager extends Handler implements IccConstants { case USIM_EFIAP_TAG: case USIM_EFSNE_TAG: data = tlv.getData(); - int efid = data[0] << 8 | data[1]; + int efid = ((data[0] & 0xFF) << 8) | (data[1] & 0xFF); val.put(tag, efid); break; } diff --git a/tests/AndroidTests/AndroidManifest.xml b/tests/AndroidTests/AndroidManifest.xml index 2b6b81e96b50..28f1e73ee357 100644 --- a/tests/AndroidTests/AndroidManifest.xml +++ b/tests/AndroidTests/AndroidManifest.xml @@ -29,6 +29,11 @@ <uses-permission android:name="android.permission.INSTALL_PACKAGES" /> <uses-permission android:name="android.permission.DELETE_PACKAGES" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> + <uses-permission android:name="android.permission.ASEC_ACCESS" /> + <uses-permission android:name="android.permission.ASEC_CREATE" /> + <uses-permission android:name="android.permission.ASEC_DESTROY" /> + <uses-permission android:name="android.permission.ASEC_MOUNT_UNMOUNT" /> + <uses-permission android:name="android.permission.ASEC_RENAME" /> <uses-permission android:name="com.android.unit_tests.permission.TEST_GRANTED" /> <uses-permission android:name="com.google.android.googleapps.permission.ACCESS_GOOGLE_PASSWORD" /> <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" /> diff --git a/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java b/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java new file mode 100755 index 000000000000..7569d7aa7f7d --- /dev/null +++ b/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2006 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 com.android.unit_tests; + +import android.os.storage.IMountService.Stub; + +import android.net.Uri; +import android.os.FileUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.Suppress; +import android.util.DisplayMetrics; +import android.util.Log; +import android.os.Environment; +import android.os.Handler; +import android.os.IBinder; +import android.os.storage.IMountService; +import android.os.storage.StorageResultCode; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.StatFs; +import android.provider.Settings; +import junit.framework.Assert; + +public class AsecTests extends AndroidTestCase { + private static final boolean localLOGV = true; + public static final String TAG="AsecTests"; + + void failStr(String errMsg) { + Log.w(TAG, "errMsg="+errMsg); + } + void failStr(Exception e) { + Log.w(TAG, "e.getMessage="+e.getMessage()); + Log.w(TAG, "e="+e); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + if (localLOGV) Log.i(TAG, "Cleaning out old test containers"); + cleanupContainers(); + } + + private void cleanupContainers() throws RemoteException { + IMountService ms = getMs(); + String[] containers = ms.getSecureContainerList(); + + for (int i = 0; i < containers.length; i++) { + if (containers[i].startsWith("com.android.unittests.AsecTests.")) { + ms.destroySecureContainer(containers[i]); + } + } + } + + private IMountService getMs() { + IBinder service = ServiceManager.getService("mount"); + if (service != null) { + return IMountService.Stub.asInterface(service); + } else { + Log.e(TAG, "Can't get mount service"); + } + return null; + } + + private boolean isMediaMounted() { + try { + String mPath = Environment.getExternalStorageDirectory().toString(); + String state = getMs().getVolumeState(mPath); + return Environment.MEDIA_MOUNTED.equals(state); + } catch (RemoteException e) { + failStr(e); + return false; + } + } + + public void testCreateContainer() { + Assert.assertTrue(isMediaMounted()); + IMountService ms = getMs(); + try { + int rc = ms.createSecureContainer("com.android.unittests.AsecTests.testCreateContainer", 4, "fat", "none", 1000); + Assert.assertEquals(StorageResultCode.OperationSucceeded, rc); + } catch (Exception e) { + failStr(e); + } + } + + public void testDestroyContainer() { + Assert.assertTrue(isMediaMounted()); + IMountService ms = getMs(); + try { + int rc = ms.createSecureContainer("com.android.unittests.AsecTests.testDestroyContainer", 4, "fat", "none", 1000); + Assert.assertEquals(StorageResultCode.OperationSucceeded, rc); + rc = ms.destroySecureContainer("com.android.unittests.AsecTests.testDestroyContainer"); + Assert.assertEquals(StorageResultCode.OperationSucceeded, rc); + } catch (Exception e) { + failStr(e); + } + } + + public void testMountContainer() { + Assert.assertTrue(isMediaMounted()); + IMountService ms = getMs(); + try { + int rc = ms.createSecureContainer( + "com.android.unittests.AsecTests.testMountContainer", 4, "fat", "none", 1000); + Assert.assertEquals(StorageResultCode.OperationSucceeded, rc); + + rc = ms.unmountSecureContainer("com.android.unittests.AsecTests.testMountContainer"); + Assert.assertEquals(StorageResultCode.OperationSucceeded, rc); + + rc = ms.mountSecureContainer("com.android.unittests.AsecTests.testMountContainer", "none", 1000); + Assert.assertEquals(StorageResultCode.OperationSucceeded, rc); + } catch (Exception e) { + failStr(e); + } + } + + public void testMountBadKey() { + Assert.assertTrue(isMediaMounted()); + IMountService ms = getMs(); + try { + int rc = ms.createSecureContainer( + "com.android.unittests.AsecTests.testMountBadKey", 4, "fat", + "00000000000000000000000000000000", 1000); + Assert.assertEquals(StorageResultCode.OperationSucceeded, rc); + + rc = ms.unmountSecureContainer("com.android.unittests.AsecTests.testMountBadKey"); + Assert.assertEquals(StorageResultCode.OperationSucceeded, rc); + + rc = ms.mountSecureContainer( + "com.android.unittests.AsecTests.testMountBadKey", + "00000000000000000000000000000001", 1001); + Assert.assertEquals(StorageResultCode.OperationFailedInternalError, rc); + + rc = ms.mountSecureContainer( + "com.android.unittests.AsecTests.testMountBadKey", "none", 1001); + Assert.assertEquals(StorageResultCode.OperationFailedInternalError, rc); + } catch (Exception e) { + failStr(e); + } + } +} diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java index 452368eb309a..4d51356df9ae 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java @@ -81,172 +81,112 @@ public class FileFilter { "platform", // platform specific "http/wml", }; - + static final String [] ignoreTestList = { - // RegExp is exponatal - "fast/regex/test1.html", - "fast/regex/slow.html", - // RegExp is too large, causing OOM - "fast/js/regexp-charclass-crash.html", - // The Android browser has no notion of private browsing. - "storage/private-browsing-readonly.html", - "storage/domstorage/localstorage/private-browsing-affects-storage.html", - "storage/domstorage/sessionstorage/private-browsing-affects-storage.html", - // Android layout tests are stored in "layout_tests". The following two - // tests expect "LayoutTests" in their output. - "storage/domstorage/localstorage/iframe-events.html", - "storage/domstorage/sessionstorage/iframe-events.html", - // We do not support multi touch events. - "fast/events/touch/basic-multi-touch-events.html", - // below tests (failed or crashes) are filtered out temporarily due to prioritizing "editing/selection/move-left-right.html", + "fast/events/touch/basic-multi-touch-events.html", // We do not support multi touch events. + "fast/js/regexp-charclass-crash.html", // RegExp is too large, causing OOM + "fast/regex/test1.html", // RegExp is exponential + "fast/regex/slow.html", // RegExp is exponential + "storage/domstorage/localstorage/iframe-events.html", // Expects test to be in LayoutTests + "storage/domstorage/localstorage/private-browsing-affects-storage.html", // No notion of private browsing. + "storage/domstorage/sessionstorage/iframe-events.html", // Expects test to be in LayoutTests + "storage/domstorage/sessionstorage/private-browsing-affects-storage.html", // No notion of private browsing. + "storage/private-browsing-readonly.html", // No notion of private browsing. }; - + static void fillIgnoreResultSet() { - // need test plugin - ignoreResultList.add("fast/dom/Window/Plug-ins.html"); - // pixel depth - ignoreResultList.add("fast/dom/Window/window-screen-properties.html"); - // missing space in textrun, ok as text is wrapped. ignore. #714933 - ignoreResultList.add("fast/events/onload-webkit-before-webcore.html"); - // missing support for textInputController.makeAttributedString() - ignoreResultList.add("fast/forms/attributed-strings.html"); - // charset convert. #516936 ignore, won't fix - ignoreResultList.add("fast/forms/form-data-encoding-2.html"); - // charset convert. #516936 ignore, won't fix - ignoreResultList.add("fast/forms/form-data-encoding.html"); - // execCommand "insertText" not supported - ignoreResultList.add("fast/forms/input-appearance-maxlength.html"); - // Copy&Paste commands not supported - ignoreResultList.add("fast/forms/input-truncate-newline.html"); - ignoreResultList.add("fast/forms/textarea-paste-newline.html"); - // requires eventSender.mouseMoveTo, mouseDown & mouseUp and - // abs. position of mouse to select a word. ignore, won't fix #716583 - ignoreResultList.add("fast/forms/onselect-textarea.html"); - // requires eventSender.mouseMoveTo, mouseDown & mouseUp and - // abs. position of mouse to select a word. ignore, won't fix #716583 - ignoreResultList.add("fast/forms/onselect-textfield.html"); - // not implemented queryCommandEnabled:BackColor, Undo & Redo - ignoreResultList.add("fast/forms/plaintext-mode-1.html"); - // Our text areas are a little thinner than Apples. Also RTL test failes - ignoreResultList.add("fast/forms/textarea-appearance-wrap.html"); - // Our text areas are a little thinner than Apples - ignoreResultList.add("fast/forms/textarea-hard-linewrap.html"); - // screen width&height are different - ignoreResultList.add("fast/frames/frameElement-widthheight.html"); - ignoreResultList.add("fast/frames/frame-js-url-clientWidth.html"); - // requires JS test API, textInputController - ignoreResultList.add("fast/text/attributed-substring-from-range.html"); - ignoreResultList.add("fast/text/attributed-substring-from-range-001.html"); - // will not fix #619707 - ignoreResultList.add("fast/css/case-transform.html"); - // different platform defaults for font and different screen size - ignoreResultList.add("fast/css/computed-style.html"); - // different screen size result in extra spaces in Apple compared to us - ignoreResultList.add("fast/dom/Element/offsetLeft-offsetTop-body-quirk.html"); - // xslt and xpath elements missing from property list - ignoreResultList.add("fast/dom/Window/window-properties.html"); - // requires textInputController.characterIndexForPoint - ignoreResultList.add("fast/dom/character-index-for-point.html"); - // requires xpath support - ignoreResultList.add("fast/dom/gc-9.html"); - // requires xslt and xpath support - ignoreResultList.add("fast/dom/global-constructors.html"); - // dynamic plugins not supported - ignoreResultList.add("fast/dom/object-embed-plugin-scripting.html"); - ignoreResultList.add("fast/js/navigator-mimeTypes-length.html"); - // there is extra spacing in the file due to multiple input boxes - // fitting on one line on Apple, ours are wrapped. Space at line ends - // are stripped. - ignoreResultList.add("fast/dom/tabindex-clamp.html"); - - // requires eventSender.mouseDown(),mouseUp() - ignoreResultList.add("fast/dom/Window/window-xy-properties.html"); - ignoreResultList.add("fast/events/window-events-bubble.html"); - ignoreResultList.add("fast/events/window-events-bubble2.html"); - ignoreResultList.add("fast/events/window-events-capture.html"); - ignoreResultList.add("fast/forms/select-empty-list.html"); - ignoreResultList.add("fast/replaced/image-map.html"); - ignoreResultList.add("fast/events/capture-on-target.html"); - ignoreResultList.add("fast/events/dblclick-addEventListener.html"); - ignoreResultList.add("fast/events/drag-in-frames.html"); - ignoreResultList.add("fast/events/drag-outside-window.html"); - ignoreResultList.add("fast/events/event-sender-mouse-click.html"); - ignoreResultList.add("fast/events/event-view-toString.html"); - ignoreResultList.add("fast/events/frame-click-focus.html"); - ignoreResultList.add("fast/events/input-image-scrolled-x-y.html"); - ignoreResultList.add("fast/events/anchor-image-scrolled-x-y.html"); - ignoreResultList.add("fast/events/mouseclick-target-and-positioning.html"); - ignoreResultList.add("fast/events/mouseover-mouseout.html"); - ignoreResultList.add("fast/events/mouseover-mouseout2.html"); - ignoreResultList.add("fast/events/mouseup-outside-button.html"); - ignoreResultList.add("fast/events/mouseup-outside-document.html"); - ignoreResultList.add("fast/events/onclick-list-marker.html"); - ignoreResultList.add("fast/events/ondragenter.html"); - ignoreResultList.add("fast/forms/drag-into-textarea.html"); - ignoreResultList.add("fast/forms/input-select-on-click.html"); - ignoreResultList.add("fast/forms/listbox-onchange.html"); - ignoreResultList.add("fast/forms/search-cancel-button-mouseup.html"); - ignoreResultList.add("fast/forms/textarea-scrolled-endline-caret.html"); - - // there is extra spacing in the file due to multiple frame boxes - // fitting on one line on Apple, ours are wrapped. Space at line ends - // are stripped. - ignoreResultList.add("fast/events/iframe-object-onload.html"); - // eventSender.mouseDown(), mouseUp() and objc API missing - ignoreResultList.add("fast/events/mouseup-outside-document.html"); - ignoreResultList.add("fast/events/objc-keyboard-event-creation.html"); - ignoreResultList.add("fast/events/objc-event-api.html"); - // not capturing the console messages - ignoreResultList.add("fast/forms/selected-index-assert.html"); - ignoreResultList.add("fast/parser/script-tag-with-trailing-slash.html"); - // there is extra spacing as the text areas and input boxes fit next - // to each other on Apple, but are wrapped on our screen. - ignoreResultList.add("fast/forms/selection-functions.html"); - // Text selection done differently on our platform. When a inputbox - // gets focus, the entire block is selected. - ignoreResultList.add("fast/forms/textarea-initial-caret-position.html"); - ignoreResultList.add("fast/forms/textarea-no-scroll-on-blur.html"); - // Requires LayoutTests to exist at /tmp/LayoutTests - ignoreResultList.add("fast/loader/local-JavaScript-from-local.html"); - ignoreResultList.add("fast/loader/local-iFrame-source-from-local.html"); - // extra spacing because iFrames rendered next to each other on Apple - ignoreResultList.add("fast/loader/opaque-base-url.html"); - ignoreResultList.add("fast/text/plain-text-line-breaks.html"); + ignoreResultList.add("fast/css/case-transform.html"); // will not fix #619707 + ignoreResultList.add("fast/css/computed-style.html"); // different platform defaults for font and different screen size + ignoreResultList.add("fast/dom/Element/offsetLeft-offsetTop-body-quirk.html"); // different screen size result in extra spaces in Apple compared to us + ignoreResultList.add("fast/dom/Window/Plug-ins.html"); // need test plugin + ignoreResultList.add("fast/dom/Window/window-properties.html"); // xslt and xpath elements missing from property list + ignoreResultList.add("fast/dom/Window/window-screen-properties.html"); // pixel depth + ignoreResultList.add("fast/dom/Window/window-xy-properties.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/dom/character-index-for-point.html"); // requires textInputController.characterIndexForPoint + ignoreResultList.add("fast/dom/gc-9.html"); // requires xpath support + ignoreResultList.add("fast/dom/global-constructors.html"); // requires xslt and xpath support + ignoreResultList.add("fast/dom/object-embed-plugin-scripting.html"); // dynamic plugins not supported + ignoreResultList.add("fast/dom/tabindex-clamp.html"); // there is extra spacing in the file due to multiple input boxes fitting on one line on Apple, ours are wrapped. Space at line ends are stripped. + ignoreResultList.add("fast/events/anchor-image-scrolled-x-y.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/events/capture-on-target.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/events/dblclick-addEventListener.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/events/drag-in-frames.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/events/drag-outside-window.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/events/event-sender-mouse-click.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/events/event-view-toString.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/events/frame-click-focus.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/events/iframe-object-onload.html"); // there is extra spacing in the file due to multiple frame boxes fitting on one line on Apple, ours are wrapped. Space at line ends are stripped. + ignoreResultList.add("fast/events/input-image-scrolled-x-y.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/events/mouseclick-target-and-positioning.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/events/mouseover-mouseout.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/events/mouseover-mouseout2.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/events/mouseup-outside-button.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/events/mouseup-outside-document.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/events/objc-event-api.html"); // eventSender.mouseDown(), mouseUp() and objc API missing + ignoreResultList.add("fast/events/objc-keyboard-event-creation.html"); // eventSender.mouseDown(), mouseUp() and objc API missing + ignoreResultList.add("fast/events/onclick-list-marker.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/events/ondragenter.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/events/onload-webkit-before-webcore.html"); // missing space in textrun, ok as text is wrapped. ignore. #714933 + ignoreResultList.add("fast/events/window-events-bubble.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/events/window-events-bubble2.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/events/window-events-capture.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/forms/attributed-strings.html"); // missing support for textInputController.makeAttributedString() + ignoreResultList.add("fast/forms/drag-into-textarea.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/forms/form-data-encoding-2.html"); // charset convert. #516936 ignore, won't fix + ignoreResultList.add("fast/forms/form-data-encoding.html"); // charset convert. #516936 ignore, won't fix + ignoreResultList.add("fast/forms/input-appearance-maxlength.html"); // execCommand "insertText" not supported + ignoreResultList.add("fast/forms/input-select-on-click.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/forms/input-truncate-newline.html"); // Copy&Paste commands not supported + ignoreResultList.add("fast/forms/listbox-onchange.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/forms/onselect-textarea.html"); // requires eventSender.mouseMoveTo, mouseDown & mouseUp and abs. position of mouse to select a word. ignore, won't fix #716583 + ignoreResultList.add("fast/forms/onselect-textfield.html"); // requires eventSender.mouseMoveTo, mouseDown & mouseUp and abs. position of mouse to select a word. ignore, won't fix #716583 + ignoreResultList.add("fast/forms/plaintext-mode-1.html"); // not implemented queryCommandEnabled:BackColor, Undo & Redo + ignoreResultList.add("fast/forms/search-cancel-button-mouseup.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/forms/select-empty-list.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/forms/selected-index-assert.html"); // not capturing the console messages + ignoreResultList.add("fast/forms/selection-functions.html"); // there is extra spacing as the text areas and input boxes fit next to each other on Apple, but are wrapped on our screen. + ignoreResultList.add("fast/forms/textarea-appearance-wrap.html"); // Our text areas are a little thinner than Apples. Also RTL test failes + ignoreResultList.add("fast/forms/textarea-hard-linewrap.html"); // Our text areas are a little thinner than Apples + ignoreResultList.add("fast/forms/textarea-initial-caret-position.html"); // Text selection done differently on our platform. When a inputbox gets focus, the entire block is selected. + ignoreResultList.add("fast/forms/textarea-no-scroll-on-blur.html"); // Text selection done differently on our platform. When a inputbox gets focus, the entire block is selected. + ignoreResultList.add("fast/forms/textarea-paste-newline.html"); // Copy&Paste commands not supported + ignoreResultList.add("fast/forms/textarea-scrolled-endline-caret.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/frames/frameElement-widthheight.html"); // screen width&height are different + ignoreResultList.add("fast/frames/frame-js-url-clientWidth.html"); // screen width&height are different + ignoreResultList.add("fast/js/navigator-mimeTypes-length.html"); // dynamic plugins not supported + ignoreResultList.add("fast/loader/local-JavaScript-from-local.html"); // Requires LayoutTests to exist at /tmp/LayoutTests + ignoreResultList.add("fast/loader/local-iFrame-source-from-local.html"); // Requires LayoutTests to exist at /tmp/LayoutTests + ignoreResultList.add("fast/loader/opaque-base-url.html"); // extra spacing because iFrames rendered next to each other on Apple + ignoreResultList.add("fast/parser/script-tag-with-trailing-slash.html"); // not capturing the console messages + ignoreResultList.add("fast/replaced/image-map.html"); // requires eventSender.mouseDown(),mouseUp() + ignoreResultList.add("fast/text/attributed-substring-from-range.html"); // requires JS test API, textInputController + ignoreResultList.add("fast/text/attributed-substring-from-range-001.html"); // requires JS test API, textInputController + ignoreResultList.add("fast/text/plain-text-line-breaks.html"); // extra spacing because iFrames rendered next to each other on Apple } - + static void fillBugTable() { - bugList.put("fast/forms/check-box-enter-key.html", "716715"); bugList.put("fast/forms/focus-control-to-page.html", "716638"); bugList.put("fast/html/tab-order.html", "719289"); bugList.put("fast/dom/attribute-namespaces-get-set.html", "733229"); - bugList.put("fast/dom/location-hash.html", "733822"); bugList.put("fast/dom/set-innerHTML.html", "733823"); bugList.put("fast/dom/xmlhttprequest-get.html", "733846"); bugList.put("fast/encoding/css-charset-default.html", "733856"); bugList.put("fast/encoding/default-xhtml-encoding.html", "733882"); bugList.put("fast/encoding/meta-in-xhtml.html", "733882"); bugList.put("fast/events/frame-tab-focus.html", "734308"); - bugList.put("fast/events/keydown-keypress-focus-change.html", "653224"); - bugList.put("fast/events/keypress-focus-change.html", "653224"); bugList.put("fast/events/option-tab.html", "734308"); bugList.put("fast/forms/focus2.html", "735111"); bugList.put("fast/forms/listbox-selection.html", "735116"); bugList.put("fast/forms/search-event-delay.html", "735120"); bugList.put("fast/frames/iframe-window-focus.html", "735140"); bugList.put("fast/innerHTML/004.html", "733882"); - bugList.put("fast/js/date-DST-time-cusps.html", "735144"); bugList.put("fast/js/string-capitalization.html", "516936"); bugList.put("fast/js/string-concatenate-outofmemory.html","735152"); bugList.put("fast/parser/external-entities.html", "735176"); - bugList.put("fast/events/div-focus.html", "735185"); bugList.put("fast/overflow/scroll-vertical-not-horizontal.html", "735196"); bugList.put("fast/events/arrow-navigation.html", "735233"); bugList.put("fast/forms/select-type-ahead-non-latin.html", "735244"); - bugList.put("fast/events/js-keyboard-event-creation.html", "735255"); - } - - - + } |