Merge "Increase the setup-sdp delay to cover for all the devices." into jb-dev
diff --git a/api/16.txt b/api/16.txt
index 1a694a8..2c02347 100644
--- a/api/16.txt
+++ b/api/16.txt
@@ -6519,6 +6519,7 @@
field public static final java.lang.String FEATURE_TELEPHONY = "android.hardware.telephony";
field public static final java.lang.String FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma";
field public static final java.lang.String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
+ field public static final java.lang.String FEATURE_TELEVISION = "android.hardware.type.television";
field public static final java.lang.String FEATURE_TOUCHSCREEN = "android.hardware.touchscreen";
field public static final java.lang.String FEATURE_TOUCHSCREEN_MULTITOUCH = "android.hardware.touchscreen.multitouch";
field public static final java.lang.String FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT = "android.hardware.touchscreen.multitouch.distinct";
@@ -6794,7 +6795,7 @@
field public static final int NAVIGATION_WHEEL = 4; // 0x4
field public static final int ORIENTATION_LANDSCAPE = 2; // 0x2
field public static final int ORIENTATION_PORTRAIT = 1; // 0x1
- field public static final int ORIENTATION_SQUARE = 3; // 0x3
+ field public static final deprecated int ORIENTATION_SQUARE = 3; // 0x3
field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
field public static final int SCREENLAYOUT_LONG_MASK = 48; // 0x30
field public static final int SCREENLAYOUT_LONG_NO = 16; // 0x10
@@ -6811,7 +6812,7 @@
field public static final int SMALLEST_SCREEN_WIDTH_DP_UNDEFINED = 0; // 0x0
field public static final int TOUCHSCREEN_FINGER = 3; // 0x3
field public static final int TOUCHSCREEN_NOTOUCH = 1; // 0x1
- field public static final int TOUCHSCREEN_STYLUS = 2; // 0x2
+ field public static final deprecated int TOUCHSCREEN_STYLUS = 2; // 0x2
field public static final int TOUCHSCREEN_UNDEFINED = 0; // 0x0
field public static final int UI_MODE_NIGHT_MASK = 48; // 0x30
field public static final int UI_MODE_NIGHT_NO = 16; // 0x10
@@ -19690,6 +19691,7 @@
method public static final android.content.Intent getVoiceDetailsIntent(android.content.Context);
field public static final java.lang.String ACTION_GET_LANGUAGE_DETAILS = "android.speech.action.GET_LANGUAGE_DETAILS";
field public static final java.lang.String ACTION_RECOGNIZE_SPEECH = "android.speech.action.RECOGNIZE_SPEECH";
+ field public static final java.lang.String ACTION_VOICE_SEARCH_HANDS_FREE = "android.speech.action.VOICE_SEARCH_HANDS_FREE";
field public static final java.lang.String ACTION_WEB_SEARCH = "android.speech.action.WEB_SEARCH";
field public static final java.lang.String DETAILS_META_DATA = "android.speech.DETAILS";
field public static final java.lang.String EXTRA_CALLING_PACKAGE = "calling_package";
@@ -19705,6 +19707,7 @@
field public static final java.lang.String EXTRA_RESULTS = "android.speech.extra.RESULTS";
field public static final java.lang.String EXTRA_RESULTS_PENDINGINTENT = "android.speech.extra.RESULTS_PENDINGINTENT";
field public static final java.lang.String EXTRA_RESULTS_PENDINGINTENT_BUNDLE = "android.speech.extra.RESULTS_PENDINGINTENT_BUNDLE";
+ field public static final java.lang.String EXTRA_SECURE = "android.speech.extras.EXTRA_SECURE";
field public static final java.lang.String EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS";
field public static final java.lang.String EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_MINIMUM_LENGTH_MILLIS";
field public static final java.lang.String EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS";
diff --git a/api/current.txt b/api/current.txt
index 1a694a8..2c02347 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6519,6 +6519,7 @@
field public static final java.lang.String FEATURE_TELEPHONY = "android.hardware.telephony";
field public static final java.lang.String FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma";
field public static final java.lang.String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
+ field public static final java.lang.String FEATURE_TELEVISION = "android.hardware.type.television";
field public static final java.lang.String FEATURE_TOUCHSCREEN = "android.hardware.touchscreen";
field public static final java.lang.String FEATURE_TOUCHSCREEN_MULTITOUCH = "android.hardware.touchscreen.multitouch";
field public static final java.lang.String FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT = "android.hardware.touchscreen.multitouch.distinct";
@@ -6794,7 +6795,7 @@
field public static final int NAVIGATION_WHEEL = 4; // 0x4
field public static final int ORIENTATION_LANDSCAPE = 2; // 0x2
field public static final int ORIENTATION_PORTRAIT = 1; // 0x1
- field public static final int ORIENTATION_SQUARE = 3; // 0x3
+ field public static final deprecated int ORIENTATION_SQUARE = 3; // 0x3
field public static final int ORIENTATION_UNDEFINED = 0; // 0x0
field public static final int SCREENLAYOUT_LONG_MASK = 48; // 0x30
field public static final int SCREENLAYOUT_LONG_NO = 16; // 0x10
@@ -6811,7 +6812,7 @@
field public static final int SMALLEST_SCREEN_WIDTH_DP_UNDEFINED = 0; // 0x0
field public static final int TOUCHSCREEN_FINGER = 3; // 0x3
field public static final int TOUCHSCREEN_NOTOUCH = 1; // 0x1
- field public static final int TOUCHSCREEN_STYLUS = 2; // 0x2
+ field public static final deprecated int TOUCHSCREEN_STYLUS = 2; // 0x2
field public static final int TOUCHSCREEN_UNDEFINED = 0; // 0x0
field public static final int UI_MODE_NIGHT_MASK = 48; // 0x30
field public static final int UI_MODE_NIGHT_NO = 16; // 0x10
@@ -19690,6 +19691,7 @@
method public static final android.content.Intent getVoiceDetailsIntent(android.content.Context);
field public static final java.lang.String ACTION_GET_LANGUAGE_DETAILS = "android.speech.action.GET_LANGUAGE_DETAILS";
field public static final java.lang.String ACTION_RECOGNIZE_SPEECH = "android.speech.action.RECOGNIZE_SPEECH";
+ field public static final java.lang.String ACTION_VOICE_SEARCH_HANDS_FREE = "android.speech.action.VOICE_SEARCH_HANDS_FREE";
field public static final java.lang.String ACTION_WEB_SEARCH = "android.speech.action.WEB_SEARCH";
field public static final java.lang.String DETAILS_META_DATA = "android.speech.DETAILS";
field public static final java.lang.String EXTRA_CALLING_PACKAGE = "calling_package";
@@ -19705,6 +19707,7 @@
field public static final java.lang.String EXTRA_RESULTS = "android.speech.extra.RESULTS";
field public static final java.lang.String EXTRA_RESULTS_PENDINGINTENT = "android.speech.extra.RESULTS_PENDINGINTENT";
field public static final java.lang.String EXTRA_RESULTS_PENDINGINTENT_BUNDLE = "android.speech.extra.RESULTS_PENDINGINTENT_BUNDLE";
+ field public static final java.lang.String EXTRA_SECURE = "android.speech.extras.EXTRA_SECURE";
field public static final java.lang.String EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS";
field public static final java.lang.String EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_MINIMUM_LENGTH_MILLIS";
field public static final java.lang.String EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS";
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 2baad62..bcdd012 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1050,6 +1050,17 @@
public static final String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: This is a device dedicated to showing UI
+ * on a television. Television here is defined to be a typical living
+ * room television experience: displayed on a big screen, where the user
+ * is sitting far away from it, and the dominant form of input will be
+ * something like a DPAD, not through touch or mouse.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_TELEVISION = "android.hardware.type.television";
+
+ /**
* Action to external storage service to clean out removed apps.
* @hide
*/
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 079f739..423b9af 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -42,17 +42,23 @@
public float fontScale;
/**
- * IMSI MCC (Mobile Country Code). 0 if undefined.
+ * IMSI MCC (Mobile Country Code), corresponding to
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#MccQualifier">mcc</a>
+ * resource qualifier. 0 if undefined.
*/
public int mcc;
/**
- * IMSI MNC (Mobile Network Code). 0 if undefined.
+ * IMSI MNC (Mobile Network Code), corresponding to
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#MccQualifier">mnc</a>
+ * resource qualifier. 0 if undefined.
*/
public int mnc;
/**
- * Current user preference for the locale.
+ * Current user preference for the locale, corresponding to
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#LocaleQualifier">locale</a>
+ * resource qualifier.
*/
public Locale locale;
@@ -69,29 +75,52 @@
* value indicating that no size has been set. */
public static final int SCREENLAYOUT_SIZE_UNDEFINED = 0x00;
/** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
- * value indicating the screen is at least approximately 320x426 dp units.
+ * value indicating the screen is at least approximately 320x426 dp units,
+ * corresponds to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenSizeQualifier">small</a>
+ * resource qualifier.
* See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
* Multiple Screens</a> for more information. */
public static final int SCREENLAYOUT_SIZE_SMALL = 0x01;
/** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
- * value indicating the screen is at least approximately 320x470 dp units.
+ * value indicating the screen is at least approximately 320x470 dp units,
+ * corresponds to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenSizeQualifier">normal</a>
+ * resource qualifier.
* See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
* Multiple Screens</a> for more information. */
public static final int SCREENLAYOUT_SIZE_NORMAL = 0x02;
/** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
- * value indicating the screen is at least approximately 480x640 dp units.
+ * value indicating the screen is at least approximately 480x640 dp units,
+ * corresponds to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenSizeQualifier">large</a>
+ * resource qualifier.
* See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
* Multiple Screens</a> for more information. */
public static final int SCREENLAYOUT_SIZE_LARGE = 0x03;
/** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_SIZE_MASK}
- * value indicating the screen is at least approximately 720x960 dp units.
+ * value indicating the screen is at least approximately 720x960 dp units,
+ * corresponds to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenSizeQualifier">xlarge</a>
+ * resource qualifier.
* See <a href="{@docRoot}guide/practices/screens_support.html">Supporting
* Multiple Screens</a> for more information.*/
public static final int SCREENLAYOUT_SIZE_XLARGE = 0x04;
-
+
+ /** Constant for {@link #screenLayout}: bits that encode the aspect ratio. */
public static final int SCREENLAYOUT_LONG_MASK = 0x30;
+ /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LONG_MASK}
+ * value indicating that no size has been set. */
public static final int SCREENLAYOUT_LONG_UNDEFINED = 0x00;
+ /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LONG_MASK}
+ * value that corresponds to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenAspectQualifier">notlong</a>
+ * resource qualifier. */
public static final int SCREENLAYOUT_LONG_NO = 0x10;
+ /** Constant for {@link #screenLayout}: a {@link #SCREENLAYOUT_LONG_MASK}
+ * value that corresponds to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenAspectQualifier">long</a>
+ * resource qualifier. */
public static final int SCREENLAYOUT_LONG_YES = 0x20;
/**
@@ -135,21 +164,38 @@
return cur >= size;
}
+ /** Constant for {@link #touchscreen}: a value indicating that no value has been set. */
public static final int TOUCHSCREEN_UNDEFINED = 0;
+ /** Constant for {@link #touchscreen}, value corresponding to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#TouchscreenQualifier">notouch</a>
+ * resource qualifier. */
public static final int TOUCHSCREEN_NOTOUCH = 1;
- public static final int TOUCHSCREEN_STYLUS = 2;
+ /** @deprecated Not currently supported or used. */
+ @Deprecated public static final int TOUCHSCREEN_STYLUS = 2;
+ /** Constant for {@link #touchscreen}, value corresponding to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#TouchscreenQualifier">finger</a>
+ * resource qualifier. */
public static final int TOUCHSCREEN_FINGER = 3;
/**
* The kind of touch screen attached to the device.
- * One of: {@link #TOUCHSCREEN_NOTOUCH}, {@link #TOUCHSCREEN_STYLUS},
- * {@link #TOUCHSCREEN_FINGER}.
+ * One of: {@link #TOUCHSCREEN_NOTOUCH}, {@link #TOUCHSCREEN_FINGER}.
*/
public int touchscreen;
-
+
+ /** Constant for {@link #keyboard}: a value indicating that no value has been set. */
public static final int KEYBOARD_UNDEFINED = 0;
+ /** Constant for {@link #keyboard}, value corresponding to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ImeQualifier">nokeys</a>
+ * resource qualifier. */
public static final int KEYBOARD_NOKEYS = 1;
+ /** Constant for {@link #keyboard}, value corresponding to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ImeQualifier">qwerty</a>
+ * resource qualifier. */
public static final int KEYBOARD_QWERTY = 2;
+ /** Constant for {@link #keyboard}, value corresponding to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ImeQualifier">12key</a>
+ * resource qualifier. */
public static final int KEYBOARD_12KEY = 3;
/**
@@ -158,9 +204,16 @@
* {@link #KEYBOARD_12KEY}.
*/
public int keyboard;
-
+
+ /** Constant for {@link #keyboardHidden}: a value indicating that no value has been set. */
public static final int KEYBOARDHIDDEN_UNDEFINED = 0;
+ /** Constant for {@link #keyboardHidden}, value corresponding to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keysexposed</a>
+ * resource qualifier. */
public static final int KEYBOARDHIDDEN_NO = 1;
+ /** Constant for {@link #keyboardHidden}, value corresponding to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyshidden</a>
+ * resource qualifier. */
public static final int KEYBOARDHIDDEN_YES = 2;
/** Constant matching actual resource implementation. {@hide} */
public static final int KEYBOARDHIDDEN_SOFT = 3;
@@ -174,8 +227,13 @@
*/
public int keyboardHidden;
+ /** Constant for {@link #hardKeyboardHidden}: a value indicating that no value has been set. */
public static final int HARDKEYBOARDHIDDEN_UNDEFINED = 0;
+ /** Constant for {@link #hardKeyboardHidden}, value corresponding to the
+ * physical keyboard being exposed. */
public static final int HARDKEYBOARDHIDDEN_NO = 1;
+ /** Constant for {@link #hardKeyboardHidden}, value corresponding to the
+ * physical keyboard being hidden. */
public static final int HARDKEYBOARDHIDDEN_YES = 2;
/**
@@ -186,10 +244,23 @@
*/
public int hardKeyboardHidden;
+ /** Constant for {@link #navigation}: a value indicating that no value has been set. */
public static final int NAVIGATION_UNDEFINED = 0;
+ /** Constant for {@link #navigation}, value corresponding to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavigationQualifier">nonav</a>
+ * resource qualifier. */
public static final int NAVIGATION_NONAV = 1;
+ /** Constant for {@link #navigation}, value corresponding to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavigationQualifier">dpad</a>
+ * resource qualifier. */
public static final int NAVIGATION_DPAD = 2;
+ /** Constant for {@link #navigation}, value corresponding to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavigationQualifier">trackball</a>
+ * resource qualifier. */
public static final int NAVIGATION_TRACKBALL = 3;
+ /** Constant for {@link #navigation}, value corresponding to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavigationQualifier">wheel</a>
+ * resource qualifier. */
public static final int NAVIGATION_WHEEL = 4;
/**
@@ -199,8 +270,15 @@
*/
public int navigation;
+ /** Constant for {@link #navigationHidden}: a value indicating that no value has been set. */
public static final int NAVIGATIONHIDDEN_UNDEFINED = 0;
+ /** Constant for {@link #navigationHidden}, value corresponding to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavAvailQualifier">navexposed</a>
+ * resource qualifier. */
public static final int NAVIGATIONHIDDEN_NO = 1;
+ /** Constant for {@link #navigationHidden}, value corresponding to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NavAvailQualifier">navhidden</a>
+ * resource qualifier. */
public static final int NAVIGATIONHIDDEN_YES = 2;
/**
@@ -211,29 +289,70 @@
*/
public int navigationHidden;
+ /** Constant for {@link #orientation}: a value indicating that no value has been set. */
public static final int ORIENTATION_UNDEFINED = 0;
+ /** Constant for {@link #orientation}, value corresponding to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#OrientationQualifier">port</a>
+ * resource qualifier. */
public static final int ORIENTATION_PORTRAIT = 1;
+ /** Constant for {@link #orientation}, value corresponding to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#OrientationQualifier">land</a>
+ * resource qualifier. */
public static final int ORIENTATION_LANDSCAPE = 2;
- public static final int ORIENTATION_SQUARE = 3;
+ /** @deprecated Not currently supported or used. */
+ @Deprecated public static final int ORIENTATION_SQUARE = 3;
/**
* Overall orientation of the screen. May be one of
- * {@link #ORIENTATION_LANDSCAPE}, {@link #ORIENTATION_PORTRAIT},
- * or {@link #ORIENTATION_SQUARE}.
+ * {@link #ORIENTATION_LANDSCAPE}, {@link #ORIENTATION_PORTRAIT}.
*/
public int orientation;
+ /** Constant for {@link #uiMode}: bits that encode the mode type. */
public static final int UI_MODE_TYPE_MASK = 0x0f;
+ /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
+ * value indicating that no mode type has been set. */
public static final int UI_MODE_TYPE_UNDEFINED = 0x00;
+ /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
+ * value that corresponds to
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">no
+ * UI mode</a> resource qualifier specified. */
public static final int UI_MODE_TYPE_NORMAL = 0x01;
+ /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
+ * value that corresponds to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">desk</a>
+ * resource qualifier. */
public static final int UI_MODE_TYPE_DESK = 0x02;
+ /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
+ * value that corresponds to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">car</a>
+ * resource qualifier. */
public static final int UI_MODE_TYPE_CAR = 0x03;
+ /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
+ * value that corresponds to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">television</a>
+ * resource qualifier. */
public static final int UI_MODE_TYPE_TELEVISION = 0x04;
+ /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK}
+ * value that corresponds to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#UiModeQualifier">appliance</a>
+ * resource qualifier. */
public static final int UI_MODE_TYPE_APPLIANCE = 0x05;
+ /** Constant for {@link #uiMode}: bits that encode the night mode. */
public static final int UI_MODE_NIGHT_MASK = 0x30;
+ /** Constant for {@link #uiMode}: a {@link #UI_MODE_NIGHT_MASK}
+ * value indicating that no mode type has been set. */
public static final int UI_MODE_NIGHT_UNDEFINED = 0x00;
+ /** Constant for {@link #uiMode}: a {@link #UI_MODE_NIGHT_MASK}
+ * value that corresponds to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NightQualifier">notnight</a>
+ * resource qualifier. */
public static final int UI_MODE_NIGHT_NO = 0x10;
+ /** Constant for {@link #uiMode}: a {@link #UI_MODE_NIGHT_MASK}
+ * value that corresponds to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#NightQualifier">night</a>
+ * resource qualifier. */
public static final int UI_MODE_NIGHT_YES = 0x20;
/**
@@ -253,21 +372,30 @@
public static final int SCREEN_WIDTH_DP_UNDEFINED = 0;
/**
- * The current width of the available screen space, in dp units.
+ * The current width of the available screen space, in dp units,
+ * corresponding to
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenWidthQualifier">screen
+ * width</a> resource qualifier.
*/
public int screenWidthDp;
public static final int SCREEN_HEIGHT_DP_UNDEFINED = 0;
/**
- * The current height of the available screen space, in dp units.
+ * The current height of the available screen space, in dp units,
+ * corresponding to
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenHeightQualifier">screen
+ * height</a> resource qualifier.
*/
public int screenHeightDp;
public static final int SMALLEST_SCREEN_WIDTH_DP_UNDEFINED = 0;
/**
- * The smallest screen size an application will see in normal operation.
+ * The smallest screen size an application will see in normal operation,
+ * corresponding to
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#SmallestScreenWidthQualifier">smallest
+ * screen width</a> resource qualifier.
* This is the smallest value of both screenWidthDp and screenHeightDp
* in both portrait and landscape.
*/
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 6448b55..dfd35e1 100755
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -77,7 +77,8 @@
* The meta-data specifies a resource that contains a description of each keyboard
* layout that is provided by the application.
* <pre><code>
- * <receiver android:name=".InputDeviceReceiver">
+ * <receiver android:name=".InputDeviceReceiver"
+ * android:label="@string/keyboard_layouts_label">
* <intent-filter>
* <action android:name="android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS" />
* </intent-filter>
@@ -90,7 +91,9 @@
* an XML resource whose root element is <code><keyboard-layouts></code> that
* contains zero or more <code><keyboard-layout></code> elements.
* Each <code><keyboard-layout></code> element specifies the name, label, and location
- * of a key character map for a particular keyboard layout.
+ * of a key character map for a particular keyboard layout. The label on the receiver
+ * is used to name the collection of keyboard layouts provided by this receiver in the
+ * keyboard layout settings.
* <pre></code>
* <?xml version="1.0" encoding="utf-8"?>
* <keyboard-layouts xmlns:android="http://schemas.android.com/apk/res/android">
diff --git a/core/java/android/hardware/input/KeyboardLayout.java b/core/java/android/hardware/input/KeyboardLayout.java
index e75a6dc..5402e75 100644
--- a/core/java/android/hardware/input/KeyboardLayout.java
+++ b/core/java/android/hardware/input/KeyboardLayout.java
@@ -28,6 +28,7 @@
Comparable<KeyboardLayout> {
private final String mDescriptor;
private final String mLabel;
+ private final String mCollection;
public static final Parcelable.Creator<KeyboardLayout> CREATOR =
new Parcelable.Creator<KeyboardLayout>() {
@@ -39,14 +40,16 @@
}
};
- public KeyboardLayout(String descriptor, String label) {
+ public KeyboardLayout(String descriptor, String label, String collection) {
mDescriptor = descriptor;
mLabel = label;
+ mCollection = collection;
}
private KeyboardLayout(Parcel source) {
mDescriptor = source.readString();
mLabel = source.readString();
+ mCollection = source.readString();
}
/**
@@ -68,6 +71,15 @@
return mLabel;
}
+ /**
+ * Gets the name of the collection to which the keyboard layout belongs. This is
+ * the label of the broadcast receiver or application that provided the keyboard layout.
+ * @return The keyboard layout collection name.
+ */
+ public String getCollection() {
+ return mCollection;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -77,15 +89,23 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mDescriptor);
dest.writeString(mLabel);
+ dest.writeString(mCollection);
}
@Override
public int compareTo(KeyboardLayout another) {
- return mLabel.compareToIgnoreCase(another.mLabel);
+ int result = mLabel.compareToIgnoreCase(another.mLabel);
+ if (result == 0) {
+ result = mCollection.compareToIgnoreCase(another.mCollection);
+ }
+ return result;
}
@Override
public String toString() {
- return mLabel;
+ if (mCollection.isEmpty()) {
+ return mLabel;
+ }
+ return mLabel + " - " + mCollection;
}
}
\ No newline at end of file
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index 7ffa575..53b41d5 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -299,7 +299,23 @@
callback = state.uriCallback;
}
if (callback != null) {
- return callback.createBeamUris(mDefaultEvent);
+ uris = callback.createBeamUris(mDefaultEvent);
+ if (uris != null) {
+ for (Uri uri : uris) {
+ if (uri == null) {
+ Log.e(TAG, "Uri not allowed to be null.");
+ return null;
+ }
+ String scheme = uri.getScheme();
+ if (scheme == null || (!scheme.equalsIgnoreCase("file") &&
+ !scheme.equalsIgnoreCase("content"))) {
+ Log.e(TAG, "Uri needs to have " +
+ "either scheme file or scheme content");
+ return null;
+ }
+ }
+ }
+ return uris;
} else {
return uris;
}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 7bf9feb..4464d58 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -584,17 +584,138 @@
}
}
- //TODO: make sure NFC service has permission for URI
- //TODO: see if we will eventually support multiple URIs
- //TODO: javadoc
+ /**
+ * Set one or more {@link Uri}s to send using Android Beam (TM). Every
+ * Uri you provide must have either scheme 'file' or scheme 'content'.
+ *
+ * <p>For the data provided through this method, Android Beam tries to
+ * switch to alternate transports such as Bluetooth to achieve a fast
+ * transfer speed. Hence this method is very suitable
+ * for transferring large files such as pictures or songs.
+ *
+ * <p>The receiving side will store the content of each Uri in
+ * a file and present a notification to the user to open the file
+ * with a {@link android.content.Intent} with action
+ * {@link android.content.Intent#ACTION_VIEW}.
+ * If multiple URIs are sent, the {@link android.content.Intent} will refer
+ * to the first of the stored files.
+ *
+ * <p>This method may be called at any time before {@link Activity#onDestroy},
+ * but the URI(s) are only made available for Android Beam when the
+ * specified activity(s) are in resumed (foreground) state. The recommended
+ * approach is to call this method during your Activity's
+ * {@link Activity#onCreate} - see sample
+ * code below. This method does not immediately perform any I/O or blocking work,
+ * so is safe to call on your main thread.
+ *
+ * <p>{@link #setBeamPushUris} and {@link #setBeamPushUrisCallback}
+ * have priority over both {@link #setNdefPushMessage} and
+ * {@link #setNdefPushMessageCallback}.
+ *
+ * <p>If {@link #setBeamPushUris} is called with a null Uri array,
+ * and/or {@link #setBeamPushUrisCallback} is called with a null callback,
+ * then the Uri push will be completely disabled for the specified activity(s).
+ *
+ * <p>Code example:
+ * <pre>
+ * protected void onCreate(Bundle savedInstanceState) {
+ * super.onCreate(savedInstanceState);
+ * NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
+ * if (nfcAdapter == null) return; // NFC not available on this device
+ * nfcAdapter.setBeamPushUris(new Uri[] {uri1, uri2}, this);
+ * }
+ * </pre>
+ * And that is it. Only one call per activity is necessary. The Android
+ * OS will automatically release its references to the Uri(s) and the
+ * Activity object when it is destroyed if you follow this pattern.
+ *
+ * <p>If your Activity wants to dynamically supply Uri(s),
+ * then set a callback using {@link #setBeamPushUrisCallback} instead
+ * of using this method.
+ *
+ * <p class="note">Do not pass in an Activity that has already been through
+ * {@link Activity#onDestroy}. This is guaranteed if you call this API
+ * during {@link Activity#onCreate}.
+ *
+ * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+ *
+ * @param uris an array of Uri(s) to push over Android Beam
+ * @param activity activity for which the Uri(s) will be pushed
+ */
public void setBeamPushUris(Uri[] uris, Activity activity) {
if (activity == null) {
throw new NullPointerException("activity cannot be null");
}
+ if (uris != null) {
+ for (Uri uri : uris) {
+ if (uri == null) throw new NullPointerException("Uri not " +
+ "allowed to be null");
+ String scheme = uri.getScheme();
+ if (scheme == null || (!scheme.equalsIgnoreCase("file") &&
+ !scheme.equalsIgnoreCase("content"))) {
+ throw new IllegalArgumentException("URI needs to have " +
+ "either scheme file or scheme content");
+ }
+ }
+ }
mNfcActivityManager.setNdefPushContentUri(activity, uris);
}
- // TODO javadoc
+ /**
+ * Set a callback that will dynamically generate one or more {@link Uri}s
+ * to send using Android Beam (TM). Every Uri the callback provides
+ * must have either scheme 'file' or scheme 'content'.
+ *
+ * <p>For the data provided through this callback, Android Beam tries to
+ * switch to alternate transports such as Bluetooth to achieve a fast
+ * transfer speed. Hence this method is very suitable
+ * for transferring large files such as pictures or songs.
+ *
+ * <p>The receiving side will store the content of each Uri in
+ * a file and present a notification to the user to open the file
+ * with a {@link android.content.Intent} with action
+ * {@link android.content.Intent#ACTION_VIEW}.
+ * If multiple URIs are sent, the {@link android.content.Intent} will refer
+ * to the first of the stored files.
+ *
+ * <p>This method may be called at any time before {@link Activity#onDestroy},
+ * but the URI(s) are only made available for Android Beam when the
+ * specified activity(s) are in resumed (foreground) state. The recommended
+ * approach is to call this method during your Activity's
+ * {@link Activity#onCreate} - see sample
+ * code below. This method does not immediately perform any I/O or blocking work,
+ * so is safe to call on your main thread.
+ *
+ * <p>{@link #setBeamPushUris} and {@link #setBeamPushUrisCallback}
+ * have priority over both {@link #setNdefPushMessage} and
+ * {@link #setNdefPushMessageCallback}.
+ *
+ * <p>If {@link #setBeamPushUris} is called with a null Uri array,
+ * and/or {@link #setBeamPushUrisCallback} is called with a null callback,
+ * then the Uri push will be completely disabled for the specified activity(s).
+ *
+ * <p>Code example:
+ * <pre>
+ * protected void onCreate(Bundle savedInstanceState) {
+ * super.onCreate(savedInstanceState);
+ * NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
+ * if (nfcAdapter == null) return; // NFC not available on this device
+ * nfcAdapter.setBeamPushUrisCallback(callback, this);
+ * }
+ * </pre>
+ * And that is it. Only one call per activity is necessary. The Android
+ * OS will automatically release its references to the Uri(s) and the
+ * Activity object when it is destroyed if you follow this pattern.
+ *
+ * <p class="note">Do not pass in an Activity that has already been through
+ * {@link Activity#onDestroy}. This is guaranteed if you call this API
+ * during {@link Activity#onCreate}.
+ *
+ * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+ *
+ * @param callback callback, or null to disable
+ * @param activity activity for which the Uri(s) will be pushed
+ */
public void setBeamPushUrisCallback(CreateBeamUrisCallback callback, Activity activity) {
if (activity == null) {
throw new NullPointerException("activity cannot be null");
@@ -663,6 +784,10 @@
* {@link Activity#onDestroy}. This is guaranteed if you call this API
* during {@link Activity#onCreate}.
*
+ * <p class="note">For sending large content such as pictures and songs,
+ * consider using {@link #setBeamPushUris}, which switches to alternate transports
+ * such as Bluetooth to achieve a fast transfer rate.
+ *
* <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
*
* @param message NDEF message to push over NFC, or null to disable
@@ -753,7 +878,9 @@
* <p class="note">Do not pass in an Activity that has already been through
* {@link Activity#onDestroy}. This is guaranteed if you call this API
* during {@link Activity#onCreate}.
- *
+ * <p class="note">For sending large content such as pictures and songs,
+ * consider using {@link #setBeamPushUris}, which switches to alternate transports
+ * such as Bluetooth to achieve a fast transfer rate.
* <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
*
* @param callback callback, or null to disable
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index f4abda6..ab64866 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -1360,7 +1360,14 @@
*/
public Parcelable[] getVolumeList() throws RemoteException;
- public String getSecureContainerFilesystemPath(String id) throws RemoteException;
+ /**
+ * Gets the path on the filesystem for the ASEC container itself.
+ *
+ * @param cid ASEC container ID
+ * @return path to filesystem or {@code null} if it's not found
+ * @throws RemoteException
+ */
+ public String getSecureContainerFilesystemPath(String cid) throws RemoteException;
/*
* Fix permissions in a container which has just been created and populated.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ea3cab4..4c86d9c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4266,6 +4266,13 @@
"contacts_preauth_uri_expiration";
/**
+ * Prefix for SMS short code regex patterns (country code is appended).
+ * @see com.android.internal.telephony.SmsUsageMonitor
+ * @hide
+ */
+ public static final String SMS_SHORT_CODES_PREFIX = "sms_short_codes_";
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java
index fd709f2..457e66c 100644
--- a/core/java/android/speech/RecognizerIntent.java
+++ b/core/java/android/speech/RecognizerIntent.java
@@ -115,6 +115,45 @@
public static final String ACTION_WEB_SEARCH = "android.speech.action.WEB_SEARCH";
/**
+ * Starts an activity that will prompt the user for speech without requiring the user's
+ * visual attention or touch input. It will send it through a speech recognizer,
+ * and either synthesize speech for a web search result or trigger
+ * another type of action based on the user's speech.
+ *
+ * This activity may be launched while device is locked in a secure mode.
+ * Special care must be taken to ensure that the voice actions that are performed while
+ * hands free cannot compromise the device's security.
+ * The activity should check the value of the {@link #EXTRA_SECURE} extra to determine
+ * whether the device has been securely locked. If so, the activity should either restrict
+ * the set of voice actions that are permitted or require some form of secure
+ * authentication before proceeding.
+ *
+ * To ensure that the activity's user interface is visible while the lock screen is showing,
+ * the activity should set the
+ * {@link android.view.WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED} window flag.
+ * Otherwise the activity's user interface may be hidden by the lock screen. The activity
+ * should take care not to leak private information when the device is securely locked.
+ *
+ * <p>Optional extras:
+ * <ul>
+ * <li>{@link #EXTRA_SECURE}
+ * </ul>
+ */
+ public static final String ACTION_VOICE_SEARCH_HANDS_FREE =
+ "android.speech.action.VOICE_SEARCH_HANDS_FREE";
+
+ /**
+ * Optional boolean to indicate that a "hands free" voice search was performed while the device
+ * was in a secure mode. An example of secure mode is when the device's screen lock is active,
+ * and it requires some form of authentication to be unlocked.
+ *
+ * When the device is securely locked, the voice search activity should either restrict
+ * the set of voice actions that are permitted, or require some form of secure authentication
+ * before proceeding.
+ */
+ public static final String EXTRA_SECURE = "android.speech.extras.EXTRA_SECURE";
+
+ /**
* The minimum length of an utterance. We will not stop recording before this amount of time.
*
* Note that it is extremely rare you'd want to specify this value in an intent. If you don't
diff --git a/core/java/android/view/AccessibilityIterators.java b/core/java/android/view/AccessibilityIterators.java
index 386c866d..cd54f24 100644
--- a/core/java/android/view/AccessibilityIterators.java
+++ b/core/java/android/view/AccessibilityIterators.java
@@ -287,12 +287,12 @@
}
}
}
- while (start < textLength && mText.charAt(start) == '\n') {
- start++;
- }
if (start < 0) {
return null;
}
+ while (start < textLength && mText.charAt(start) == '\n') {
+ start++;
+ }
int end = start;
for (int i = end + 1; i < textLength; i++) {
end = i;
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 2048de2..411aed3 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -23,6 +23,7 @@
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
+import android.os.Looper;
import android.util.AttributeSet;
import android.util.Log;
@@ -353,7 +354,12 @@
synchronized (mLock) {
mUpdateLayer = true;
}
- postInvalidate();
+
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ invalidate();
+ } else {
+ postInvalidate();
+ }
}
};
mSurface.setOnFrameAvailableListener(mUpdateListener);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index d62f513..d94275b 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -691,13 +691,6 @@
*/
public static final int FLAG_NEEDS_MENU_KEY = 0x08000000;
- /** Window flag: *sigh* The lock screen wants to continue running its
- * animation while it is fading. A kind-of hack to allow this. Maybe
- * in the future we just make this the default behavior.
- *
- * {@hide} */
- public static final int FLAG_KEEP_SURFACE_WHILE_ANIMATING = 0x10000000;
-
/** Window flag: special flag to limit the size of the window to be
* original size ([320x480] x density). Used to create window for applications
* running under compatibility mode.
diff --git a/core/java/android/webkit/AccessibilityInjector.java b/core/java/android/webkit/AccessibilityInjector.java
index 915e2dc..cc490bd 100644
--- a/core/java/android/webkit/AccessibilityInjector.java
+++ b/core/java/android/webkit/AccessibilityInjector.java
@@ -17,25 +17,37 @@
package android.webkit;
import android.content.Context;
-import android.os.Vibrator;
+import android.os.Bundle;
+import android.os.SystemClock;
import android.provider.Settings;
import android.speech.tts.TextToSpeech;
import android.view.KeyEvent;
+import android.view.View;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.webkit.WebViewCore.EventHub;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
+import org.json.JSONException;
+import org.json.JSONObject;
import java.net.URI;
import java.net.URISyntaxException;
+import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Handles injecting accessibility JavaScript and related JavaScript -> Java
* APIs.
*/
class AccessibilityInjector {
+ // Default result returned from AndroidVox. Using true here means if the
+ // script fails, an accessibility service will always think that traversal
+ // has succeeded.
+ private static final String DEFAULT_ANDROIDVOX_RESULT = "true";
+
// The WebViewClassic this injector is responsible for managing.
private final WebViewClassic mWebViewClassic;
@@ -47,10 +59,12 @@
// The Java objects that are exposed to JavaScript.
private TextToSpeech mTextToSpeech;
+ private CallbackHandler mCallback;
// Lazily loaded helper objects.
private AccessibilityManager mAccessibilityManager;
- private AccessibilityInjectorFallback mAccessibilityInjector;
+ private AccessibilityInjectorFallback mAccessibilityInjectorFallback;
+ private JSONObject mAccessibilityJSONObject;
// Whether the accessibility script has been injected into the current page.
private boolean mAccessibilityScriptInjected;
@@ -61,8 +75,11 @@
@SuppressWarnings("unused")
private static final int ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED = 1;
- // Aliases for Java objects exposed to JavaScript.
- private static final String ALIAS_ACCESSIBILITY_JS_INTERFACE = "accessibility";
+ // Alias for TTS API exposed to JavaScript.
+ private static final String ALIAS_TTS_JS_INTERFACE = "accessibility";
+
+ // Alias for traversal callback exposed to JavaScript.
+ private static final String ALIAS_TRAVERSAL_JS_INTERFACE = "accessibilityTraversal";
// Template for JavaScript that injects a screen-reader.
private static final String ACCESSIBILITY_SCREEN_READER_JAVASCRIPT_TEMPLATE =
@@ -73,6 +90,10 @@
" document.getElementsByTagName('head')[0].appendChild(chooser);" +
" })();";
+ // Template for JavaScript that performs AndroidVox actions.
+ private static final String ACCESSIBILITY_ANDROIDVOX_TEMPLATE =
+ "cvox.AndroidVox.performAction('%1s')";
+
/**
* Creates an instance of the AccessibilityInjector based on
* {@code webViewClassic}.
@@ -99,6 +120,7 @@
}
addTtsApis();
+ addCallbackApis();
}
/**
@@ -109,6 +131,81 @@
*/
public void removeAccessibilityApisIfNecessary() {
removeTtsApis();
+ removeCallbackApis();
+ }
+
+ /**
+ * Initializes an {@link AccessibilityNodeInfo} with the actions and
+ * movement granularity levels supported by this
+ * {@link AccessibilityInjector}.
+ * <p>
+ * If an action identifier is added in this method, this
+ * {@link AccessibilityInjector} should also return {@code true} from
+ * {@link #supportsAccessibilityAction(int)}.
+ * </p>
+ *
+ * @param info The info to initialize.
+ * @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
+ */
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
+ | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
+ info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY);
+ info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY);
+ info.addAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT);
+ info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT);
+ info.addAction(AccessibilityNodeInfo.ACTION_CLICK);
+ info.setClickable(true);
+ }
+
+ /**
+ * Returns {@code true} if this {@link AccessibilityInjector} should handle
+ * the specified action.
+ *
+ * @param action An accessibility action identifier.
+ * @return {@code true} if this {@link AccessibilityInjector} should handle
+ * the specified action.
+ */
+ public boolean supportsAccessibilityAction(int action) {
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
+ case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
+ case AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT:
+ case AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT:
+ case AccessibilityNodeInfo.ACTION_CLICK:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Performs the specified accessibility action.
+ *
+ * @param action The identifier of the action to perform.
+ * @param arguments The action arguments, or {@code null} if no arguments.
+ * @return {@code true} if the action was successful.
+ * @see View#performAccessibilityAction(int, Bundle)
+ */
+ public boolean performAccessibilityAction(int action, Bundle arguments) {
+ if (!isAccessibilityEnabled()) {
+ mAccessibilityScriptInjected = false;
+ toggleFallbackAccessibilityInjector(false);
+ return false;
+ }
+
+ if (mAccessibilityScriptInjected) {
+ return sendActionToAndroidVox(action, arguments);
+ }
+
+ if (mAccessibilityInjectorFallback != null) {
+ return mAccessibilityInjectorFallback.performAccessibilityAction(action, arguments);
+ }
+
+ return false;
}
/**
@@ -140,11 +237,11 @@
return true;
}
- if (mAccessibilityInjector != null) {
+ if (mAccessibilityInjectorFallback != null) {
// if an accessibility injector is present (no JavaScript enabled or
// the site opts out injecting our JavaScript screen reader) we let
// it decide whether to act on and consume the event.
- return mAccessibilityInjector.onKeyEvent(event);
+ return mAccessibilityInjectorFallback.onKeyEvent(event);
}
return false;
@@ -157,8 +254,8 @@
* @param selectionString The selection string.
*/
public void handleSelectionChangedIfNecessary(String selectionString) {
- if (mAccessibilityInjector != null) {
- mAccessibilityInjector.onSelectionStringChange(selectionString);
+ if (mAccessibilityInjectorFallback != null) {
+ mAccessibilityInjectorFallback.onSelectionStringChange(selectionString);
}
}
@@ -206,10 +303,10 @@
* {@code false} to disable it.
*/
private void toggleFallbackAccessibilityInjector(boolean enabled) {
- if (enabled && (mAccessibilityInjector == null)) {
- mAccessibilityInjector = new AccessibilityInjectorFallback(mWebViewClassic);
+ if (enabled && (mAccessibilityInjectorFallback == null)) {
+ mAccessibilityInjectorFallback = new AccessibilityInjectorFallback(mWebViewClassic);
} else {
- mAccessibilityInjector = null;
+ mAccessibilityInjectorFallback = null;
}
}
@@ -261,7 +358,7 @@
final String pkgName = mContext.getPackageName();
mTextToSpeech = new TextToSpeech(mContext, null, null, pkgName + ".**webview**", true);
- mWebView.addJavascriptInterface(mTextToSpeech, ALIAS_ACCESSIBILITY_JS_INTERFACE);
+ mWebView.addJavascriptInterface(mTextToSpeech, ALIAS_TTS_JS_INTERFACE);
}
/**
@@ -273,12 +370,30 @@
return;
}
- mWebView.removeJavascriptInterface(ALIAS_ACCESSIBILITY_JS_INTERFACE);
+ mWebView.removeJavascriptInterface(ALIAS_TTS_JS_INTERFACE);
mTextToSpeech.stop();
mTextToSpeech.shutdown();
mTextToSpeech = null;
}
+ private void addCallbackApis() {
+ if (mCallback != null) {
+ return;
+ }
+
+ mCallback = new CallbackHandler(ALIAS_TRAVERSAL_JS_INTERFACE);
+ mWebView.addJavascriptInterface(mCallback, ALIAS_TRAVERSAL_JS_INTERFACE);
+ }
+
+ private void removeCallbackApis() {
+ if (mCallback == null) {
+ return;
+ }
+
+ mWebView.removeJavascriptInterface(ALIAS_TRAVERSAL_JS_INTERFACE);
+ mCallback = null;
+ }
+
/**
* Returns the script injection preference requested by the URL, or
* {@link #ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED} if the page has no
@@ -347,4 +462,169 @@
private boolean isAccessibilityEnabled() {
return mAccessibilityManager.isEnabled();
}
+
+ /**
+ * Packs an accessibility action into a JSON object and sends it to AndroidVox.
+ *
+ * @param action The action identifier.
+ * @param arguments The action arguments, if applicable.
+ * @return The result of the action.
+ */
+ private boolean sendActionToAndroidVox(int action, Bundle arguments) {
+ if (mAccessibilityJSONObject == null) {
+ mAccessibilityJSONObject = new JSONObject();
+ } else {
+ // Remove all keys from the object.
+ final Iterator<?> keys = mAccessibilityJSONObject.keys();
+ while (keys.hasNext()) {
+ keys.next();
+ keys.remove();
+ }
+ }
+
+ try {
+ mAccessibilityJSONObject.accumulate("action", action);
+
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
+ case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
+ final int granularity = arguments.getInt(
+ AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT);
+ mAccessibilityJSONObject.accumulate("granularity", granularity);
+ break;
+ case AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT:
+ case AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT:
+ final String element = arguments.getString(
+ AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING);
+ mAccessibilityJSONObject.accumulate("element", element);
+ break;
+ }
+ } catch (JSONException e) {
+ return false;
+ }
+
+ final String jsonString = mAccessibilityJSONObject.toString();
+ final String jsCode = String.format(ACCESSIBILITY_ANDROIDVOX_TEMPLATE, jsonString);
+ final String result = mCallback.performAction(mWebView, jsCode, DEFAULT_ANDROIDVOX_RESULT);
+
+ return ("true".equalsIgnoreCase(result));
+ }
+
+ /**
+ * Exposes result interface to JavaScript.
+ */
+ private static class CallbackHandler {
+ private static final String JAVASCRIPT_ACTION_TEMPLATE =
+ "javascript:(function() { %s.onResult(%d, %s); })();";
+
+ // Time in milliseconds to wait for a result before failing.
+ private static final long RESULT_TIMEOUT = 200;
+
+ private final AtomicInteger mResultIdCounter = new AtomicInteger();
+ private final Object mResultLock = new Object();
+ private final String mInterfaceName;
+
+ private String mResult = null;
+ private long mResultId = -1;
+
+ private CallbackHandler(String interfaceName) {
+ mInterfaceName = interfaceName;
+ }
+
+ /**
+ * Performs an action and attempts to wait for a result.
+ *
+ * @param webView The WebView to perform the action on.
+ * @param code JavaScript code that evaluates to a result.
+ * @param defaultResult The result to return if the action times out.
+ * @return The result of the action, or false if it timed out.
+ */
+ private String performAction(WebView webView, String code, String defaultResult) {
+ final int resultId = mResultIdCounter.getAndIncrement();
+ final String url = String.format(
+ JAVASCRIPT_ACTION_TEMPLATE, mInterfaceName, resultId, code);
+ webView.loadUrl(url);
+
+ return getResultAndClear(resultId, defaultResult);
+ }
+
+ /**
+ * Gets the result of a request to perform an accessibility action.
+ *
+ * @param resultId The result id to match the result with the request.
+ * @param defaultResult The default result to return on timeout.
+ * @return The result of the request.
+ */
+ private String getResultAndClear(int resultId, String defaultResult) {
+ synchronized (mResultLock) {
+ final boolean success = waitForResultTimedLocked(resultId);
+ final String result = success ? mResult : defaultResult;
+ clearResultLocked();
+ return result;
+ }
+ }
+
+ /**
+ * Clears the result state.
+ */
+ private void clearResultLocked() {
+ mResultId = -1;
+ mResult = null;
+ }
+
+ /**
+ * Waits up to a given bound for a result of a request and returns it.
+ *
+ * @param resultId The result id to match the result with the request.
+ * @return Whether the result was received.
+ */
+ private boolean waitForResultTimedLocked(int resultId) {
+ long waitTimeMillis = RESULT_TIMEOUT;
+ final long startTimeMillis = SystemClock.uptimeMillis();
+ while (true) {
+ try {
+ if (mResultId == resultId) {
+ return true;
+ }
+ if (mResultId > resultId) {
+ return false;
+ }
+ final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+ waitTimeMillis = RESULT_TIMEOUT - elapsedTimeMillis;
+ if (waitTimeMillis <= 0) {
+ return false;
+ }
+ mResultLock.wait(waitTimeMillis);
+ } catch (InterruptedException ie) {
+ /* ignore */
+ }
+ }
+ }
+
+ /**
+ * Callback exposed to JavaScript. Handles returning the result of a
+ * request to a waiting (or potentially timed out) thread.
+ *
+ * @param id The result id of the request as a {@link String}.
+ * @param result The result of the request as a {@link String}.
+ */
+ @SuppressWarnings("unused")
+ public void onResult(String id, String result) {
+ final long resultId;
+
+ try {
+ resultId = Long.parseLong(id);
+ } catch (NumberFormatException e) {
+ return;
+ }
+
+ synchronized (mResultLock) {
+ if (resultId > mResultId) {
+ mResult = result;
+ mResultId = resultId;
+ }
+ mResultLock.notifyAll();
+ }
+ }
+ }
}
diff --git a/core/java/android/webkit/AccessibilityInjectorFallback.java b/core/java/android/webkit/AccessibilityInjectorFallback.java
index 9bf52ca..4d9c26c 100644
--- a/core/java/android/webkit/AccessibilityInjectorFallback.java
+++ b/core/java/android/webkit/AccessibilityInjectorFallback.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.os.Bundle;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
@@ -23,6 +24,7 @@
import android.view.KeyEvent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.webkit.WebViewCore.EventHub;
import java.util.ArrayList;
@@ -48,7 +50,7 @@
* {@link #setCurrentAxis(int, boolean, String)}, or
* {@link #traverseCurrentAxis(int, boolean, String)}
* {@link #traverseGivenAxis(int, int, boolean, String)}
- * {@link #prefromAxisTransition(int, int, boolean, String)}
+ * {@link #performAxisTransition(int, int, boolean, String)}
* referred via the values of:
* {@link #ACTION_SET_CURRENT_AXIS},
* {@link #ACTION_TRAVERSE_CURRENT_AXIS},
@@ -72,15 +74,30 @@
private static final int ACTION_PERFORM_AXIS_TRANSITION = 3;
private static final int ACTION_TRAVERSE_DEFAULT_WEB_VIEW_BEHAVIOR_AXIS = 4;
- // the default WebView behavior abstracted as a navigation axis
+ // WebView navigation axes from WebViewCore.h, plus an additional axis for
+ // the default behavior.
+ private static final int NAVIGATION_AXIS_CHARACTER = 0;
+ private static final int NAVIGATION_AXIS_WORD = 1;
+ private static final int NAVIGATION_AXIS_SENTENCE = 2;
+ @SuppressWarnings("unused")
+ private static final int NAVIGATION_AXIS_HEADING = 3;
+ private static final int NAVIGATION_AXIS_SIBLING = 5;
+ @SuppressWarnings("unused")
+ private static final int NAVIGATION_AXIS_PARENT_FIRST_CHILD = 5;
+ private static final int NAVIGATION_AXIS_DOCUMENT = 6;
private static final int NAVIGATION_AXIS_DEFAULT_WEB_VIEW_BEHAVIOR = 7;
+ // WebView navigation directions from WebViewCore.h.
+ private static final int NAVIGATION_DIRECTION_BACKWARD = 0;
+ private static final int NAVIGATION_DIRECTION_FORWARD = 1;
+
// these are the same for all instances so make them process wide
private static ArrayList<AccessibilityWebContentKeyBinding> sBindings =
new ArrayList<AccessibilityWebContentKeyBinding>();
// handle to the WebViewClassic this injector is associated with.
private final WebViewClassic mWebView;
+ private final WebView mWebViewInternal;
// events scheduled for sending as soon as we receive the selected text
private final Stack<AccessibilityEvent> mScheduledEventStack = new Stack<AccessibilityEvent>();
@@ -104,6 +121,7 @@
*/
public AccessibilityInjectorFallback(WebViewClassic webView) {
mWebView = webView;
+ mWebViewInternal = mWebView.getWebView();
ensureWebContentKeyBindings();
}
@@ -176,7 +194,7 @@
int fromAxis = binding.getFirstArgument(i);
int toAxis = binding.getSecondArgument(i);
sendEvent = (binding.getThirdArgument(i) == 1);
- prefromAxisTransition(fromAxis, toAxis, sendEvent, contentDescription);
+ performAxisTransition(fromAxis, toAxis, sendEvent, contentDescription);
mLastDownEventHandled = true;
break;
case ACTION_TRAVERSE_DEFAULT_WEB_VIEW_BEHAVIOR_AXIS:
@@ -214,7 +232,8 @@
private void setCurrentAxis(int axis, boolean sendEvent, String contentDescription) {
mCurrentAxis = axis;
if (sendEvent) {
- AccessibilityEvent event = getPartialyPopulatedAccessibilityEvent();
+ final AccessibilityEvent event = getPartialyPopulatedAccessibilityEvent(
+ AccessibilityEvent.TYPE_ANNOUNCEMENT);
event.getText().add(String.valueOf(axis));
event.setContentDescription(contentDescription);
sendAccessibilityEvent(event);
@@ -229,7 +248,7 @@
* @param sendEvent Flag if to send an event to announce successful transition.
* @param contentDescription A description of the performed action.
*/
- private void prefromAxisTransition(int fromAxis, int toAxis, boolean sendEvent,
+ private void performAxisTransition(int fromAxis, int toAxis, boolean sendEvent,
String contentDescription) {
if (mCurrentAxis == fromAxis) {
setCurrentAxis(toAxis, sendEvent, contentDescription);
@@ -249,6 +268,62 @@
String contentDescription) {
return traverseGivenAxis(direction, mCurrentAxis, sendEvent, contentDescription);
}
+
+ boolean performAccessibilityAction(int action, Bundle arguments) {
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
+ case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
+ final int direction = getDirectionForAction(action);
+ final int axis = getAxisForGranularity(arguments.getInt(
+ AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT));
+ return traverseGivenAxis(direction, axis, true, null);
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Returns the {@link WebView}-defined direction for the given
+ * {@link AccessibilityNodeInfo}-defined action.
+ *
+ * @param action An accessibility action identifier.
+ * @return A web view navigation direction.
+ */
+ private static int getDirectionForAction(int action) {
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
+ return NAVIGATION_DIRECTION_FORWARD;
+ case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
+ return NAVIGATION_DIRECTION_BACKWARD;
+ default:
+ return -1;
+ }
+ }
+
+ /**
+ * Returns the {@link WebView}-defined axis for the given
+ * {@link AccessibilityNodeInfo}-defined granularity.
+ *
+ * @param granularity An accessibility granularity identifier.
+ * @return A web view navigation axis.
+ */
+ private static int getAxisForGranularity(int granularity) {
+ switch (granularity) {
+ case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER:
+ return NAVIGATION_AXIS_CHARACTER;
+ case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD:
+ return NAVIGATION_AXIS_WORD;
+ case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE:
+ return NAVIGATION_AXIS_SENTENCE;
+ case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH:
+ // TODO: Figure out what nextSibling() actually means.
+ return NAVIGATION_AXIS_SIBLING;
+ case AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE:
+ return NAVIGATION_AXIS_DOCUMENT;
+ default:
+ return -1;
+ }
+ }
/**
* Traverse the document along the given navigation axis.
@@ -268,7 +343,8 @@
AccessibilityEvent event = null;
if (sendEvent) {
- event = getPartialyPopulatedAccessibilityEvent();
+ event = getPartialyPopulatedAccessibilityEvent(
+ AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY);
// the text will be set upon receiving the selection string
event.setContentDescription(contentDescription);
}
@@ -296,8 +372,10 @@
return;
}
AccessibilityEvent event = mScheduledEventStack.pop();
- if (event != null) {
+ if ((event != null) && (selectionString != null)) {
event.getText().add(selectionString);
+ event.setFromIndex(0);
+ event.setToIndex(selectionString.length());
sendAccessibilityEvent(event);
}
}
@@ -323,11 +401,9 @@
* @return An accessibility event whose members are populated except its
* text and content description.
*/
- private AccessibilityEvent getPartialyPopulatedAccessibilityEvent() {
- AccessibilityEvent event = AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SELECTED);
- event.setClassName(mWebView.getClass().getName());
- event.setPackageName(mWebView.getContext().getPackageName());
- event.setEnabled(mWebView.getWebView().isEnabled());
+ private AccessibilityEvent getPartialyPopulatedAccessibilityEvent(int eventType) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
+ mWebViewInternal.onInitializeAccessibilityEvent(event);
return event;
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index ba5a417..cbb3011 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1686,6 +1686,10 @@
WebView.super.computeScroll();
}
+ public boolean super_performAccessibilityAction(int action, Bundle arguments) {
+ return WebView.super.performAccessibilityAction(action, arguments);
+ }
+
public boolean super_performLongClick() {
return WebView.super.performLongClick();
}
@@ -1938,6 +1942,11 @@
mProvider.getViewDelegate().onInitializeAccessibilityEvent(event);
}
+ @Override
+ public boolean performAccessibilityAction(int action, Bundle arguments) {
+ return mProvider.getViewDelegate().performAccessibilityAction(action, arguments);
+ }
+
/** @hide */
@Override
protected void onDrawVerticalScrollBar(Canvas canvas, Drawable scrollBar,
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index ee24e5e..7f43552 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -100,7 +100,6 @@
import android.webkit.WebViewCore.EventHub;
import android.webkit.WebViewCore.TextFieldInitData;
import android.webkit.WebViewCore.TextSelectionData;
-import android.webkit.WebViewCore.TouchHighlightData;
import android.webkit.WebViewCore.WebKitHitTest;
import android.widget.AbsoluteLayout;
import android.widget.Adapter;
@@ -274,6 +273,7 @@
newCursorPosition -= text.length() - limitedText.length();
}
super.setComposingText(limitedText, newCursorPosition);
+ updateSelection();
if (limitedText != text) {
restartInput();
int lastCaret = start + limitedText.length();
@@ -286,19 +286,44 @@
@Override
public boolean commitText(CharSequence text, int newCursorPosition) {
setComposingText(text, newCursorPosition);
- int cursorPosition = Selection.getSelectionEnd(getEditable());
- setComposingRegion(cursorPosition, cursorPosition);
+ finishComposingText();
return true;
}
@Override
public boolean deleteSurroundingText(int leftLength, int rightLength) {
- Editable editable = getEditable();
- int cursorPosition = Selection.getSelectionEnd(editable);
- int startDelete = Math.max(0, cursorPosition - leftLength);
- int endDelete = Math.min(editable.length(),
- cursorPosition + rightLength);
- setNewText(startDelete, endDelete, "");
+ // This code is from BaseInputConnection#deleteSurroundText.
+ // We have to delete the same text in webkit.
+ Editable content = getEditable();
+ int a = Selection.getSelectionStart(content);
+ int b = Selection.getSelectionEnd(content);
+
+ if (a > b) {
+ int tmp = a;
+ a = b;
+ b = tmp;
+ }
+
+ int ca = getComposingSpanStart(content);
+ int cb = getComposingSpanEnd(content);
+ if (cb < ca) {
+ int tmp = ca;
+ ca = cb;
+ cb = tmp;
+ }
+ if (ca != -1 && cb != -1) {
+ if (ca < a) a = ca;
+ if (cb > b) b = cb;
+ }
+
+ int endDelete = Math.min(content.length(), b + rightLength);
+ if (endDelete > b) {
+ setNewText(b, endDelete, "");
+ }
+ int startDelete = Math.max(0, a - leftLength);
+ if (startDelete < a) {
+ setNewText(startDelete, a, "");
+ }
return super.deleteSurroundingText(leftLength, rightLength);
}
@@ -411,6 +436,46 @@
outAttrs.imeOptions = mImeOptions;
outAttrs.hintText = mHint;
outAttrs.initialCapsMode = getCursorCapsMode(InputType.TYPE_CLASS_TEXT);
+
+ Editable editable = getEditable();
+ int selectionStart = Selection.getSelectionStart(editable);
+ int selectionEnd = Selection.getSelectionEnd(editable);
+ if (selectionStart < 0 || selectionEnd < 0) {
+ selectionStart = editable.length();
+ selectionEnd = selectionStart;
+ }
+ outAttrs.initialSelStart = selectionStart;
+ outAttrs.initialSelEnd = selectionEnd;
+ }
+
+ @Override
+ public boolean setSelection(int start, int end) {
+ boolean result = super.setSelection(start, end);
+ updateSelection();
+ return result;
+ }
+
+ @Override
+ public boolean setComposingRegion(int start, int end) {
+ boolean result = super.setComposingRegion(start, end);
+ updateSelection();
+ return result;
+ }
+
+ /**
+ * Send the selection and composing spans to the IME.
+ */
+ private void updateSelection() {
+ Editable editable = getEditable();
+ int selectionStart = Selection.getSelectionStart(editable);
+ int selectionEnd = Selection.getSelectionEnd(editable);
+ int composingStart = getComposingSpanStart(editable);
+ int composingEnd = getComposingSpanEnd(editable);
+ InputMethodManager imm = InputMethodManager.peekInstance();
+ if (imm != null) {
+ imm.updateSelection(mWebView, selectionStart, selectionEnd,
+ composingStart, composingEnd);
+ }
}
/**
@@ -1599,8 +1664,65 @@
}
@Override
+ public boolean performAccessibilityAction(int action, Bundle arguments) {
+ if (!mWebView.isEnabled()) {
+ // Only default actions are supported while disabled.
+ return mWebViewPrivate.super_performAccessibilityAction(action, arguments);
+ }
+
+ if (mAccessibilityInjector.supportsAccessibilityAction(action)) {
+ return mAccessibilityInjector.performAccessibilityAction(action, arguments);
+ }
+
+ switch (action) {
+ case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+ case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
+ final int convertedContentHeight = contentToViewY(getContentHeight());
+ final int adjustedViewHeight = getHeight() - mWebView.getPaddingTop()
+ - mWebView.getPaddingBottom();
+ final int maxScrollY = Math.max(convertedContentHeight - adjustedViewHeight, 0);
+ final boolean canScrollBackward = (getScrollY() > 0);
+ final boolean canScrollForward = ((getScrollY() - maxScrollY) > 0);
+ if ((action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) && canScrollBackward) {
+ mWebView.scrollBy(0, adjustedViewHeight);
+ return true;
+ }
+ if ((action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) && canScrollForward) {
+ mWebView.scrollBy(0, -adjustedViewHeight);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ return mWebViewPrivate.super_performAccessibilityAction(action, arguments);
+ }
+
+ @Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ if (!mWebView.isEnabled()) {
+ // Only default actions are supported while disabled.
+ return;
+ }
+
info.setScrollable(isScrollableForAccessibility());
+
+ final int convertedContentHeight = contentToViewY(getContentHeight());
+ final int adjustedViewHeight = getHeight() - mWebView.getPaddingTop()
+ - mWebView.getPaddingBottom();
+ final int maxScrollY = Math.max(convertedContentHeight - adjustedViewHeight, 0);
+ final boolean canScrollBackward = (getScrollY() > 0);
+ final boolean canScrollForward = ((getScrollY() - maxScrollY) > 0);
+
+ if (canScrollForward) {
+ info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+ }
+
+ if (canScrollForward) {
+ info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+ }
+
+ mAccessibilityInjector.onInitializeAccessibilityNodeInfo(info);
}
@Override
@@ -4965,6 +5087,7 @@
private void adjustSelectionCursors() {
if (mIsCaretSelection) {
+ syncSelectionCursors();
return; // no need to swap left and right handles.
}
@@ -7410,6 +7533,11 @@
invalidate();
}
}
+
+ @Override
+ public void clearPreviousHitTest() {
+ setHitTestResult(null);
+ }
}
private void setHitTestTypeFromUrl(String url) {
diff --git a/core/java/android/webkit/WebViewInputDispatcher.java b/core/java/android/webkit/WebViewInputDispatcher.java
index c5a86d8..feff16e 100644
--- a/core/java/android/webkit/WebViewInputDispatcher.java
+++ b/core/java/android/webkit/WebViewInputDispatcher.java
@@ -399,7 +399,6 @@
unscheduleHideTapHighlightLocked();
unscheduleShowTapHighlightLocked();
mUiCallbacks.showTapHighlight(true);
- scheduleHideTapHighlightLocked();
}
private void scheduleShowTapHighlightLocked() {
@@ -466,13 +465,13 @@
return;
}
mPostClickScheduled = false;
- showTapCandidateLocked();
MotionEvent event = mPostTouchStream.getLastEvent();
if (event == null || event.getAction() != MotionEvent.ACTION_UP) {
return;
}
+ showTapCandidateLocked();
MotionEvent eventToEnqueue = MotionEvent.obtainNoHistory(event);
DispatchEvent d = obtainDispatchEventLocked(eventToEnqueue, EVENT_TYPE_CLICK, 0,
mPostLastWebKitXOffset, mPostLastWebKitYOffset, mPostLastWebKitScale);
@@ -511,6 +510,7 @@
}
private void enqueueHitTestLocked(MotionEvent event) {
+ mUiCallbacks.clearPreviousHitTest();
MotionEvent eventToEnqueue = MotionEvent.obtainNoHistory(event);
DispatchEvent d = obtainDispatchEventLocked(eventToEnqueue, EVENT_TYPE_HIT_TEST, 0,
mPostLastWebKitXOffset, mPostLastWebKitYOffset, mPostLastWebKitScale);
@@ -553,12 +553,17 @@
mIsTapCandidate = true;
mInitialDownX = event.getX();
mInitialDownY = event.getY();
- scheduleShowTapHighlightLocked();
enqueueHitTestLocked(event);
+ if (mIsDoubleTapCandidate) {
+ hideTapCandidateLocked();
+ } else {
+ scheduleShowTapHighlightLocked();
+ }
} else if (action == MotionEvent.ACTION_UP) {
unscheduleLongPressLocked();
if (isClickCandidateLocked(event)) {
if (mIsDoubleTapCandidate) {
+ hideTapCandidateLocked();
enqueueDoubleTapLocked(event);
} else {
scheduleClickLocked();
@@ -666,6 +671,10 @@
if (event != null && recycleEvent) {
event.recycle();
}
+
+ if (eventType == EVENT_TYPE_CLICK) {
+ scheduleHideTapHighlightLocked();
+ }
}
}
}
@@ -802,6 +811,10 @@
d.mEvent = null; // retain ownership of event, don't recycle it yet
}
recycleDispatchEventLocked(d);
+
+ if (eventType == EVENT_TYPE_CLICK) {
+ scheduleHideTapHighlightLocked();
+ }
}
// Handle the event.
@@ -1050,6 +1063,12 @@
* @param show True if it should show the highlight, false if it should hide it
*/
public void showTapHighlight(boolean show);
+
+ /**
+ * Called when we are sending a new EVENT_TYPE_HIT_TEST to WebKit, so
+ * previous hit tests should be cleared as they are obsolete.
+ */
+ public void clearPreviousHitTest();
}
/* Implemented by {@link WebViewCore} to perform operations on the web kit thread. */
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 74a215c..867ee54 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -276,6 +276,8 @@
public void onInitializeAccessibilityEvent(AccessibilityEvent event);
+ public boolean performAccessibilityAction(int action, Bundle arguments);
+
public void setOverScrollMode(int mode);
public void setScrollBarStyle(int style);
diff --git a/core/jni/android/graphics/BitmapFactory.h b/core/jni/android/graphics/BitmapFactory.h
index 9ae61bc..f2aaab7 100644
--- a/core/jni/android/graphics/BitmapFactory.h
+++ b/core/jni/android/graphics/BitmapFactory.h
@@ -16,6 +16,7 @@
extern jfieldID gOptions_heightFieldID;
extern jfieldID gOptions_mimeFieldID;
extern jfieldID gOptions_mCancelID;
+extern jfieldID gOptions_bitmapFieldID;
jstring getMimeTypeString(JNIEnv* env, SkImageDecoder::Format format);
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index dd8e84f..b218bcd 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -29,6 +29,7 @@
#include "CreateJavaOutputStreamAdaptor.h"
#include "Utils.h"
#include "JNIHelp.h"
+#include "SkTScopedPtr.h"
#include <android_runtime/AndroidRuntime.h>
#include "android_util_Binder.h"
@@ -180,7 +181,8 @@
* reportSizeToVM not supported
*/
static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd,
- int start_x, int start_y, int width, int height, jobject options) {
+ int start_x, int start_y, int width, int height, jobject options) {
+ jobject tileBitmap = NULL;
SkImageDecoder *decoder = brd->getDecoder();
int sampleSize = 1;
SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
@@ -199,12 +201,12 @@
doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
preferQualityOverSpeed = env->GetBooleanField(options,
gOptions_preferQualityOverSpeedFieldID);
+ // Get the bitmap for re-use if it exists.
+ tileBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
}
decoder->setDitherImage(doDither);
decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
- SkBitmap* bitmap = new SkBitmap;
- SkAutoTDelete<SkBitmap> adb(bitmap);
AutoDecoderCancel adc(options, decoder);
// To fix the race condition in case "requestCancelDecode"
@@ -219,6 +221,17 @@
region.fTop = start_y;
region.fRight = start_x + width;
region.fBottom = start_y + height;
+ SkBitmap* bitmap = NULL;
+ SkTScopedPtr<SkBitmap> adb;
+
+ if (tileBitmap != NULL) {
+ // Re-use bitmap.
+ bitmap = GraphicsJNI::getNativeBitmap(env, tileBitmap);
+ }
+ if (bitmap == NULL) {
+ bitmap = new SkBitmap;
+ adb.reset(bitmap);
+ }
if (!brd->decodeRegion(bitmap, region, prefConfig, sampleSize)) {
return nullObjectReturn("decoder->decodeRegion returned false");
@@ -235,12 +248,12 @@
getMimeTypeString(env, decoder->getFormat()));
}
- // detach bitmap from its autodeleter, since we want to own it now
- adb.detach();
+ if (tileBitmap != NULL) {
+ return tileBitmap;
+ }
- SkPixelRef* pr = bitmap->pixelRef();
- // promise we will never change our pixels (great for sharing and pictures)
- pr->setImmutable();
+ // detach bitmap from its autodeleter, since we want to own it now
+ adb.release();
JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator();
jbyteArray buff = allocator->getStorageObjAndReset();
diff --git a/core/res/res/drawable-hdpi/activity_picker_bg_activated.9.png b/core/res/res/drawable-hdpi/activity_picker_bg_activated.9.png
new file mode 100644
index 0000000..e591a7b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/activity_picker_bg_activated.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/activity_picker_bg_focused.9.png b/core/res/res/drawable-hdpi/activity_picker_bg_focused.9.png
new file mode 100644
index 0000000..ea27290d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/activity_picker_bg_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_thumb_activated_holo_dark.9.png b/core/res/res/drawable-hdpi/switch_thumb_activated_holo_dark.9.png
index 2fc475b..9c5147e 100644
--- a/core/res/res/drawable-hdpi/switch_thumb_activated_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/switch_thumb_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_thumb_activated_holo_light.9.png b/core/res/res/drawable-hdpi/switch_thumb_activated_holo_light.9.png
index 5adecf1..9c5147e 100644
--- a/core/res/res/drawable-hdpi/switch_thumb_activated_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/switch_thumb_activated_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_thumb_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/switch_thumb_disabled_holo_dark.9.png
index 457fa84..a257e26 100644
--- a/core/res/res/drawable-hdpi/switch_thumb_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/switch_thumb_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_thumb_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/switch_thumb_disabled_holo_light.9.png
index c3cfc29..a257e26 100644
--- a/core/res/res/drawable-hdpi/switch_thumb_disabled_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/switch_thumb_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_thumb_holo_dark.9.png b/core/res/res/drawable-hdpi/switch_thumb_holo_dark.9.png
index d0e1806..dd999d6 100644
--- a/core/res/res/drawable-hdpi/switch_thumb_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/switch_thumb_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_thumb_holo_light.9.png b/core/res/res/drawable-hdpi/switch_thumb_holo_light.9.png
index c30506d..dd999d6 100644
--- a/core/res/res/drawable-hdpi/switch_thumb_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/switch_thumb_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_thumb_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/switch_thumb_pressed_holo_dark.9.png
index 9106687..ea54380 100644
--- a/core/res/res/drawable-hdpi/switch_thumb_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/switch_thumb_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_thumb_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/switch_thumb_pressed_holo_light.9.png
index 2bdda56..ea54380 100644
--- a/core/res/res/drawable-hdpi/switch_thumb_pressed_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/switch_thumb_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/activity_picker_bg_activated.9.png b/core/res/res/drawable-mdpi/activity_picker_bg_activated.9.png
new file mode 100644
index 0000000..7dfea4c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/activity_picker_bg_activated.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/activity_picker_bg_focused.9.png b/core/res/res/drawable-mdpi/activity_picker_bg_focused.9.png
new file mode 100644
index 0000000..99b0279
--- /dev/null
+++ b/core/res/res/drawable-mdpi/activity_picker_bg_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_thumb_activated_holo_dark.9.png b/core/res/res/drawable-mdpi/switch_thumb_activated_holo_dark.9.png
index 0787d16..3d7c236 100644
--- a/core/res/res/drawable-mdpi/switch_thumb_activated_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/switch_thumb_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_thumb_activated_holo_light.9.png b/core/res/res/drawable-mdpi/switch_thumb_activated_holo_light.9.png
index 0157e68..3d7c236 100644
--- a/core/res/res/drawable-mdpi/switch_thumb_activated_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/switch_thumb_activated_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_thumb_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/switch_thumb_disabled_holo_dark.9.png
index 51b14d0..82f05d6 100644
--- a/core/res/res/drawable-mdpi/switch_thumb_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/switch_thumb_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_thumb_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/switch_thumb_disabled_holo_light.9.png
index d68568a..82f05d6 100644
--- a/core/res/res/drawable-mdpi/switch_thumb_disabled_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/switch_thumb_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_thumb_holo_dark.9.png b/core/res/res/drawable-mdpi/switch_thumb_holo_dark.9.png
index 6bf153a..9bc7a68 100644
--- a/core/res/res/drawable-mdpi/switch_thumb_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/switch_thumb_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_thumb_holo_light.9.png b/core/res/res/drawable-mdpi/switch_thumb_holo_light.9.png
index 0d98983..9bc7a68 100644
--- a/core/res/res/drawable-mdpi/switch_thumb_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/switch_thumb_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_thumb_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/switch_thumb_pressed_holo_dark.9.png
index 3cee7b8..670dc2e 100644
--- a/core/res/res/drawable-mdpi/switch_thumb_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/switch_thumb_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_thumb_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/switch_thumb_pressed_holo_light.9.png
index 43a7c4c..670dc2e 100644
--- a/core/res/res/drawable-mdpi/switch_thumb_pressed_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/switch_thumb_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/activity_picker_bg_activated.9.png b/core/res/res/drawable-xhdpi/activity_picker_bg_activated.9.png
new file mode 100644
index 0000000..f01a79e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/activity_picker_bg_activated.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/activity_picker_bg_focused.9.png b/core/res/res/drawable-xhdpi/activity_picker_bg_focused.9.png
new file mode 100644
index 0000000..7bea197
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/activity_picker_bg_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_thumb_activated_holo_dark.9.png b/core/res/res/drawable-xhdpi/switch_thumb_activated_holo_dark.9.png
index a0e6b20..ca48bd8 100644
--- a/core/res/res/drawable-xhdpi/switch_thumb_activated_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/switch_thumb_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_thumb_activated_holo_light.9.png b/core/res/res/drawable-xhdpi/switch_thumb_activated_holo_light.9.png
index 88235fe..ca48bd8 100644
--- a/core/res/res/drawable-xhdpi/switch_thumb_activated_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/switch_thumb_activated_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_thumb_disabled_holo_dark.9.png b/core/res/res/drawable-xhdpi/switch_thumb_disabled_holo_dark.9.png
index 04fb9a1..c3d80f0 100644
--- a/core/res/res/drawable-xhdpi/switch_thumb_disabled_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/switch_thumb_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_thumb_disabled_holo_light.9.png b/core/res/res/drawable-xhdpi/switch_thumb_disabled_holo_light.9.png
index 06a14f3..c3d80f0 100644
--- a/core/res/res/drawable-xhdpi/switch_thumb_disabled_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/switch_thumb_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_thumb_holo_dark.9.png b/core/res/res/drawable-xhdpi/switch_thumb_holo_dark.9.png
index af7d631..df236df 100644
--- a/core/res/res/drawable-xhdpi/switch_thumb_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/switch_thumb_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_thumb_holo_light.9.png b/core/res/res/drawable-xhdpi/switch_thumb_holo_light.9.png
index d6ab3ea..df236df 100644
--- a/core/res/res/drawable-xhdpi/switch_thumb_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/switch_thumb_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_thumb_pressed_holo_dark.9.png b/core/res/res/drawable-xhdpi/switch_thumb_pressed_holo_dark.9.png
index 5a8e807..4acb32b 100644
--- a/core/res/res/drawable-xhdpi/switch_thumb_pressed_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/switch_thumb_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_thumb_pressed_holo_light.9.png b/core/res/res/drawable-xhdpi/switch_thumb_pressed_holo_light.9.png
index 392f3dc..4acb32b 100644
--- a/core/res/res/drawable-xhdpi/switch_thumb_pressed_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/switch_thumb_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable/activity_picker_bg.xml b/core/res/res/drawable/activity_picker_bg.xml
new file mode 100644
index 0000000..a471c94
--- /dev/null
+++ b/core/res/res/drawable/activity_picker_bg.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_window_focused="false" android:drawable="@color/transparent" />
+
+ <!-- Even though these two point to the same resource, have two states so the drawable will invalidate itself when coming out of pressed state. -->
+ <item android:state_focused="true" android:state_enabled="false" android:state_pressed="true" android:drawable="@drawable/list_selector_disabled_holo_light" />
+ <item android:state_focused="true" android:state_enabled="false" android:drawable="@drawable/list_selector_disabled_holo_light" />
+ <item android:state_focused="true" android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition_holo_light" />
+ <item android:state_focused="false" android:state_pressed="true" android:drawable="@drawable/list_selector_background_transition_holo_light" />
+ <item android:state_focused="true" android:drawable="@drawable/activity_picker_bg_focused" />
+ <item android:state_activated="true" android:drawable="@drawable/activity_picker_bg_activated" />
+ <item android:drawable="@color/transparent" />
+
+</selector>
diff --git a/core/res/res/drawable/switch_track_holo_dark.xml b/core/res/res/drawable/switch_track_holo_dark.xml
index c9a940d..5f796c1 100644
--- a/core/res/res/drawable/switch_track_holo_dark.xml
+++ b/core/res/res/drawable/switch_track_holo_dark.xml
@@ -15,7 +15,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false" android:drawable="@drawable/switch_bg_disabled_holo_dark" />
<item android:state_focused="true" android:drawable="@drawable/switch_bg_focused_holo_dark" />
<item android:drawable="@drawable/switch_bg_holo_dark" />
</selector>
diff --git a/core/res/res/drawable/switch_track_holo_light.xml b/core/res/res/drawable/switch_track_holo_light.xml
index 98e53b5..39bee37 100644
--- a/core/res/res/drawable/switch_track_holo_light.xml
+++ b/core/res/res/drawable/switch_track_holo_light.xml
@@ -15,7 +15,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="false" android:drawable="@drawable/switch_bg_disabled_holo_light" />
<item android:state_focused="true" android:drawable="@drawable/switch_bg_focused_holo_light" />
<item android:drawable="@drawable/switch_bg_holo_light" />
</selector>
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock.xml b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock.xml
index efd406d..e6829a9 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock.xml
@@ -57,8 +57,6 @@
android:layout_height="wrap_content"
android:layout_marginLeft="50dip"
android:layout_marginTop="50dip"
- android:layout_marginBottom="100dip"
- android:layout_marginRight="64dip"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"/>
diff --git a/core/res/res/layout/resolve_list_item.xml b/core/res/res/layout/resolve_list_item.xml
index abeb7ba..1d464aa 100644
--- a/core/res/res/layout/resolve_list_item.xml
+++ b/core/res/res/layout/resolve_list_item.xml
@@ -22,7 +22,7 @@
android:orientation="vertical"
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:background="?android:attr/activatedBackgroundIndicator"
+ android:background="@android:drawable/activity_picker_bg"
android:padding="16dp">
<!-- Extended activity info to distinguish between duplicate activity names -->
diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd
index 847681b..82b5e29 100644
--- a/docs/html/guide/topics/resources/providing-resources.jd
+++ b/docs/html/guide/topics/resources/providing-resources.jd
@@ -533,13 +533,19 @@
<td>
<code>car</code><br/>
<code>desk</code><br/>
- <code>television</code>
+ <code>television<br/>
+ <code>appliance</code>
</td>
<td>
<ul class="nolist">
<li>{@code car}: Device is displaying in a car dock</li>
<li>{@code desk}: Device is displaying in a desk dock</li>
- <li>{@code television}: Device is displaying on a television</li>
+ <li>{@code television}: Device is displaying on a television, providing
+ a "ten foot" experience where its UI is on a large screen that the
+ user is far away from, primarily oriented around DPAD or other
+ non-pointer interaction</li>
+ <li>{@code appliance}: Device is serving as an appliance, with
+ no display</li>
</ul>
<p><em>Added in API level 8, television added in API 13.</em></p>
<p>This can change during the life of your application if the user places the device in a
@@ -610,15 +616,13 @@
<td>Touchscreen type</td>
<td>
<code>notouch</code><br/>
- <code>stylus</code><br/>
<code>finger</code>
</td>
<td>
<ul class="nolist">
<li>{@code notouch}: Device does not have a touchscreen.</li>
- <li>{@code stylus}: Device has a resistive touchscreen that's suited for use with a
-stylus.</li>
- <li>{@code finger}: Device has a touchscreen.</li>
+ <li>{@code finger}: Device has a touchscreen that is intended to
+ be used through direction interaction of the user's finger.</li>
</ul>
<p>Also see the {@link android.content.res.Configuration#touchscreen} configuration field,
which indicates the type of touchscreen on the device.</p>
@@ -694,7 +698,7 @@
field, which indicates whether navigation keys are hidden.</p>
</td>
</tr>
- <tr id="TouchQualifier">
+ <tr id="NavigationQualifier">
<td>Primary non-touch navigation method</td>
<td>
<code>nonav</code><br/>
diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java
index 496e0c7..c1d3407 100644
--- a/graphics/java/android/graphics/BitmapRegionDecoder.java
+++ b/graphics/java/android/graphics/BitmapRegionDecoder.java
@@ -180,9 +180,9 @@
*/
public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) {
checkRecycled("decodeRegion called on recycled region decoder");
- if (rect.left < 0 || rect.top < 0 || rect.right > getWidth()
- || rect.bottom > getHeight())
- throw new IllegalArgumentException("rectangle is not inside the image");
+ if (rect.right <= 0 || rect.bottom <= 0 || rect.left >= getWidth()
+ || rect.top >= getHeight())
+ throw new IllegalArgumentException("rectangle is outside the image");
return nativeDecodeRegion(mNativeBitmapRegionDecoder, rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top, options);
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 91d8bc1..1299574 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -403,7 +403,7 @@
* the named provider. Periodically, the supplied LocationListener will
* be called with the current Location or with status updates.
*
- * <p> It may take a while to receive the most recent location. If
+ * <p> It may take a while to receive the first location update. If
* an immediate location is required, applications may use the
* {@link #getLastKnownLocation(String)} method.
*
@@ -413,32 +413,61 @@
* the {@link LocationListener#onProviderEnabled(String)} method will
* be called and location updates will start again.
*
- * <p> The frequency of notification may be controlled using the
- * minTime and minDistance parameters. If minTime is greater than 0,
- * the LocationManager could potentially rest for minTime milliseconds
- * between location updates to conserve power. If minDistance is greater than 0,
- * a location will only be broadcasted if the device moves by minDistance meters.
- * To obtain notifications as frequently as possible, set both parameters to 0.
+ * <p> The update interval can be controlled using the minTime parameter.
+ * The elapsed time between location updates will never be less than
+ * minTime, although it can be more depending on the Location Provider
+ * implementation and the update interval requested by other applications.
*
- * <p> Background services should be careful about setting a sufficiently high
- * minTime so that the device doesn't consume too much power by keeping the
- * GPS or wireless radios on all the time. In particular, values under 60000ms
- * are not recommended.
+ * <p> Choosing a sensible value for minTime is important to conserve
+ * battery life. Each location update requires power from
+ * GPS, WIFI, Cell and other radios. Select a minTime value as high as
+ * possible while still providing a reasonable user experience.
+ * If your application is not in the foreground and showing
+ * location to the user then your application should avoid using an active
+ * provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}),
+ * but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes)
+ * or greater. If your application is in the foreground and showing
+ * location to the user then it is appropriate to select a faster
+ * update interval.
+ *
+ * <p> The minDistance parameter can also be used to control the
+ * frequency of location updates. If it is greater than 0 then the
+ * location provider will only send your application an update when
+ * the location has changed by at least minDistance meters, AND
+ * at least minTime milliseconds have passed. However it is more
+ * difficult for location providers to save power using the minDistance
+ * parameter, so minTime should be the primary tool to conserving battery
+ * life.
+ *
+ * <p> If your application wants to passively observe location
+ * updates triggered by other applications, but not consume
+ * any additional power otherwise, then use the {@link #PASSIVE_PROVIDER}
+ * This provider does not actively turn on or modify active location
+ * providers, so you do not need to be as careful about minTime and
+ * minDistance. However if your application performs heavy work
+ * on a location update (such as network activity) then you should
+ * select non-zero values for minTime and/or minDistance to rate-limit
+ * your update frequency in the case another application enables a
+ * location provider with extremely fast updates.
*
* <p> The calling thread must be a {@link android.os.Looper} thread such as
* the main thread of the calling Activity.
*
+ * <p class="note"> Prior to Jellybean, the minTime parameter was
+ * only a hint, and some location provider implementations ignored it.
+ * From Jellybean and onwards it is mandatory for Android compatible
+ * devices to observe both the minTime and minDistance parameters.
+ *
* @param provider the name of the provider with which to register
- * @param minTime the minimum time interval for notifications, in
- * milliseconds. This field is only used as a hint to conserve power, and actual
- * time between location updates may be greater or lesser than this value.
- * @param minDistance the minimum distance interval for notifications,
- * in meters
+ * @param minTime minimum time interval between location updates, in milliseconds
+ * @param minDistance minimum distance between location updates, in meters
* @param listener a {#link LocationListener} whose
* {@link LocationListener#onLocationChanged} method will be called for
* each location update
*
- * @throws IllegalArgumentException if provider or listener is null
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * on this device
+ * @throws IllegalArgumentException if listener is null
* @throws RuntimeException if the calling thread has no Looper
* @throws SecurityException if no suitable permission is present for the provider.
*/
@@ -458,7 +487,7 @@
* the named provider. Periodically, the supplied LocationListener will
* be called with the current Location or with status updates.
*
- * <p> It may take a while to receive the most recent location. If
+ * <p> It may take a while to receive the first location update. If
* an immediate location is required, applications may use the
* {@link #getLastKnownLocation(String)} method.
*
@@ -468,32 +497,59 @@
* the {@link LocationListener#onProviderEnabled(String)} method will
* be called and location updates will start again.
*
- * <p> The frequency of notification may be controlled using the
- * minTime and minDistance parameters. If minTime is greater than 0,
- * the LocationManager could potentially rest for minTime milliseconds
- * between location updates to conserve power. If minDistance is greater than 0,
- * a location will only be broadcasted if the device moves by minDistance meters.
- * To obtain notifications as frequently as possible, set both parameters to 0.
+ * <p> The update interval can be controlled using the minTime parameter.
+ * The elapsed time between location updates will never be less than
+ * minTime, although it can be more depending on the Location Provider
+ * implementation and the update interval requested by other applications.
*
- * <p> Background services should be careful about setting a sufficiently high
- * minTime so that the device doesn't consume too much power by keeping the
- * GPS or wireless radios on all the time. In particular, values under 60000ms
- * are not recommended.
+ * <p> Choosing a sensible value for minTime is important to conserve
+ * battery life. Each location update requires power from
+ * GPS, WIFI, Cell and other radios. Select a minTime value as high as
+ * possible while still providing a reasonable user experience.
+ * If your application is not in the foreground and showing
+ * location to the user then your application should avoid using an active
+ * provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}),
+ * but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes)
+ * or greater. If your application is in the foreground and showing
+ * location to the user then it is appropriate to select a faster
+ * update interval.
+ *
+ * <p> The minDistance parameter can also be used to control the
+ * frequency of location updates. If it is greater than 0 then the
+ * location provider will only send your application an update when
+ * the location has changed by at least minDistance meters, AND
+ * at least minTime milliseconds have passed. However it is more
+ * difficult for location providers to save power using the minDistance
+ * parameter, so minTime should be the primary tool to conserving battery
+ * life.
+ *
+ * <p> If your application wants to passively observe location
+ * updates triggered by other applications, but not consume
+ * any additional power otherwise, then use the {@link #PASSIVE_PROVIDER}
+ * This provider does not actively turn on or modify active location
+ * providers, so you do not need to be as careful about minTime and
+ * minDistance. However if your application performs heavy work
+ * on a location update (such as network activity) then you should
+ * select non-zero values for minTime and/or minDistance to rate-limit
+ * your update frequency in the case another application enables a
+ * location provider with extremely fast updates.
*
* <p> The supplied Looper is used to implement the callback mechanism.
*
+ * <p class="note"> Prior to Jellybean, the minTime parameter was
+ * only a hint, and some location provider implementations ignored it.
+ * From Jellybean and onwards it is mandatory for Android compatible
+ * devices to observe both the minTime and minDistance parameters.
+ *
* @param provider the name of the provider with which to register
- * @param minTime the minimum time interval for notifications, in
- * milliseconds. This field is only used as a hint to conserve power, and actual
- * time between location updates may be greater or lesser than this value.
- * @param minDistance the minimum distance interval for notifications,
- * in meters
+ * @param minTime minimum time interval between location updates, in milliseconds
+ * @param minDistance minimum distance between location updates, in meters
* @param listener a {#link LocationListener} whose
* {@link LocationListener#onLocationChanged} method will be called for
* each location update
* @param looper a Looper object whose message queue will be used to
- * implement the callback mechanism.
- * If looper is null then the callbacks will be called on the main thread.
+ * implement the callback mechanism, or null to make callbacks on the
+ * main thread
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if listener is null
@@ -513,10 +569,10 @@
/**
* Registers the current activity to be notified periodically based on
- * the specified criteria. Periodically, the supplied LocationListener will
+ * the supplied criteria. Periodically, the supplied LocationListener will
* be called with the current Location or with status updates.
*
- * <p> It may take a while to receive the most recent location. If
+ * <p> It may take a while to receive the first location update. If
* an immediate location is required, applications may use the
* {@link #getLastKnownLocation(String)} method.
*
@@ -526,38 +582,53 @@
* the {@link LocationListener#onProviderEnabled(String)} method will
* be called and location updates will start again.
*
- * <p> The frequency of notification may be controlled using the
- * minTime and minDistance parameters. If minTime is greater than 0,
- * the LocationManager could potentially rest for minTime milliseconds
- * between location updates to conserve power. If minDistance is greater than 0,
- * a location will only be broadcasted if the device moves by minDistance meters.
- * To obtain notifications as frequently as possible, set both parameters to 0.
+ * <p> The update interval can be controlled using the minTime parameter.
+ * The elapsed time between location updates will never be less than
+ * minTime, although it can be more depending on the Location Provider
+ * implementation and the update interval requested by other applications.
*
- * <p> Background services should be careful about setting a sufficiently high
- * minTime so that the device doesn't consume too much power by keeping the
- * GPS or wireless radios on all the time. In particular, values under 60000ms
- * are not recommended.
+ * <p> Choosing a sensible value for minTime is important to conserve
+ * battery life. Each location update requires power from
+ * GPS, WIFI, Cell and other radios. Select a minTime value as high as
+ * possible while still providing a reasonable user experience.
+ * If your application is not in the foreground and showing
+ * location to the user then your application should avoid using an active
+ * provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}),
+ * but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes)
+ * or greater. If your application is in the foreground and showing
+ * location to the user then it is appropriate to select a faster
+ * update interval.
+ *
+ * <p> The minDistance parameter can also be used to control the
+ * frequency of location updates. If it is greater than 0 then the
+ * location provider will only send your application an update when
+ * the location has changed by at least minDistance meters, AND
+ * at least minTime milliseconds have passed. However it is more
+ * difficult for location providers to save power using the minDistance
+ * parameter, so minTime should be the primary tool to conserving battery
+ * life.
*
* <p> The supplied Looper is used to implement the callback mechanism.
*
- * @param minTime the minimum time interval for notifications, in
- * milliseconds. This field is only used as a hint to conserve power, and actual
- * time between location updates may be greater or lesser than this value.
- * @param minDistance the minimum distance interval for notifications,
- * in meters
+ * <p class="note"> Prior to Jellybean, the minTime parameter was
+ * only a hint, and some location provider implementations ignored it.
+ * From Jellybean and onwards it is mandatory for Android compatible
+ * devices to observe both the minTime and minDistance parameters.
+ *
+ * @param minTime minimum time interval between location updates, in milliseconds
+ * @param minDistance minimum distance between location updates, in meters
* @param criteria contains parameters for the location manager to choose the
* appropriate provider and parameters to compute the location
* @param listener a {#link LocationListener} whose
* {@link LocationListener#onLocationChanged} method will be called for
* each location update
* @param looper a Looper object whose message queue will be used to
- * implement the callback mechanism.
- * If looper is null then the callbacks will be called on the main thread.
+ * implement the callback mechanism, or null to make callbacks on the
+ * main thread.
*
* @throws IllegalArgumentException if criteria is null
* @throws IllegalArgumentException if listener is null
- * @throws SecurityException if no suitable permission is present to access
- * the location services.
+ * @throws SecurityException if no suitable permission is present for the provider.
*/
public void requestLocationUpdates(long minTime, float minDistance,
Criteria criteria, LocationListener listener, Looper looper) {
@@ -598,43 +669,74 @@
* the named provider. Periodically, the supplied PendingIntent will
* be broadcast with the current Location or with status updates.
*
- * <p> Location updates are sent with a key of KEY_LOCATION_CHANGED and a Location value.
+ * <p> Location updates are sent with a key of
+ * {@link #KEY_LOCATION_CHANGED} and a {@link android.location.Location} value.
*
- * <p> It may take a while to receive the most recent location. If
+ * <p> It may take a while to receive the first location update. If
* an immediate location is required, applications may use the
* {@link #getLastKnownLocation(String)} method.
*
- * <p> The frequency of notification or new locations may be controlled using the
- * minTime and minDistance parameters. If minTime is greater than 0,
- * the LocationManager could potentially rest for minTime milliseconds
- * between location updates to conserve power. If minDistance is greater than 0,
- * a location will only be broadcast if the device moves by minDistance meters.
- * To obtain notifications as frequently as possible, set both parameters to 0.
+ * <p> The update interval can be controlled using the minTime parameter.
+ * The elapsed time between location updates will never be less than
+ * minTime, although it can be more depending on the Location Provider
+ * implementation and the update interval requested by other applications.
*
- * <p> Background services should be careful about setting a sufficiently high
- * minTime so that the device doesn't consume too much power by keeping the
- * GPS or wireless radios on all the time. In particular, values under 60000ms
- * are not recommended.
+ * <p> Choosing a sensible value for minTime is important to conserve
+ * battery life. Each location update requires power from
+ * GPS, WIFI, Cell and other radios. Select a minTime value as high as
+ * possible while still providing a reasonable user experience.
+ * If your application is not in the foreground and showing
+ * location to the user then your application should avoid using an active
+ * provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}),
+ * but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes)
+ * or greater. If your application is in the foreground and showing
+ * location to the user then it is appropriate to select a faster
+ * update interval.
*
- * <p> In case the provider is disabled by the user, updates will stop,
- * and an intent will be sent with an extra with key KEY_PROVIDER_ENABLED and a boolean value
- * of false. If the provider is re-enabled, an intent will be sent with an
- * extra with key KEY_PROVIDER_ENABLED and a boolean value of true and location updates will
- * start again.
+ * <p> The minDistance parameter can also be used to control the
+ * frequency of location updates. If it is greater than 0 then the
+ * location provider will only send your application an update when
+ * the location has changed by at least minDistance meters, AND
+ * at least minTime milliseconds have passed. However it is more
+ * difficult for location providers to save power using the minDistance
+ * parameter, so minTime should be the primary tool to conserving battery
+ * life.
*
- * <p> If the provider's status changes, an intent will be sent with an extra with key
- * KEY_STATUS_CHANGED and an integer value indicating the new status. Any extras associated
- * with the status update will be sent as well.
+ * <p> If your application wants to passively observe location
+ * updates triggered by other applications, but not consume
+ * any additional power otherwise, then use the {@link #PASSIVE_PROVIDER}
+ * This provider does not actively turn on or modify active location
+ * providers, so you do not need to be as careful about minTime and
+ * minDistance. However if your application performs heavy work
+ * on a location update (such as network activity) then you should
+ * select non-zero values for minTime and/or minDistance to rate-limit
+ * your update frequency in the case another application enables a
+ * location provider with extremely fast updates.
+ *
+ * <p> If the provider is disabled by the user, updates will stop,
+ * and an intent will be sent with an extra with key
+ * {@link #KEY_PROVIDER_ENABLED} and a boolean value of false.
+ * If the provider is re-enabled, an intent will be sent with an
+ * extra with key {@link #KEY_PROVIDER_ENABLED} and a boolean value of
+ * true and location updates will start again.
+ *
+ * <p> If the provider's status changes, an intent will be sent with
+ * an extra with key {@link #KEY_STATUS_CHANGED} and an integer value
+ * indicating the new status. Any extras associated with the status
+ * update will be sent as well.
+ *
+ * <p class="note"> Prior to Jellybean, the minTime parameter was
+ * only a hint, and some location provider implementations ignored it.
+ * From Jellybean and onwards it is mandatory for Android compatible
+ * devices to observe both the minTime and minDistance parameters.
*
* @param provider the name of the provider with which to register
- * @param minTime the minimum time interval for notifications, in
- * milliseconds. This field is only used as a hint to conserve power, and actual
- * time between location updates may be greater or lesser than this value.
- * @param minDistance the minimum distance interval for notifications,
- * in meters
+ * @param minTime minimum time interval between location updates, in milliseconds
+ * @param minDistance minimum distance between location updates, in meters
* @param intent a {#link PendingIntent} to be sent for each location update
*
* @throws IllegalArgumentException if provider is null or doesn't exist
+ * on this device
* @throws IllegalArgumentException if intent is null
* @throws SecurityException if no suitable permission is present for the provider.
*/
@@ -651,51 +753,71 @@
/**
* Registers the current activity to be notified periodically based on
- * the specified criteria. Periodically, the supplied PendingIntent will
+ * the supplied criteria. Periodically, the supplied PendingIntent will
* be broadcast with the current Location or with status updates.
*
- * <p> Location updates are sent with a key of KEY_LOCATION_CHANGED and a Location value.
+ * <p> Location updates are sent with a key of
+ * {@link #KEY_LOCATION_CHANGED} and a {@link android.location.Location} value.
*
- * <p> It may take a while to receive the most recent location. If
+ * <p> It may take a while to receive the first location update. If
* an immediate location is required, applications may use the
* {@link #getLastKnownLocation(String)} method.
*
- * <p> The frequency of notification or new locations may be controlled using the
- * minTime and minDistance parameters. If minTime is greater than 0,
- * the LocationManager could potentially rest for minTime milliseconds
- * between location updates to conserve power. If minDistance is greater than 0,
- * a location will only be broadcast if the device moves by minDistance meters.
- * To obtain notifications as frequently as possible, set both parameters to 0.
+ * <p> The update interval can be controlled using the minTime parameter.
+ * The elapsed time between location updates will never be less than
+ * minTime, although it can be more depending on the Location Provider
+ * implementation and the update interval requested by other applications.
*
- * <p> Background services should be careful about setting a sufficiently high
- * minTime so that the device doesn't consume too much power by keeping the
- * GPS or wireless radios on all the time. In particular, values under 60000ms
- * are not recommended.
+ * <p> Choosing a sensible value for minTime is important to conserve
+ * battery life. Each location update requires power from
+ * GPS, WIFI, Cell and other radios. Select a minTime value as high as
+ * possible while still providing a reasonable user experience.
+ * If your application is not in the foreground and showing
+ * location to the user then your application should avoid using an active
+ * provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}),
+ * but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes)
+ * or greater. If your application is in the foreground and showing
+ * location to the user then it is appropriate to select a faster
+ * update interval.
*
- * <p> In case the provider is disabled by the user, updates will stop,
- * and an intent will be sent with an extra with key KEY_PROVIDER_ENABLED and a boolean value
- * of false. If the provider is re-enabled, an intent will be sent with an
- * extra with key KEY_PROVIDER_ENABLED and a boolean value of true and location updates will
- * start again.
+ * <p> The minDistance parameter can also be used to control the
+ * frequency of location updates. If it is greater than 0 then the
+ * location provider will only send your application an update when
+ * the location has changed by at least minDistance meters, AND
+ * at least minTime milliseconds have passed. However it is more
+ * difficult for location providers to save power using the minDistance
+ * parameter, so minTime should be the primary tool to conserving battery
+ * life.
*
- * <p> If the provider's status changes, an intent will be sent with an extra with key
- * KEY_STATUS_CHANGED and an integer value indicating the new status. Any extras associated
- * with the status update will be sent as well.
+ * <p> If the provider is disabled by the user, updates will stop,
+ * and an intent will be sent with an extra with key
+ * {@link #KEY_PROVIDER_ENABLED} and a boolean value of false.
+ * If the provider is re-enabled, an intent will be sent with an
+ * extra with key {@link #KEY_PROVIDER_ENABLED} and a boolean value of
+ * true and location updates will start again.
*
- * @param minTime the minimum time interval for notifications, in
- * milliseconds. This field is only used as a hint to conserve power, and actual
- * time between location updates may be greater or lesser than this value.
- * @param minDistance the minimum distance interval for notifications,
- * in meters
+ * <p> If the provider's status changes, an intent will be sent with
+ * an extra with key {@link #KEY_STATUS_CHANGED} and an integer value
+ * indicating the new status. Any extras associated with the status
+ * update will be sent as well.
+ *
+ * <p class="note"> Prior to Jellybean, the minTime parameter was
+ * only a hint, and some location provider implementations ignored it.
+ * From Jellybean and onwards it is mandatory for Android compatible
+ * devices to observe both the minTime and minDistance parameters.
+ *
+ * @param minTime minimum time interval between location updates, in milliseconds
+ * @param minDistance minimum distance between location updates, in meters
* @param criteria contains parameters for the location manager to choose the
* appropriate provider and parameters to compute the location
* @param intent a {#link PendingIntent} to be sent for each location update
*
- * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if criteria is null
* @throws IllegalArgumentException if intent is null
* @throws SecurityException if no suitable permission is present for the provider.
*/
- public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria, PendingIntent intent) {
+ public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria,
+ PendingIntent intent) {
if (criteria == null) {
throw new IllegalArgumentException("criteria==null");
}
@@ -741,12 +863,12 @@
* {@link LocationListener#onLocationChanged} method will be called when
* the location update is available
* @param looper a Looper object whose message queue will be used to
- * implement the callback mechanism.
- * If looper is null then the callbacks will be called on the main thread.
+ * implement the callback mechanism, or null to make callbacks on the
+ * main thread
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if listener is null
- * @throws SecurityException if no suitable permission is present for the provider.
+ * @throws SecurityException if no suitable permission is present for the provider
*/
public void requestSingleUpdate(String provider, LocationListener listener, Looper looper) {
if (provider == null) {
@@ -779,13 +901,13 @@
* {@link LocationListener#onLocationChanged} method will be called when
* the location update is available
* @param looper a Looper object whose message queue will be used to
- * implement the callback mechanism.
- * If looper is null then the callbacks will be called on the current thread.
+ * implement the callback mechanism, or null to make callbacks on the
+ * main thread
*
* @throws IllegalArgumentException if criteria is null
* @throws IllegalArgumentException if listener is null
* @throws SecurityException if no suitable permission is present to access
- * the location services.
+ * the location services
*/
public void requestSingleUpdate(Criteria criteria, LocationListener listener, Looper looper) {
if (criteria == null) {
@@ -804,7 +926,8 @@
* an immediate location is required, applications may use the
* {@link #getLastKnownLocation(String)} method.
*
- * <p> Location updates are sent with a key of KEY_LOCATION_CHANGED and a Location value.
+ * <p> Location updates are sent with a key of
+ * {@link #KEY_LOCATION_CHANGED} and a {@link android.location.Location} value.
*
* <p> In case the provider is disabled by the user, the update will not be received,
* and the {@link LocationListener#onProviderDisabled(String)}
@@ -817,7 +940,7 @@
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if intent is null
- * @throws SecurityException if no suitable permission is present for the provider.
+ * @throws SecurityException if no suitable permission is present for the provider
*/
public void requestSingleUpdate(String provider, PendingIntent intent) {
if (provider == null) {
@@ -836,13 +959,15 @@
* an immediate location is required, applications may use the
* {@link #getLastKnownLocation(String)} method.
*
- * <p> Location updates are sent with a key of KEY_LOCATION_CHANGED and a Location value.
+ * <p> Location updates are sent with a key of
+ * {@link #KEY_LOCATION_CHANGED} and a {@link android.location.Location} value.
*
- * <p> In case the provider is disabled by the user, the update will not be received,
- * and the {@link LocationListener#onProviderDisabled(String)}
- * method will be called. As soon as the provider is enabled again,
- * the {@link LocationListener#onProviderEnabled(String)} method will
- * be called and location updates will start again.
+ * <p> If the provider is disabled by the user, an update will not be
+ * received, and an intent will be sent with an extra with key
+ * {@link #KEY_PROVIDER_ENABLED} and a boolean value of false.
+ * If the provider is re-enabled, an intent will be sent with an
+ * extra with key {@link #KEY_PROVIDER_ENABLED} and a boolean value of
+ * true and the location update will occur.
*
* @param criteria contains parameters for the location manager to choose the
* appropriate provider and parameters to compute the location
@@ -850,7 +975,7 @@
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if intent is null
- * @throws SecurityException if no suitable permission is present for the provider.
+ * @throws SecurityException if no suitable permission is present for the provider
*/
public void requestSingleUpdate(Criteria criteria, PendingIntent intent) {
if (criteria == null) {
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index d165b5e..aa29444 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -3699,38 +3699,15 @@
}
/**
- * The minimum duration during which a user must press to trigger voice-based interactions
- */
- private final static int MEDIABUTTON_LONG_PRESS_DURATION_MS = 300;
- /**
- * The different states of the state machine to handle the launch of voice-based interactions,
- * stored in mVoiceButtonState.
- */
- private final static int VOICEBUTTON_STATE_IDLE = 0;
- private final static int VOICEBUTTON_STATE_DOWN = 1;
- private final static int VOICEBUTTON_STATE_DOWN_IGNORE_NEW = 2;
- /**
- * The different actions after state transitions on mVoiceButtonState.
+ * The different actions performed in response to a voice button key event.
*/
private final static int VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS = 1;
private final static int VOICEBUTTON_ACTION_START_VOICE_INPUT = 2;
private final static int VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS = 3;
private final Object mVoiceEventLock = new Object();
- private int mVoiceButtonState = VOICEBUTTON_STATE_IDLE;
- private long mVoiceButtonDownTime = 0;
-
- /**
- * Log an error when an unexpected action is encountered in the state machine to filter
- * key events.
- * @param keyAction the unexpected action of the key event being filtered
- * @param stateName the string corresponding to the state in which the error occurred
- */
- private static void logErrorForKeyAction(int keyAction, String stateName) {
- Log.e(TAG, "unexpected action "
- + KeyEvent.actionToString(keyAction)
- + " in " + stateName + " state");
- }
+ private boolean mVoiceButtonDown;
+ private boolean mVoiceButtonHandled;
/**
* Filter key events that may be used for voice-based interactions
@@ -3740,67 +3717,32 @@
* is dispatched.
*/
private void filterVoiceInputKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
+ if (DEBUG_RC) {
+ Log.v(TAG, "voice input key event: " + keyEvent + ", needWakeLock=" + needWakeLock);
+ }
+
int voiceButtonAction = VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS;
int keyAction = keyEvent.getAction();
synchronized (mVoiceEventLock) {
- // state machine on mVoiceButtonState
- switch (mVoiceButtonState) {
-
- case VOICEBUTTON_STATE_IDLE:
- if (keyAction == KeyEvent.ACTION_DOWN) {
- mVoiceButtonDownTime = keyEvent.getDownTime();
- // valid state transition
- mVoiceButtonState = VOICEBUTTON_STATE_DOWN;
- } else if (keyAction == KeyEvent.ACTION_UP) {
- // no state transition
- // action is still VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS
- } else {
- logErrorForKeyAction(keyAction, "VOICEBUTTON_STATE_IDLE");
+ if (keyAction == KeyEvent.ACTION_DOWN) {
+ if (keyEvent.getRepeatCount() == 0) {
+ // initial down
+ mVoiceButtonDown = true;
+ mVoiceButtonHandled = false;
+ } else if (mVoiceButtonDown && !mVoiceButtonHandled
+ && (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
+ // long-press, start voice-based interactions
+ mVoiceButtonHandled = true;
+ voiceButtonAction = VOICEBUTTON_ACTION_START_VOICE_INPUT;
+ }
+ } else if (keyAction == KeyEvent.ACTION_UP) {
+ if (mVoiceButtonDown) {
+ // voice button up
+ mVoiceButtonDown = false;
+ if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
+ voiceButtonAction = VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS;
}
- break;
-
- case VOICEBUTTON_STATE_DOWN:
- if ((keyEvent.getEventTime() - mVoiceButtonDownTime)
- >= MEDIABUTTON_LONG_PRESS_DURATION_MS) {
- // press was long enough, start voice-based interactions, regardless of
- // whether this was a DOWN or UP key event
- voiceButtonAction = VOICEBUTTON_ACTION_START_VOICE_INPUT;
- if (keyAction == KeyEvent.ACTION_UP) {
- // done tracking the key press, so transition back to idle state
- mVoiceButtonState = VOICEBUTTON_STATE_IDLE;
- } else if (keyAction == KeyEvent.ACTION_DOWN) {
- // no need to observe the upcoming key events
- mVoiceButtonState = VOICEBUTTON_STATE_DOWN_IGNORE_NEW;
- } else {
- logErrorForKeyAction(keyAction, "VOICEBUTTON_STATE_DOWN");
- }
- } else {
- if (keyAction == KeyEvent.ACTION_UP) {
- // press wasn't long enough, simulate complete key press
- voiceButtonAction = VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS;
- // not tracking the key press anymore, so transition back to idle state
- mVoiceButtonState = VOICEBUTTON_STATE_IDLE;
- } else if (keyAction == KeyEvent.ACTION_DOWN) {
- // no state transition
- // action is still VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS
- } else {
- logErrorForKeyAction(keyAction, "VOICEBUTTON_STATE_DOWN");
- }
- }
- break;
-
- case VOICEBUTTON_STATE_DOWN_IGNORE_NEW:
- if (keyAction == KeyEvent.ACTION_UP) {
- // done tracking the key press, so transition back to idle state
- mVoiceButtonState = VOICEBUTTON_STATE_IDLE;
- // action is still VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS
- } else if (keyAction == KeyEvent.ACTION_DOWN) {
- // no state transition: we've already launched voice-based interactions
- // action is still VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS
- } else {
- logErrorForKeyAction(keyAction, "VOICEBUTTON_STATE_DOWN_IGNORE_NEW");
- }
- break;
+ }
}
}//synchronized (mVoiceEventLock)
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index aa4cdbe..9ed9de0 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -737,7 +737,7 @@
* @see MediaPlayer#VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
*/
public void setVideoScalingMode(int mode) {
- if (isVideoScalingModeSupported(mode)) {
+ if (!isVideoScalingModeSupported(mode)) {
final String msg = "Scaling mode " + mode + " is not supported";
throw new IllegalArgumentException(msg);
}
diff --git a/packages/InputDevices/AndroidManifest.xml b/packages/InputDevices/AndroidManifest.xml
index 6831a74..f0e4abc 100644
--- a/packages/InputDevices/AndroidManifest.xml
+++ b/packages/InputDevices/AndroidManifest.xml
@@ -8,7 +8,8 @@
android:label="@string/app_label"
android:process="system">
- <receiver android:name=".InputDeviceReceiver">
+ <receiver android:name=".InputDeviceReceiver"
+ android:label="@string/keyboard_layouts_label">
<intent-filter>
<action android:name="android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS" />
</intent-filter>
diff --git a/packages/InputDevices/res/values/strings.xml b/packages/InputDevices/res/values/strings.xml
index 140c7d4..c13e606 100644
--- a/packages/InputDevices/res/values/strings.xml
+++ b/packages/InputDevices/res/values/strings.xml
@@ -3,6 +3,9 @@
<!-- Name of the application. [CHAR LIMIT=35] -->
<string name="app_label">Input Devices</string>
+ <!-- Keyboard layouts label, used to describe the set of all built-in layouts in the UI. [CHAR LIMIT=35] -->
+ <string name="keyboard_layouts_label">Android keyboard</string>
+
<!-- US English keyboard layout label. [CHAR LIMIT=35] -->
<string name="keyboard_layout_english_us_label">English (US)</string>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 0038d13..c9957f5 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -30,6 +30,7 @@
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
<uses-permission android:name="android.permission.MASTER_CLEAR" />
+ <uses-permission android:name="android.permission.VIBRATE" />
<!-- ActivityManager -->
<uses-permission android:name="android.permission.GET_TASKS" />
diff --git a/packages/SystemUI/res/layout-land/status_bar_search_panel.xml b/packages/SystemUI/res/layout-land/status_bar_search_panel.xml
index 392a8b5..ae81167 100644
--- a/packages/SystemUI/res/layout-land/status_bar_search_panel.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_search_panel.xml
@@ -55,7 +55,7 @@
prvandroid:snapMargin="@dimen/navbar_search_snap_margin"
prvandroid:hitRadius="@dimen/navbar_search_hit_radius"
prvandroid:feedbackCount="0"
- prvandroid:vibrationDuration="0"
+ prvandroid:vibrationDuration="@integer/config_vibration_duration"
prvandroid:alwaysTrackFinger="true"/>
</RelativeLayout>
diff --git a/packages/SystemUI/res/layout-port/status_bar_search_panel.xml b/packages/SystemUI/res/layout-port/status_bar_search_panel.xml
index 371c575..785d5dd 100644
--- a/packages/SystemUI/res/layout-port/status_bar_search_panel.xml
+++ b/packages/SystemUI/res/layout-port/status_bar_search_panel.xml
@@ -55,7 +55,7 @@
prvandroid:snapMargin="@dimen/navbar_search_snap_margin"
prvandroid:hitRadius="@dimen/navbar_search_hit_radius"
prvandroid:feedbackCount="0"
- prvandroid:vibrationDuration="0"
+ prvandroid:vibrationDuration="@integer/config_vibration_duration"
prvandroid:alwaysTrackFinger="true"/>
</RelativeLayout>
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_search_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_search_panel.xml
index 0ccfe95..4da05d9 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_search_panel.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_search_panel.xml
@@ -64,7 +64,7 @@
prvandroid:snapMargin="@dimen/navbar_search_snap_margin"
prvandroid:hitRadius="@dimen/navbar_search_hit_radius"
prvandroid:feedbackCount="0"
- prvandroid:vibrationDuration="0"
+ prvandroid:vibrationDuration="@integer/config_vibration_duration"
prvandroid:alwaysTrackFinger="true"/>
</RelativeLayout>
diff --git a/packages/SystemUI/res/layout-sw720dp/status_bar_search_panel.xml b/packages/SystemUI/res/layout-sw720dp/status_bar_search_panel.xml
index 0a5390a..1ae8a694 100644
--- a/packages/SystemUI/res/layout-sw720dp/status_bar_search_panel.xml
+++ b/packages/SystemUI/res/layout-sw720dp/status_bar_search_panel.xml
@@ -64,7 +64,7 @@
prvandroid:snapMargin="@dimen/navbar_search_snap_margin"
prvandroid:hitRadius="@dimen/navbar_search_hit_radius"
prvandroid:feedbackCount="0"
- prvandroid:vibrationDuration="0"
+ prvandroid:vibrationDuration="@integer/config_vibration_duration"
prvandroid:alwaysTrackFinger="true"/>
</RelativeLayout>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index be59075..1bf59b0 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -28,10 +28,10 @@
<integer name="notification_panel_layout_gravity">0x31</integer>
<!-- Diameter of outer shape drawable shown in navbar search-->
- <dimen name="navbar_search_outerring_diameter">364dp</dimen>
+ <dimen name="navbar_search_outerring_diameter">430dip</dimen>
<!-- Height of search panel including navigation bar height -->
- <dimen name="navbar_search_panel_height">300dip</dimen>
+ <dimen name="navbar_search_panel_height">280dip</dimen>
<!-- Size of application thumbnail -->
<dimen name="status_bar_recents_thumbnail_width">200dp</dimen>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 5548445..2786013 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -3,16 +3,16 @@
/*
** Copyright 2009, 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
+** 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
+** 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
+** 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.
*/
-->
@@ -56,5 +56,8 @@
<!-- Show rotation lock button in phone-style notification panel. -->
<bool name="config_showRotationLock">true</bool>
+
+ <!-- Vibration duration for MultiWaveView used in SearchPanelView -->
+ <integer translatable="false" name="config_vibration_duration">20</integer>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index c60c806..96f83c6 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -281,9 +281,16 @@
return;
}
- if (mBackgroundWidth < 0 || mBackgroundHeight < 0) {
- // If we don't yet know the size of the wallpaper bitmap,
- // we need to get it now.
+ // If we don't yet know the size of the wallpaper bitmap,
+ // we need to get it now.
+ boolean updateWallpaper = mBackgroundWidth < 0 || mBackgroundHeight < 0 ;
+
+ // If we somehow got to this point after we have last flushed
+ // the wallpaper, well we really need it to draw again. So
+ // seems like we need to reload it. Ouch.
+ updateWallpaper = updateWallpaper || mBackground == null;
+
+ if (updateWallpaper) {
updateWallpaperLocked();
}
@@ -308,12 +315,6 @@
mLastXTranslation = xPixels;
mLastYTranslation = yPixels;
- if (mBackground == null) {
- // If we somehow got to this point after we have last flushed
- // the wallpaper, well we really need it to draw again. So
- // seems like we need to reload it. Ouch.
- updateWallpaperLocked();
- }
if (mIsHwAccelerated) {
if (!drawWallpaperWithOpenGL(sh, availw, availh, xPixels, yPixels)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
index 7edeaef..a90192e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DelegateViewHelper.java
@@ -33,11 +33,7 @@
private float mTriggerThreshhold;
public DelegateViewHelper(View sourceView) {
- mSourceView = sourceView;
- if (mSourceView != null) {
- mTriggerThreshhold = mSourceView.getContext().getResources()
- .getDimension(R.dimen.navbar_search_up_threshhold);
- }
+ setSourceView(sourceView);
}
public void setDelegateView(View view) {
@@ -92,4 +88,12 @@
}
return false;
}
+
+ public void setSourceView(View view) {
+ mSourceView = view;
+ if (mSourceView != null) {
+ mTriggerThreshhold = mSourceView.getContext().getResources()
+ .getDimension(R.dimen.navbar_search_up_threshhold);
+ }
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java
index a6fc396..25fdf07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.tablet;
+import com.android.systemui.R;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.DelegateViewHelper;
@@ -37,8 +38,7 @@
private DelegateViewHelper mDelegateHelper;
public TabletStatusBarView(Context context) {
- super(context);
- mDelegateHelper = new DelegateViewHelper(this);
+ this(context, null);
}
public TabletStatusBarView(Context context, AttributeSet attrs) {
@@ -55,6 +55,20 @@
}
@Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ // Find the view we wish to grab events from in order to detect search gesture.
+ // Depending on the device, this will be one of the id's listed below.
+ // If we don't find one, we'll use the view provided in the constructor above (this view).
+ View view = null;
+ if ((view = findViewById(R.id.navigationArea)) != null) {
+ mDelegateHelper.setSourceView(view);
+ } else if ((view = findViewById(R.id.nav_buttons)) != null) {
+ mDelegateHelper.setSourceView(view);
+ }
+ }
+
+ @Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
if (TabletStatusBar.DEBUG) {
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
index 7f432bf..504bb63 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewManager.java
@@ -117,7 +117,6 @@
final int stretch = ViewGroup.LayoutParams.MATCH_PARENT;
int flags = WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN
| WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER
- | WindowManager.LayoutParams.FLAG_KEEP_SURFACE_WHILE_ANIMATING
| WindowManager.LayoutParams.FLAG_SLIPPERY
/*| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR*/ ;
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 9629f0a..47703c6 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -1630,7 +1630,12 @@
if (mActionBar != null) {
SparseArray<Parcelable> actionBarStates =
savedInstanceState.getSparseParcelableArray(ACTION_BAR_TAG);
- mActionBar.restoreHierarchyState(actionBarStates);
+ if (actionBarStates != null) {
+ mActionBar.restoreHierarchyState(actionBarStates);
+ } else {
+ Log.w(TAG, "Missing saved instance states for action bar views! " +
+ "State will not be restored.");
+ }
}
}
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index a3768c6..2167c49 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -48,6 +48,7 @@
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -252,6 +253,34 @@
IBackupTransport mLocalTransport, mGoogleTransport;
ActiveRestoreSession mActiveRestoreSession;
+ // Watch the device provisioning operation during setup
+ ContentObserver mProvisionedObserver;
+
+ class ProvisionedObserver extends ContentObserver {
+ public ProvisionedObserver(Handler handler) {
+ super(handler);
+ }
+
+ public void onChange(boolean selfChange) {
+ final boolean wasProvisioned = mProvisioned;
+ final boolean isProvisioned = deviceIsProvisioned();
+ // latch: never unprovision
+ mProvisioned = wasProvisioned || isProvisioned;
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Provisioning change: was=" + wasProvisioned
+ + " is=" + isProvisioned + " now=" + mProvisioned);
+ }
+
+ synchronized (mQueueLock) {
+ if (mProvisioned && !wasProvisioned && mEnabled) {
+ // we're now good to go, so start the backup alarms
+ if (MORE_DEBUG) Slog.d(TAG, "Now provisioned, so starting backups");
+ startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL);
+ }
+ }
+ }
+ }
+
class RestoreGetSetsParams {
public IBackupTransport transport;
public ActiveRestoreSession session;
@@ -695,12 +724,19 @@
mBackupHandler = new BackupHandler(mHandlerThread.getLooper());
// Set up our bookkeeping
- boolean areEnabled = Settings.Secure.getInt(context.getContentResolver(),
+ final ContentResolver resolver = context.getContentResolver();
+ boolean areEnabled = Settings.Secure.getInt(resolver,
Settings.Secure.BACKUP_ENABLED, 0) != 0;
- mProvisioned = Settings.Secure.getInt(context.getContentResolver(),
- Settings.Secure.BACKUP_PROVISIONED, 0) != 0;
- mAutoRestore = Settings.Secure.getInt(context.getContentResolver(),
+ mProvisioned = Settings.Secure.getInt(resolver,
+ Settings.Secure.DEVICE_PROVISIONED, 0) != 0;
+ mAutoRestore = Settings.Secure.getInt(resolver,
Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
+
+ mProvisionedObserver = new ProvisionedObserver(mBackupHandler);
+ resolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.DEVICE_PROVISIONED),
+ false, mProvisionedObserver);
+
// If Encrypted file systems is enabled or disabled, this call will return the
// correct directory.
mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup");
@@ -5172,24 +5208,9 @@
public void setBackupProvisioned(boolean available) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"setBackupProvisioned");
-
- boolean wasProvisioned = mProvisioned;
- synchronized (this) {
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.BACKUP_PROVISIONED, available ? 1 : 0);
- mProvisioned = available;
- }
-
- synchronized (mQueueLock) {
- if (available && !wasProvisioned && mEnabled) {
- // we're now good to go, so start the backup alarms
- startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL);
- } else if (!available) {
- // No longer enabled, so stop running backups
- Slog.w(TAG, "Backup service no longer provisioned");
- mAlarmManager.cancel(mRunBackupIntent);
- }
- }
+ /*
+ * This is now a no-op; provisioning is simply the device's own setup state.
+ */
}
private void startBackupAlarmsLocked(long delayBeforeFirstBackup) {
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 4582d67..1e707b2 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -105,6 +105,12 @@
private static final String INSTALL_LOCATION_PROVIDER =
android.Manifest.permission.INSTALL_LOCATION_PROVIDER;
+ // Location Providers may sometimes deliver location updates
+ // slightly faster that requested - provide grace period so
+ // we don't unnecessarily filter events that are otherwise on
+ // time
+ private static final int MAX_PROVIDER_SCHEDULING_JITTER = 100;
+
// Set of providers that are explicitly enabled
private final Set<String> mEnabledProviders = new HashSet<String>();
@@ -194,8 +200,9 @@
final PendingIntent mPendingIntent;
final Object mKey;
final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
+
int mPendingBroadcasts;
- String requiredPermissions;
+ String mRequiredPermissions;
Receiver(ILocationListener listener) {
mListener = listener;
@@ -286,7 +293,7 @@
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler,
- requiredPermissions);
+ mRequiredPermissions);
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcastsLocked();
@@ -322,7 +329,7 @@
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler,
- requiredPermissions);
+ mRequiredPermissions);
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcastsLocked();
@@ -362,7 +369,7 @@
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler,
- requiredPermissions);
+ mRequiredPermissions);
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcastsLocked();
@@ -374,6 +381,7 @@
return true;
}
+ @Override
public void binderDied() {
if (LOCAL_LOGV) {
Slog.v(TAG, "Location listener died");
@@ -1026,7 +1034,7 @@
+ Integer.toHexString(System.identityHashCode(this))
+ " mProvider: " + mProvider + " mUid: " + mUid + "}";
}
-
+
void dump(PrintWriter pw, String prefix) {
pw.println(prefix + this);
pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver);
@@ -1155,10 +1163,11 @@
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) {
- throw new IllegalArgumentException("provider=" + provider);
+ throw new IllegalArgumentException("requested provider " + provider +
+ " doesn't exisit");
}
- receiver.requiredPermissions = checkPermissionsSafe(provider,
- receiver.requiredPermissions);
+ receiver.mRequiredPermissions = checkPermissionsSafe(provider,
+ receiver.mRequiredPermissions);
// so wakelock calls will succeed
final int callingPid = Binder.getCallingPid();
@@ -1752,9 +1761,9 @@
return true;
}
- // Don't broadcast same location again regardless of condition
- // TODO - we should probably still rebroadcast if user explicitly sets a minTime > 0
- if (loc.getTime() == lastLoc.getTime()) {
+ // Check whether sufficient time has passed
+ long minTime = record.mMinTime;
+ if (loc.getTime() - lastLoc.getTime() < minTime - MAX_PROVIDER_SCHEDULING_JITTER) {
return false;
}
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index d6606f6..13ab586 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -79,6 +79,8 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import java.util.Set;
import javax.crypto.SecretKey;
@@ -178,7 +180,8 @@
final private ArrayList<MountServiceBinderListener> mListeners =
new ArrayList<MountServiceBinderListener>();
private boolean mBooted = false;
- private boolean mReady = false;
+ private CountDownLatch mConnectedSignal = new CountDownLatch(1);
+ private CountDownLatch mAsecsScanned = new CountDownLatch(1);
private boolean mSendUmsConnectedOnBoot = false;
// true if we should fake MEDIA_MOUNTED state for external storage
private boolean mEmulateExternalStorage = false;
@@ -446,15 +449,30 @@
final private HandlerThread mHandlerThread;
final private Handler mHandler;
+ void waitForAsecScan() {
+ waitForLatch(mAsecsScanned);
+ }
+
private void waitForReady() {
- while (mReady == false) {
- for (int retries = 5; retries > 0; retries--) {
- if (mReady) {
+ waitForLatch(mConnectedSignal);
+ }
+
+ private void waitForLatch(CountDownLatch latch) {
+ if (latch == null) {
+ return;
+ }
+
+ for (;;) {
+ try {
+ if (latch.await(5000, TimeUnit.MILLISECONDS)) {
return;
+ } else {
+ Slog.w(TAG, "Thread " + Thread.currentThread().getName()
+ + " still waiting for MountService ready...");
}
- SystemClock.sleep(1000);
+ } catch (InterruptedException e) {
+ Slog.w(TAG, "Interrupt while waiting for MountService to be ready.");
}
- Slog.w(TAG, "Waiting too long for mReady!");
}
}
@@ -627,7 +645,7 @@
* Since we'll be calling back into the NativeDaemonConnector,
* we need to do our work in a new thread.
*/
- new Thread() {
+ new Thread("MountService#onDaemonConnected") {
@Override
public void run() {
/**
@@ -668,14 +686,19 @@
updatePublicVolumeState(mExternalStoragePath, Environment.MEDIA_REMOVED);
}
- // Let package manager load internal ASECs.
- mPms.updateExternalMediaStatus(true, false);
-
/*
* Now that we've done our initialization, release
* the hounds!
*/
- mReady = true;
+ mConnectedSignal.countDown();
+ mConnectedSignal = null;
+
+ // Let package manager load internal ASECs.
+ mPms.scanAvailableAsecs();
+
+ // Notify people waiting for ASECs to be scanned that it's done.
+ mAsecsScanned.countDown();
+ mAsecsScanned = null;
}
}.start();
}
@@ -1159,22 +1182,12 @@
mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());
/*
- * Vold does not run in the simulator, so pretend the connector thread
- * ran and did its thing.
- */
- if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
- mReady = true;
- mUmsEnabling = true;
- return;
- }
-
- /*
* Create the connection to vold with a maximum queue of twice the
* amount of containers we'd ever expect to have. This keeps an
* "asec list" from blocking a thread repeatedly.
*/
mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25);
- mReady = false;
+
Thread thread = new Thread(mConnector, VOLD_TAG);
thread.start();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index bb103580..eaecd4c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -320,6 +320,21 @@
}
if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
+ MountService mountService = null;
+ if (!"0".equals(SystemProperties.get("system_init.startmountservice"))) {
+ try {
+ /*
+ * NotificationManagerService is dependant on MountService,
+ * (for media / usb notifications) so we must start MountService first.
+ */
+ Slog.i(TAG, "Mount Service");
+ mountService = new MountService(context);
+ ServiceManager.addService("mount", mountService);
+ } catch (Throwable e) {
+ reportWtf("starting Mount Service", e);
+ }
+ }
+
try {
Slog.i(TAG, "LockSettingsService");
lockSettings = new LockSettingsService(context);
@@ -441,17 +456,13 @@
reportWtf("starting UpdateLockService", e);
}
- if (!"0".equals(SystemProperties.get("system_init.startmountservice"))) {
- try {
- /*
- * NotificationManagerService is dependant on MountService,
- * (for media / usb notifications) so we must start MountService first.
- */
- Slog.i(TAG, "Mount Service");
- ServiceManager.addService("mount", new MountService(context));
- } catch (Throwable e) {
- reportWtf("starting Mount Service", e);
- }
+ /*
+ * MountService has a few dependencies: Notification Manager and
+ * AppWidget Provider. Make sure MountService is completely started
+ * first before continuing.
+ */
+ if (mountService != null) {
+ mountService.waitForAsecScan();
}
try {
diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java
index 84daead..c936ef9 100644
--- a/services/java/com/android/server/UiModeManagerService.java
+++ b/services/java/com/android/server/UiModeManagerService.java
@@ -93,6 +93,7 @@
private final int mDefaultUiModeType;
private final boolean mCarModeKeepsScreenOn;
private final boolean mDeskModeKeepsScreenOn;
+ private final boolean mTelevision;
private boolean mComputedNightMode;
private int mCurUiMode = 0;
@@ -354,7 +355,9 @@
com.android.internal.R.integer.config_carDockKeepsScreenOn) == 1);
mDeskModeKeepsScreenOn = (context.getResources().getInteger(
com.android.internal.R.integer.config_deskDockKeepsScreenOn) == 1);
-
+ mTelevision = context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEVISION);
+
mNightMode = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.UI_NIGHT_MODE, UiModeManager.MODE_NIGHT_AUTO);
}
@@ -455,7 +458,8 @@
}
final void updateConfigurationLocked(boolean sendIt) {
- int uiMode = mDefaultUiModeType;
+ int uiMode = mTelevision ? Configuration.UI_MODE_TYPE_TELEVISION
+ : mDefaultUiModeType;
if (mCarModeEnabled) {
uiMode = Configuration.UI_MODE_TYPE_CAR;
} else if (isDeskDockState(mDockState)) {
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index b1558c7..1f03d17 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -376,11 +376,7 @@
@Override
public void onReceive(Context context, Intent intent) {
mAirplaneModeOn.set(isAirplaneModeOn());
- /* On airplane mode disable, restore wifi state if necessary */
- if (!mAirplaneModeOn.get() && (testAndClearWifiSavedState() ||
- mPersistWifiState.get() == WIFI_ENABLED_AIRPLANE_OVERRIDE)) {
- persistWifiState(true);
- }
+ handleAirplaneModeToggled(mAirplaneModeOn.get());
updateWifiState();
}
},
@@ -447,7 +443,10 @@
boolean wifiEnabled = shouldWifiBeEnabled() || testAndClearWifiSavedState();
Slog.i(TAG, "WifiService starting up with Wi-Fi " +
(wifiEnabled ? "enabled" : "disabled"));
- setWifiEnabled(wifiEnabled);
+
+ // If we are already disabled (could be due to airplane mode), avoid changing persist
+ // state here
+ if (wifiEnabled) setWifiEnabled(wifiEnabled);
mWifiWatchdogStateMachine = WifiWatchdogStateMachine.
makeWifiWatchdogStateMachine(mContext);
@@ -485,26 +484,43 @@
}
}
- private void persistWifiState(boolean enabled) {
- final ContentResolver cr = mContext.getContentResolver();
- boolean airplane = mAirplaneModeOn.get() && isAirplaneToggleable();
- if (enabled) {
- if (airplane) {
- mPersistWifiState.set(WIFI_ENABLED_AIRPLANE_OVERRIDE);
+ private void handleWifiToggled(boolean wifiEnabled) {
+ boolean airplaneEnabled = mAirplaneModeOn.get() && isAirplaneToggleable();
+ if (wifiEnabled) {
+ if (airplaneEnabled) {
+ persistWifiState(WIFI_ENABLED_AIRPLANE_OVERRIDE);
} else {
- mPersistWifiState.set(WIFI_ENABLED);
+ persistWifiState(WIFI_ENABLED);
}
} else {
- if (airplane) {
- mPersistWifiState.set(WIFI_DISABLED_AIRPLANE_ON);
- } else {
- mPersistWifiState.set(WIFI_DISABLED);
- }
+ // When wifi state is disabled, we do not care
+ // if airplane mode is on or not. The scenario of
+ // wifi being disabled due to airplane mode being turned on
+ // is handled handleAirplaneModeToggled()
+ persistWifiState(WIFI_DISABLED);
}
-
- Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, mPersistWifiState.get());
}
+ private void handleAirplaneModeToggled(boolean airplaneEnabled) {
+ if (airplaneEnabled) {
+ // Wifi disabled due to airplane on
+ if (mWifiEnabled) {
+ persistWifiState(WIFI_DISABLED_AIRPLANE_ON);
+ }
+ } else {
+ /* On airplane mode disable, restore wifi state if necessary */
+ if (testAndClearWifiSavedState() ||
+ mPersistWifiState.get() == WIFI_ENABLED_AIRPLANE_OVERRIDE) {
+ persistWifiState(WIFI_ENABLED);
+ }
+ }
+ }
+
+ private void persistWifiState(int state) {
+ final ContentResolver cr = mContext.getContentResolver();
+ mPersistWifiState.set(state);
+ Settings.Secure.putInt(cr, Settings.Secure.WIFI_ON, state);
+ }
/**
* see {@link android.net.wifi.WifiManager#pingSupplicant()}
@@ -578,12 +594,9 @@
* only CHANGE_WIFI_STATE is enforced
*/
- /* Avoids overriding of airplane state when wifi is already in the expected state */
- if (enable != mWifiEnabled) {
- long ident = Binder.clearCallingIdentity();
- persistWifiState(enable);
- Binder.restoreCallingIdentity(ident);
- }
+ long ident = Binder.clearCallingIdentity();
+ handleWifiToggled(enable);
+ Binder.restoreCallingIdentity(ident);
if (enable) {
if (!mIsReceiverRegistered) {
diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java
index 9e94b52..4f1f76f 100644
--- a/services/java/com/android/server/input/InputManagerService.java
+++ b/services/java/com/android/server/input/InputManagerService.java
@@ -37,7 +37,6 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.content.res.TypedArray;
@@ -597,8 +596,8 @@
visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
@Override
public void visitKeyboardLayout(Resources resources,
- String descriptor, String label, int keyboardLayoutResId) {
- list.add(new KeyboardLayout(descriptor, label));
+ String descriptor, String label, String collection, int keyboardLayoutResId) {
+ list.add(new KeyboardLayout(descriptor, label, collection));
}
});
return list.toArray(new KeyboardLayout[list.size()]);
@@ -614,8 +613,8 @@
visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() {
@Override
public void visitKeyboardLayout(Resources resources,
- String descriptor, String label, int keyboardLayoutResId) {
- result[0] = new KeyboardLayout(descriptor, label);
+ String descriptor, String label, String collection, int keyboardLayoutResId) {
+ result[0] = new KeyboardLayout(descriptor, label, collection);
}
});
if (result[0] == null) {
@@ -663,6 +662,9 @@
return;
}
+ CharSequence receiverLabel = receiver.loadLabel(pm);
+ String collection = receiverLabel != null ? receiverLabel.toString() : "";
+
try {
Resources resources = pm.getResourcesForApplication(receiver.applicationInfo);
XmlResourceParser parser = resources.getXml(configResId);
@@ -696,7 +698,7 @@
receiver.packageName, receiver.name, name);
if (keyboardName == null || name.equals(keyboardName)) {
visitor.visitKeyboardLayout(resources, descriptor,
- label, keyboardLayoutResId);
+ label, collection, keyboardLayoutResId);
}
}
} finally {
@@ -1139,7 +1141,7 @@
visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() {
@Override
public void visitKeyboardLayout(Resources resources,
- String descriptor, String label, int keyboardLayoutResId) {
+ String descriptor, String label, String collection, int keyboardLayoutResId) {
try {
result[0] = descriptor;
result[1] = Streams.readFully(new InputStreamReader(
@@ -1262,7 +1264,7 @@
private interface KeyboardLayoutVisitor {
void visitKeyboardLayout(Resources resources,
- String descriptor, String label, int keyboardLayoutResId);
+ String descriptor, String label, String collection, int keyboardLayoutResId);
}
private final class InputDevicesChangedListenerRecord implements DeathRecipient {
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 3936c18..77c3e78 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -240,6 +240,9 @@
// This is where all application persistent data goes for secondary users.
final File mUserAppDataDir;
+ /** The location for ASEC container files on internal storage. */
+ final String mAsecInternalPath;
+
// This is the object monitoring the framework dir.
final FileObserver mFrameworkInstallObserver;
@@ -907,6 +910,7 @@
File dataDir = Environment.getDataDirectory();
mAppDataDir = new File(dataDir, "data");
+ mAsecInternalPath = new File(dataDir, "app-asec").getPath();
mUserAppDataDir = new File(dataDir, "user");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
@@ -1043,7 +1047,7 @@
scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanMode | SCAN_NO_DEX, 0);
-
+
// Collect all system packages.
mSystemAppDir = new File(Environment.getRootDirectory(), "app");
mSystemInstallObserver = new AppDirObserver(
@@ -6479,6 +6483,11 @@
}
}
+ private boolean isAsecExternal(String cid) {
+ final String asecPath = PackageHelper.getSdFilesystem(cid);
+ return !asecPath.startsWith(mAsecInternalPath);
+ }
+
/**
* Extract the MountService "container ID" from the full code path of an
* .apk.
@@ -6517,7 +6526,7 @@
}
AsecInstallArgs(String cid) {
- super(null, null, 0, null, null);
+ super(null, null, isAsecExternal(cid) ? PackageManager.INSTALL_EXTERNAL : 0, null, null);
this.cid = cid;
setCachePath(PackageHelper.getSdDir(cid));
}
@@ -8659,6 +8668,14 @@
});
}
+ /**
+ * Called by MountService when the initial ASECs to scan are available.
+ * Should block until all the ASEC containers are finished being scanned.
+ */
+ public void scanAvailableAsecs() {
+ updateExternalMediaStatusInner(true, false);
+ }
+
/*
* Collect information of applications on external media, map them against
* existing containers and update information based on current mount status.
@@ -8793,7 +8810,11 @@
continue;
}
// Parse package
- int parseFlags = PackageParser.PARSE_ON_SDCARD | mDefParseFlags;
+ int parseFlags = mDefParseFlags;
+ if (args.isExternal()) {
+ parseFlags |= PackageParser.PARSE_ON_SDCARD;
+ }
+
doGc = true;
synchronized (mInstallLock) {
final PackageParser.Package pkg = scanPackageLI(new File(codePath), parseFlags,
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index b3ac6f1..076ba9a 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -534,6 +534,8 @@
Display mDisplay;
+ boolean mIsTouchDevice;
+
final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
final DisplayMetrics mRealDisplayMetrics = new DisplayMetrics();
final DisplayMetrics mTmpDisplayMetrics = new DisplayMetrics();
@@ -1173,8 +1175,7 @@
if (DEBUG_INPUT_METHOD) {
Slog.i(TAG, "isVisibleOrAdding " + w + ": " + w.isVisibleOrAdding());
if (!w.isVisibleOrAdding()) {
- Slog.i(TAG, " mSurface=" + w.mWinAnimator.mSurface + " reportDestroy="
- + w.mWinAnimator.mReportDestroySurface
+ Slog.i(TAG, " mSurface=" + w.mWinAnimator.mSurface
+ " relayoutCalled=" + w.mRelayoutCalled + " viewVis=" + w.mViewVisibility
+ " policyVis=" + w.mPolicyVisibility + " attachHid=" + w.mAttachedHidden
+ " exiting=" + w.mExiting + " destroying=" + w.mDestroying);
@@ -2651,7 +2652,7 @@
int requestedHeight, int viewVisibility, int flags,
Rect outFrame, Rect outContentInsets,
Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
- boolean displayed = false;
+ boolean toBeDisplayed = false;
boolean inTouchMode;
boolean configChanged;
boolean surfaceChanged = false;
@@ -2754,7 +2755,7 @@
}
if (viewVisibility == View.VISIBLE &&
(win.mAppToken == null || !win.mAppToken.clientHidden)) {
- displayed = !win.isVisibleLw();
+ toBeDisplayed = !win.isVisibleLw();
if (win.mExiting) {
winAnimator.cancelExitAnimationForNextAnimationLocked();
win.mExiting = false;
@@ -2766,7 +2767,7 @@
if (oldVisibility == View.GONE) {
winAnimator.mEnterAnimationPending = true;
}
- if (displayed) {
+ if (toBeDisplayed) {
if (win.isDrawnLw() && okToDisplay()) {
winAnimator.applyEnterAnimationLocked();
}
@@ -2792,7 +2793,7 @@
if ((attrChanges&WindowManager.LayoutParams.FORMAT_CHANGED) != 0) {
// To change the format, we need to re-build the surface.
winAnimator.destroySurfaceLocked();
- displayed = true;
+ toBeDisplayed = true;
surfaceChanged = true;
}
try {
@@ -2802,8 +2803,6 @@
Surface surface = winAnimator.createSurfaceLocked();
if (surface != null) {
outSurface.copyFrom(surface);
- winAnimator.mReportDestroySurface = false;
- winAnimator.mSurfacePendingDestroy = false;
if (SHOW_TRANSACTIONS) Slog.i(TAG,
" OUT SURFACE " + outSurface + ": copied");
} else {
@@ -2820,7 +2819,7 @@
Binder.restoreCallingIdentity(origId);
return 0;
}
- if (displayed) {
+ if (toBeDisplayed) {
focusMayChange = true;
}
if (win.mAttrs.type == TYPE_INPUT_METHOD
@@ -2845,11 +2844,10 @@
winAnimator.mEnterAnimationPending = false;
if (winAnimator.mSurface != null) {
if (DEBUG_VISIBILITY) Slog.i(TAG, "Relayout invis " + win
- + ": mExiting=" + win.mExiting
- + " mSurfacePendingDestroy=" + winAnimator.mSurfacePendingDestroy);
+ + ": mExiting=" + win.mExiting);
// If we are not currently running the exit animation, we
// need to see about starting one.
- if (!win.mExiting || winAnimator.mSurfacePendingDestroy) {
+ if (!win.mExiting) {
surfaceChanged = true;
// Try starting an animation; if there isn't one, we
// can destroy the surface right away.
@@ -2857,7 +2855,7 @@
if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
}
- if (!winAnimator.mSurfacePendingDestroy && win.isWinVisibleLw() &&
+ if (win.isWinVisibleLw() &&
winAnimator.applyAnimationLocked(transit, false)) {
focusMayChange = true;
win.mExiting = true;
@@ -2880,22 +2878,8 @@
}
}
- if (winAnimator.mSurface == null || (win.getAttrs().flags
- & WindowManager.LayoutParams.FLAG_KEEP_SURFACE_WHILE_ANIMATING) == 0
- || winAnimator.mSurfacePendingDestroy) {
- // We could be called from a local process, which
- // means outSurface holds its current surface. Ensure the
- // surface object is cleared, but we don't necessarily want
- // it actually destroyed at this point.
- winAnimator.mSurfacePendingDestroy = false;
- outSurface.release();
- if (DEBUG_VISIBILITY) Slog.i(TAG, "Releasing surface in: " + win);
- } else if (winAnimator.mSurface != null) {
- if (DEBUG_VISIBILITY) Slog.i(TAG,
- "Keeping surface, will report destroy: " + win);
- winAnimator.mReportDestroySurface = true;
- outSurface.copyFrom(winAnimator.mSurface);
- }
+ outSurface.release();
+ if (DEBUG_VISIBILITY) Slog.i(TAG, "Releasing surface in: " + win);
}
if (focusMayChange) {
@@ -2912,7 +2896,7 @@
boolean assignLayers = false;
if (imMayMove) {
- if (moveInputMethodWindowsIfNeededLocked(false) || displayed) {
+ if (moveInputMethodWindowsIfNeededLocked(false) || toBeDisplayed) {
// Little hack here -- we -should- be able to rely on the
// function to return true if the IME has moved and needs
// its layer recomputed. However, if the IME was hidden
@@ -2934,7 +2918,7 @@
}
configChanged = updateOrientationFromAppTokensLocked(false);
performLayoutAndPlaceSurfacesLocked();
- if (displayed && win.mIsWallpaper) {
+ if (toBeDisplayed && win.mIsWallpaper) {
updateWallpaperOffsetLocked(win, mAppDisplayWidth, mAppDisplayHeight, false);
}
if (win.mAppToken != null) {
@@ -2970,7 +2954,7 @@
Binder.restoreCallingIdentity(origId);
return (inTouchMode ? WindowManagerImpl.RELAYOUT_RES_IN_TOUCH_MODE : 0)
- | (displayed ? WindowManagerImpl.RELAYOUT_RES_FIRST_TIME : 0)
+ | (toBeDisplayed ? WindowManagerImpl.RELAYOUT_RES_FIRST_TIME : 0)
| (surfaceChanged ? WindowManagerImpl.RELAYOUT_RES_SURFACE_CHANGED : 0)
| (animating ? WindowManagerImpl.RELAYOUT_RES_ANIMATING : 0);
}
@@ -6391,8 +6375,12 @@
WindowManagerPolicy.PRESENCE_EXTERNAL :
WindowManagerPolicy.PRESENCE_INTERNAL;
- if ((sources & InputDevice.SOURCE_TOUCHSCREEN) != 0) {
- config.touchscreen = Configuration.TOUCHSCREEN_FINGER;
+ if (mIsTouchDevice) {
+ if ((sources & InputDevice.SOURCE_TOUCHSCREEN) != 0) {
+ config.touchscreen = Configuration.TOUCHSCREEN_FINGER;
+ }
+ } else {
+ config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
}
if ((sources & InputDevice.SOURCE_TRACKBALL) != 0) {
@@ -6626,6 +6614,8 @@
}
WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
mDisplay = wm.getDefaultDisplay();
+ mIsTouchDevice = mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TOUCHSCREEN);
synchronized(mDisplaySizeLock) {
mInitialDisplayWidth = mDisplay.getRawWidth();
mInitialDisplayHeight = mDisplay.getRawHeight();
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index 1fd80c2..e2a904f 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -679,8 +679,7 @@
*/
boolean isVisibleOrAdding() {
final AppWindowToken atoken = mAppToken;
- return ((mHasSurface && !mWinAnimator.mReportDestroySurface)
- || (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
+ return (mHasSurface || (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
&& mPolicyVisibility && !mAttachedHidden
&& (atoken == null || !atoken.hiddenRequested)
&& !mExiting && !mDestroying;
diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java
index 5516dea..355db6e 100644
--- a/services/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/java/com/android/server/wm/WindowStateAnimator.java
@@ -71,8 +71,6 @@
Surface mSurface;
Surface mPendingDestroySurface;
- boolean mReportDestroySurface;
- boolean mSurfacePendingDestroy;
/**
* Set when we have changed the size of the surface, to know that
@@ -561,8 +559,6 @@
Surface createSurfaceLocked() {
if (mSurface == null) {
- mReportDestroySurface = false;
- mSurfacePendingDestroy = false;
if (DEBUG_ANIM || DEBUG_ORIENTATION) Slog.i(TAG,
"createSurface " + this + ": mDrawState=DRAW_PENDING");
mDrawState = DRAW_PENDING;
@@ -694,7 +690,6 @@
mWin.mAppToken.startingDisplayed = false;
}
- mDrawState = NO_SURFACE;
if (mSurface != null) {
int i = mWin.mChildWindows.size();
@@ -704,17 +699,6 @@
c.mAttachedHidden = true;
}
- if (mReportDestroySurface) {
- mReportDestroySurface = false;
- mSurfacePendingDestroy = true;
- try {
- mWin.mClient.dispatchGetNewSurface();
- // We'll really destroy on the next time around.
- return;
- } catch (RemoteException e) {
- }
- }
-
try {
if (DEBUG_VISIBILITY) {
RuntimeException e = null;
@@ -760,6 +744,7 @@
mSurfaceShown = false;
mSurface = null;
mWin.mHasSurface =false;
+ mDrawState = NO_SURFACE;
}
}
@@ -1147,7 +1132,7 @@
}
} else {
if (DEBUG_ANIM) {
- Slog.v(TAG, "prepareSurface: No changes in animation for " + mWin);
+ // Slog.v(TAG, "prepareSurface: No changes in animation for " + mWin);
}
displayed = true;
}
diff --git a/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java b/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java
index 07a0a28..f40958d 100644
--- a/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java
+++ b/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java
@@ -19,15 +19,21 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.XmlResourceParser;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.Message;
import android.provider.Settings;
import android.telephony.PhoneNumberUtils;
import android.util.Log;
import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
import java.io.IOException;
+import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -45,6 +51,8 @@
*/
public class SmsUsageMonitor {
private static final String TAG = "SmsUsageMonitor";
+ private static final boolean DBG = true;
+ private static final boolean VDBG = false;
/** Default checking period for SMS sent without user permission. */
private static final int DEFAULT_SMS_CHECK_PERIOD = 1800000; // 30 minutes
@@ -69,6 +77,7 @@
private final int mCheckPeriod;
private final int mMaxAllowed;
+
private final HashMap<String, ArrayList<Long>> mSmsStamp =
new HashMap<String, ArrayList<Long>>();
@@ -87,6 +96,12 @@
/** Cached short code pattern matcher for {@link #mCurrentCountry}. */
private ShortCodePatternMatcher mCurrentPatternMatcher;
+ /** Cached short code regex patterns from secure settings for {@link #mCurrentCountry}. */
+ private String mSettingsShortCodePatterns;
+
+ /** Handler for responding to content observer updates. */
+ private final SettingsObserverHandler mSettingsObserverHandler;
+
/** XML tag for root element. */
private static final String TAG_SHORTCODES = "shortcodes";
@@ -149,6 +164,74 @@
}
/**
+ * Observe the secure setting for updated regex patterns.
+ */
+ private static class SettingsObserver extends ContentObserver {
+ private final int mWhat;
+ private final Handler mHandler;
+
+ SettingsObserver(Handler handler, int what) {
+ super(handler);
+ mHandler = handler;
+ mWhat = what;
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ mHandler.obtainMessage(mWhat).sendToTarget();
+ }
+ }
+
+ /**
+ * Handler to update regex patterns when secure setting for the current country is updated.
+ */
+ private class SettingsObserverHandler extends Handler {
+ /** Current content observer, or null. */
+ SettingsObserver mSettingsObserver;
+
+ /** Current country code to watch for settings updates. */
+ private String mCountryIso;
+
+ /** Request to start observing a secure setting. */
+ static final int OBSERVE_SETTING = 1;
+
+ /** Handler event for updated secure settings. */
+ static final int SECURE_SETTINGS_CHANGED = 2;
+
+ /** Send a message to this handler requesting to observe the setting for a new country. */
+ void observeSettingForCountry(String countryIso) {
+ obtainMessage(OBSERVE_SETTING, countryIso).sendToTarget();
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case OBSERVE_SETTING:
+ if (msg.obj != null && msg.obj instanceof String) {
+ mCountryIso = (String) msg.obj;
+ String settingName = getSettingNameForCountry(mCountryIso);
+ ContentResolver resolver = mContext.getContentResolver();
+
+ if (mSettingsObserver != null) {
+ if (VDBG) log("Unregistering old content observer");
+ resolver.unregisterContentObserver(mSettingsObserver);
+ }
+
+ mSettingsObserver = new SettingsObserver(this, SECURE_SETTINGS_CHANGED);
+ resolver.registerContentObserver(
+ Settings.Secure.getUriFor(settingName), false, mSettingsObserver);
+ if (VDBG) log("Registered content observer for " + settingName);
+ }
+ break;
+
+ case SECURE_SETTINGS_CHANGED:
+ loadPatternsFromSettings(mCountryIso);
+ break;
+ }
+ }
+ }
+
+ /**
* Create SMS usage monitor.
* @param context the context to use to load resources and get TelephonyManager service
*/
@@ -164,6 +247,8 @@
Settings.Secure.SMS_OUTGOING_CHECK_INTERVAL_MS,
DEFAULT_SMS_CHECK_PERIOD);
+ mSettingsObserverHandler = new SettingsObserverHandler();
+
// system MMS app is always allowed to send to short codes
mApprovedShortCodeSenders.add("com.android.mms");
}
@@ -178,27 +263,7 @@
XmlResourceParser parser = mContext.getResources().getXml(id);
try {
- XmlUtils.beginDocument(parser, TAG_SHORTCODES);
-
- while (true) {
- XmlUtils.nextElement(parser);
-
- String element = parser.getName();
- if (element == null) break;
-
- if (element.equals(TAG_SHORTCODE)) {
- String currentCountry = parser.getAttributeValue(null, ATTR_COUNTRY);
- if (country.equals(currentCountry)) {
- String pattern = parser.getAttributeValue(null, ATTR_PATTERN);
- String premium = parser.getAttributeValue(null, ATTR_PREMIUM);
- String free = parser.getAttributeValue(null, ATTR_FREE);
- String standard = parser.getAttributeValue(null, ATTR_STANDARD);
- return new ShortCodePatternMatcher(pattern, premium, free, standard);
- }
- } else {
- Log.e(TAG, "Error: skipping unknown XML tag " + element);
- }
- }
+ return getPatternMatcher(country, parser);
} catch (XmlPullParserException e) {
Log.e(TAG, "XML parser exception reading short code pattern resource", e);
} catch (IOException e) {
@@ -209,6 +274,60 @@
return null; // country not found
}
+ /**
+ * Return a pattern matcher object for the specified country from a secure settings string.
+ * @return a {@link ShortCodePatternMatcher} for the specified country, or null if not found
+ */
+ private static ShortCodePatternMatcher getPatternMatcher(String country, String settingsPattern) {
+ // embed pattern tag into an XML document.
+ String document = "<shortcodes>" + settingsPattern + "</shortcodes>";
+ if (VDBG) log("loading updated patterns from: " + document);
+
+ try {
+ XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
+ XmlPullParser parser = factory.newPullParser();
+ parser.setInput(new StringReader(document));
+ return getPatternMatcher(country, parser);
+ } catch (XmlPullParserException e) {
+ Log.e(TAG, "XML parser exception reading short code pattern from settings", e);
+ } catch (IOException e) {
+ Log.e(TAG, "I/O exception reading short code pattern from settings", e);
+ }
+ return null; // country not found
+ }
+
+ /**
+ * Return a pattern matcher object for the specified country and pattern XML parser.
+ * @param country the country to search for
+ * @return a {@link ShortCodePatternMatcher} for the specified country, or null if not found
+ */
+ private static ShortCodePatternMatcher getPatternMatcher(String country, XmlPullParser parser)
+ throws XmlPullParserException, IOException
+ {
+ XmlUtils.beginDocument(parser, TAG_SHORTCODES);
+
+ while (true) {
+ XmlUtils.nextElement(parser);
+
+ String element = parser.getName();
+ if (element == null) break;
+
+ if (element.equals(TAG_SHORTCODE)) {
+ String currentCountry = parser.getAttributeValue(null, ATTR_COUNTRY);
+ if (country.equals(currentCountry)) {
+ String pattern = parser.getAttributeValue(null, ATTR_PATTERN);
+ String premium = parser.getAttributeValue(null, ATTR_PREMIUM);
+ String free = parser.getAttributeValue(null, ATTR_FREE);
+ String standard = parser.getAttributeValue(null, ATTR_STANDARD);
+ return new ShortCodePatternMatcher(pattern, premium, free, standard);
+ }
+ } else {
+ Log.e(TAG, "Error: skipping unknown XML tag " + element);
+ }
+ }
+ return null; // country not found
+ }
+
/** Clear the SMS application list for disposal. */
void dispose() {
mSmsStamp.clear();
@@ -244,7 +363,9 @@
* @return true if the app is approved; false if we need to confirm short code destinations
*/
public boolean isApprovedShortCodeSender(String appName) {
- return mApprovedShortCodeSenders.contains(appName);
+ synchronized (mApprovedShortCodeSenders) {
+ return mApprovedShortCodeSenders.contains(appName);
+ }
}
/**
@@ -252,8 +373,10 @@
* @param appName the package name of the app to add
*/
public void addApprovedShortCodeSender(String appName) {
- Log.d(TAG, "Adding " + appName + " to list of approved short code senders.");
- mApprovedShortCodeSenders.add(appName);
+ if (DBG) log("Adding " + appName + " to list of approved short code senders.");
+ synchronized (mApprovedShortCodeSenders) {
+ mApprovedShortCodeSenders.add(appName);
+ }
}
/**
@@ -271,32 +394,71 @@
* {@link #CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE}, or {@link #CATEGORY_PREMIUM_SHORT_CODE}.
*/
public int checkDestination(String destAddress, String countryIso) {
- // always allow emergency numbers
- if (PhoneNumberUtils.isEmergencyNumber(destAddress, countryIso)) {
- return CATEGORY_NOT_SHORT_CODE;
- }
+ synchronized (mSettingsObserverHandler) {
+ // always allow emergency numbers
+ if (PhoneNumberUtils.isEmergencyNumber(destAddress, countryIso)) {
+ return CATEGORY_NOT_SHORT_CODE;
+ }
- ShortCodePatternMatcher patternMatcher = null;
+ ShortCodePatternMatcher patternMatcher = null;
- if (countryIso != null) {
- if (countryIso.equals(mCurrentCountry)) {
- patternMatcher = mCurrentPatternMatcher;
+ if (countryIso != null) {
+ // query secure settings and initialize content observer for updated regex patterns
+ if (mCurrentCountry == null || !countryIso.equals(mCurrentCountry)) {
+ loadPatternsFromSettings(countryIso);
+ mSettingsObserverHandler.observeSettingForCountry(countryIso);
+ }
+
+ if (countryIso.equals(mCurrentCountry)) {
+ patternMatcher = mCurrentPatternMatcher;
+ } else {
+ patternMatcher = getPatternMatcher(countryIso);
+ mCurrentCountry = countryIso;
+ mCurrentPatternMatcher = patternMatcher; // may be null if not found
+ }
+ }
+
+ if (patternMatcher != null) {
+ return patternMatcher.getNumberCategory(destAddress);
} else {
- patternMatcher = getPatternMatcher(countryIso);
- mCurrentCountry = countryIso;
- mCurrentPatternMatcher = patternMatcher; // may be null if not found
+ // Generic rule: numbers of 5 digits or less are considered potential short codes
+ Log.e(TAG, "No patterns for \"" + countryIso + "\": using generic short code rule");
+ if (destAddress.length() <= 5) {
+ return CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE;
+ } else {
+ return CATEGORY_NOT_SHORT_CODE;
+ }
}
}
+ }
- if (patternMatcher != null) {
- return patternMatcher.getNumberCategory(destAddress);
- } else {
- // Generic rule: numbers of 5 digits or less are considered potential short codes
- Log.e(TAG, "No patterns for \"" + countryIso + "\": using generic short code rule");
- if (destAddress.length() <= 5) {
- return CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE;
- } else {
- return CATEGORY_NOT_SHORT_CODE;
+ private static String getSettingNameForCountry(String countryIso) {
+ return Settings.Secure.SMS_SHORT_CODES_PREFIX + countryIso;
+ }
+
+ /**
+ * Load regex patterns from secure settings if present.
+ * @param countryIso the country to search for
+ */
+ void loadPatternsFromSettings(String countryIso) {
+ synchronized (mSettingsObserverHandler) {
+ if (VDBG) log("loadPatternsFromSettings(" + countryIso + ") called");
+ String settingsPatterns = Settings.Secure.getString(
+ mContext.getContentResolver(), getSettingNameForCountry(countryIso));
+ if (settingsPatterns != null && !settingsPatterns.equals(
+ mSettingsShortCodePatterns)) {
+ // settings pattern string has changed: update the pattern matcher
+ mSettingsShortCodePatterns = settingsPatterns;
+ ShortCodePatternMatcher matcher = getPatternMatcher(countryIso, settingsPatterns);
+ if (matcher != null) {
+ mCurrentCountry = countryIso;
+ mCurrentPatternMatcher = matcher;
+ }
+ } else if (settingsPatterns == null && mSettingsShortCodePatterns != null) {
+ // pattern string was removed: caller will load default patterns from XML resource
+ mCurrentCountry = null;
+ mCurrentPatternMatcher = null;
+ mSettingsShortCodePatterns = null;
}
}
}
@@ -324,7 +486,7 @@
Long ct = System.currentTimeMillis();
long beginCheckPeriod = ct - mCheckPeriod;
- Log.d(TAG, "SMS send size=" + sent.size() + " time=" + ct);
+ if (VDBG) log("SMS send size=" + sent.size() + " time=" + ct);
while (!sent.isEmpty() && sent.get(0) < beginCheckPeriod) {
sent.remove(0);
@@ -338,4 +500,8 @@
}
return false;
}
+
+ private static void log(String msg) {
+ Log.d(TAG, msg);
+ }
}