Merge "Minor tweaks to GridLayout for KeyGuardScreenUnlock."
diff --git a/api/current.txt b/api/current.txt
index fc244d8..721e33e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -16782,6 +16782,9 @@
method public void addF32(android.renderscript.Float3);
method public void addF32(android.renderscript.Float4);
method public void addF64(double);
+ method public void addF64(android.renderscript.Double2);
+ method public void addF64(android.renderscript.Double3);
+ method public void addF64(android.renderscript.Double4);
method public void addI16(short);
method public void addI16(android.renderscript.Short2);
method public void addI16(android.renderscript.Short3);
@@ -16791,6 +16794,9 @@
method public void addI32(android.renderscript.Int3);
method public void addI32(android.renderscript.Int4);
method public void addI64(long);
+ method public void addI64(android.renderscript.Long2);
+ method public void addI64(android.renderscript.Long3);
+ method public void addI64(android.renderscript.Long4);
method public void addI8(byte);
method public void addI8(android.renderscript.Byte2);
method public void addI8(android.renderscript.Byte3);
@@ -16808,6 +16814,9 @@
method public void addU32(android.renderscript.Long3);
method public void addU32(android.renderscript.Long4);
method public void addU64(long);
+ method public void addU64(android.renderscript.Long2);
+ method public void addU64(android.renderscript.Long3);
+ method public void addU64(android.renderscript.Long4);
method public void addU8(short);
method public void addU8(android.renderscript.Short2);
method public void addU8(android.renderscript.Short3);
@@ -21695,7 +21704,10 @@
method public void dispatchDisplayHint(int);
method public boolean dispatchDragEvent(android.view.DragEvent);
method protected void dispatchDraw(android.graphics.Canvas);
+ method protected boolean dispatchGenericFocusedEvent(android.view.MotionEvent);
method public boolean dispatchGenericMotionEvent(android.view.MotionEvent);
+ method protected boolean dispatchGenericPointerEvent(android.view.MotionEvent);
+ method protected boolean dispatchHoverEvent(android.view.MotionEvent);
method public boolean dispatchKeyEvent(android.view.KeyEvent);
method public boolean dispatchKeyEventPreIme(android.view.KeyEvent);
method public boolean dispatchKeyShortcutEvent(android.view.KeyEvent);
@@ -21883,6 +21895,7 @@
method public void onFinishTemporaryDetach();
method protected void onFocusChanged(boolean, int, android.graphics.Rect);
method public boolean onGenericMotionEvent(android.view.MotionEvent);
+ method public void onHoverChanged(boolean);
method public boolean onHoverEvent(android.view.MotionEvent);
method public void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
method public void onInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo);
@@ -22159,6 +22172,10 @@
method public abstract boolean onGenericMotion(android.view.View, android.view.MotionEvent);
}
+ public static abstract interface View.OnHoverListener {
+ method public abstract boolean onHover(android.view.View, android.view.MotionEvent);
+ }
+
public static abstract interface View.OnKeyListener {
method public abstract boolean onKey(android.view.View, int, android.view.KeyEvent);
}
@@ -22329,6 +22346,7 @@
method protected void measureChildren(int, int);
method public final void offsetDescendantRectToMyCoords(android.view.View, android.graphics.Rect);
method public final void offsetRectIntoDescendantCoords(android.view.View, android.graphics.Rect);
+ method public boolean onInterceptHoverEvent(android.view.MotionEvent);
method public boolean onInterceptTouchEvent(android.view.MotionEvent);
method protected abstract void onLayout(boolean, int, int, int, int);
method protected boolean onRequestFocusInDescendants(int, android.graphics.Rect);
@@ -25272,10 +25290,16 @@
method public void dismiss();
method public android.view.Menu getMenu();
method public android.view.MenuInflater getMenuInflater();
+ method public void inflate(int);
+ method public void setOnDismissListener(android.widget.PopupMenu.OnDismissListener);
method public void setOnMenuItemClickListener(android.widget.PopupMenu.OnMenuItemClickListener);
method public void show();
}
+ public static abstract interface PopupMenu.OnDismissListener {
+ method public abstract void onDismiss(android.widget.PopupMenu);
+ }
+
public static abstract interface PopupMenu.OnMenuItemClickListener {
method public abstract boolean onMenuItemClick(android.view.MenuItem);
}
diff --git a/cmds/bu/src/com/android/commands/bu/Backup.java b/cmds/bu/src/com/android/commands/bu/Backup.java
index e81f799..4c4bf98 100644
--- a/cmds/bu/src/com/android/commands/bu/Backup.java
+++ b/cmds/bu/src/com/android/commands/bu/Backup.java
@@ -22,8 +22,6 @@
import android.os.ServiceManager;
import android.util.Log;
-import java.io.FileDescriptor;
-import java.io.IOException;
import java.util.ArrayList;
public final class Backup {
@@ -51,17 +49,19 @@
return;
}
+ int socketFd = Integer.parseInt(nextArg());
+
String arg = nextArg();
if (arg.equals("backup")) {
- doFullBackup();
+ doFullBackup(socketFd);
} else if (arg.equals("restore")) {
- doFullRestore();
+ doFullRestore(socketFd);
} else {
Log.e(TAG, "Invalid operation '" + arg + "'");
}
}
- private void doFullBackup() {
+ private void doFullBackup(int socketFd) {
ArrayList<String> packages = new ArrayList<String>();
boolean saveApks = false;
boolean saveShared = false;
@@ -100,24 +100,20 @@
}
try {
- ParcelFileDescriptor fd = ParcelFileDescriptor.dup(FileDescriptor.out);
+ ParcelFileDescriptor fd = ParcelFileDescriptor.adoptFd(socketFd);
String[] packArray = new String[packages.size()];
mBackupManager.fullBackup(fd, saveApks, saveShared, doEverything,
packages.toArray(packArray));
- } catch (IOException e) {
- Log.e(TAG, "Can't dup out");
} catch (RemoteException e) {
Log.e(TAG, "Unable to invoke backup manager for backup");
}
}
- private void doFullRestore() {
+ private void doFullRestore(int socketFd) {
// No arguments to restore
try {
- ParcelFileDescriptor fd = ParcelFileDescriptor.dup(FileDescriptor.in);
+ ParcelFileDescriptor fd = ParcelFileDescriptor.adoptFd(socketFd);
mBackupManager.fullRestore(fd);
- } catch (IOException e) {
- Log.e(TAG, "Can't dup System.in");
} catch (RemoteException e) {
Log.e(TAG, "Unable to invoke backup manager for restore");
}
diff --git a/core/java/android/pim/EventRecurrence.java b/core/java/android/pim/EventRecurrence.java
index 56c4f7a..cde7dac 100644
--- a/core/java/android/pim/EventRecurrence.java
+++ b/core/java/android/pim/EventRecurrence.java
@@ -18,36 +18,18 @@
import android.text.TextUtils;
import android.text.format.Time;
+import android.util.Log;
+import android.util.TimeFormatException;
import java.util.Calendar;
+import java.util.HashMap;
-public class EventRecurrence
-{
- /**
- * Thrown when a recurrence string provided can not be parsed according
- * to RFC2445.
- */
- public static class InvalidFormatException extends RuntimeException
- {
- InvalidFormatException(String s) {
- super(s);
- }
- }
+/**
+ * Event recurrence utility functions.
+ */
+public class EventRecurrence {
+ private static String TAG = "EventRecur";
- public EventRecurrence()
- {
- wkst = MO;
- }
-
- /**
- * Parse an iCalendar/RFC2445 recur type according to Section 4.3.10.
- */
- public native void parse(String recur);
-
- public void setStartDate(Time date) {
- startDate = date;
- }
-
public static final int SECONDLY = 1;
public static final int MINUTELY = 2;
public static final int HOURLY = 3;
@@ -64,13 +46,15 @@
public static final int FR = 0x00200000;
public static final int SA = 0x00400000;
- public Time startDate;
- public int freq;
+ public Time startDate; // set by setStartDate(), not parse()
+
+ public int freq; // SECONDLY, MINUTELY, etc.
public String until;
public int count;
public int interval;
- public int wkst;
+ public int wkst; // SU, MO, TU, etc.
+ /* lists with zero entries may be null references */
public int[] bysecond;
public int bysecondCount;
public int[] byminute;
@@ -79,7 +63,7 @@
public int byhourCount;
public int[] byday;
public int[] bydayNum;
- public int bydayCount;
+ public int bydayCount;
public int[] bymonthday;
public int bymonthdayCount;
public int[] byyearday;
@@ -91,6 +75,134 @@
public int[] bysetpos;
public int bysetposCount;
+ /** maps a part string to a parser object */
+ private static HashMap<String,PartParser> sParsePartMap;
+ static {
+ sParsePartMap = new HashMap<String,PartParser>();
+ sParsePartMap.put("FREQ", new ParseFreq());
+ sParsePartMap.put("UNTIL", new ParseUntil());
+ sParsePartMap.put("COUNT", new ParseCount());
+ sParsePartMap.put("INTERVAL", new ParseInterval());
+ sParsePartMap.put("BYSECOND", new ParseBySecond());
+ sParsePartMap.put("BYMINUTE", new ParseByMinute());
+ sParsePartMap.put("BYHOUR", new ParseByHour());
+ sParsePartMap.put("BYDAY", new ParseByDay());
+ sParsePartMap.put("BYMONTHDAY", new ParseByMonthDay());
+ sParsePartMap.put("BYYEARDAY", new ParseByYearDay());
+ sParsePartMap.put("BYWEEKNO", new ParseByWeekNo());
+ sParsePartMap.put("BYMONTH", new ParseByMonth());
+ sParsePartMap.put("BYSETPOS", new ParseBySetPos());
+ sParsePartMap.put("WKST", new ParseWkst());
+ }
+
+ /* values for bit vector that keeps track of what we have already seen */
+ private static final int PARSED_FREQ = 1 << 0;
+ private static final int PARSED_UNTIL = 1 << 1;
+ private static final int PARSED_COUNT = 1 << 2;
+ private static final int PARSED_INTERVAL = 1 << 3;
+ private static final int PARSED_BYSECOND = 1 << 4;
+ private static final int PARSED_BYMINUTE = 1 << 5;
+ private static final int PARSED_BYHOUR = 1 << 6;
+ private static final int PARSED_BYDAY = 1 << 7;
+ private static final int PARSED_BYMONTHDAY = 1 << 8;
+ private static final int PARSED_BYYEARDAY = 1 << 9;
+ private static final int PARSED_BYWEEKNO = 1 << 10;
+ private static final int PARSED_BYMONTH = 1 << 11;
+ private static final int PARSED_BYSETPOS = 1 << 12;
+ private static final int PARSED_WKST = 1 << 13;
+
+ /** maps a FREQ value to an integer constant */
+ private static final HashMap<String,Integer> sParseFreqMap = new HashMap<String,Integer>();
+ static {
+ sParseFreqMap.put("SECONDLY", SECONDLY);
+ sParseFreqMap.put("MINUTELY", MINUTELY);
+ sParseFreqMap.put("HOURLY", HOURLY);
+ sParseFreqMap.put("DAILY", DAILY);
+ sParseFreqMap.put("WEEKLY", WEEKLY);
+ sParseFreqMap.put("MONTHLY", MONTHLY);
+ sParseFreqMap.put("YEARLY", YEARLY);
+ }
+
+ /** maps a two-character weekday string to an integer constant */
+ private static final HashMap<String,Integer> sParseWeekdayMap = new HashMap<String,Integer>();
+ static {
+ sParseWeekdayMap.put("SU", SU);
+ sParseWeekdayMap.put("MO", MO);
+ sParseWeekdayMap.put("TU", TU);
+ sParseWeekdayMap.put("WE", WE);
+ sParseWeekdayMap.put("TH", TH);
+ sParseWeekdayMap.put("FR", FR);
+ sParseWeekdayMap.put("SA", SA);
+ }
+
+ /** If set, allow lower-case recurrence rule strings. Minor performance impact. */
+ private static final boolean ALLOW_LOWER_CASE = false;
+
+ /** If set, validate the value of UNTIL parts. Minor performance impact. */
+ private static final boolean VALIDATE_UNTIL = false;
+
+ /** If set, require that only one of {UNTIL,COUNT} is present. Breaks compat w/ old parser. */
+ private static final boolean ONLY_ONE_UNTIL_COUNT = false;
+
+
+ /**
+ * Thrown when a recurrence string provided can not be parsed according
+ * to RFC2445.
+ */
+ public static class InvalidFormatException extends RuntimeException {
+ InvalidFormatException(String s) {
+ super(s);
+ }
+ }
+
+ /**
+ * Parse an iCalendar/RFC2445 recur type according to Section 4.3.10. The string is
+ * parsed twice, by the old and new parsers, and the results are compared.
+ * <p>
+ * TODO: this will go away, and what is now parse2() will simply become parse().
+ */
+ public void parse(String recur) {
+ InvalidFormatException newExcep = null;
+ try {
+ parse2(recur);
+ } catch (InvalidFormatException ife) {
+ newExcep = ife;
+ }
+
+ boolean oldThrew = false;
+ try {
+ EventRecurrence check = new EventRecurrence();
+ check.parseNative(recur);
+ if (newExcep == null) {
+ // Neither threw, check to see if results match.
+ if (!equals(check)) {
+ throw new InvalidFormatException("Recurrence rule parse does not match [" +
+ recur + "]");
+ }
+ }
+ } catch (InvalidFormatException ife) {
+ oldThrew = true;
+ if (newExcep == null) {
+ // Old threw, but new didn't. Log a warning, but don't throw.
+ Log.d(TAG, "NOTE: old parser rejected [" + recur + "]: " + ife.getMessage());
+ }
+ }
+
+ if (newExcep != null) {
+ if (!oldThrew) {
+ // New threw, but old didn't. Log a warning and throw the exception.
+ Log.d(TAG, "NOTE: new parser rejected [" + recur + "]: " + newExcep.getMessage());
+ }
+ throw newExcep;
+ }
+ }
+
+ native void parseNative(String recur);
+
+ public void setStartDate(Time date) {
+ startDate = date;
+ }
+
/**
* Converts one of the Calendar.SUNDAY constants to the SU, MO, etc.
* constants. btw, I think we should switch to those here too, to
@@ -118,7 +230,7 @@
throw new RuntimeException("bad day of week: " + day);
}
}
-
+
public static int timeDay2Day(int day)
{
switch (day)
@@ -191,16 +303,16 @@
throw new RuntimeException("bad day of week: " + day);
}
}
-
+
/**
* Converts one of the internal day constants (SU, MO, etc.) to the
* two-letter string representing that constant.
- *
- * @throws IllegalArgumentException Thrown if the day argument is not one of
- * the defined day constants.
- *
+ *
* @param day one the internal constants SU, MO, etc.
* @return the two-letter string for the day ("SU", "MO", etc.)
+ *
+ * @throws IllegalArgumentException Thrown if the day argument is not one of
+ * the defined day constants.
*/
private static String day2String(int day) {
switch (day) {
@@ -283,7 +395,7 @@
s.append(";UNTIL=");
s.append(until);
}
-
+
if (this.count != 0) {
s.append(";COUNT=");
s.append(this.count);
@@ -323,36 +435,484 @@
return s.toString();
}
-
+
public boolean repeatsOnEveryWeekDay() {
if (this.freq != WEEKLY) {
- return false;
+ return false;
}
-
+
int count = this.bydayCount;
if (count != 5) {
return false;
}
-
+
for (int i = 0 ; i < count ; i++) {
int day = byday[i];
if (day == SU || day == SA) {
return false;
}
}
-
+
return true;
}
-
+
public boolean repeatsMonthlyOnDayCount() {
if (this.freq != MONTHLY) {
return false;
}
-
+
if (bydayCount != 1 || bymonthdayCount != 0) {
return false;
}
-
+
return true;
}
+
+ /**
+ * Determines whether two integer arrays contain identical elements.
+ * <p>
+ * The native implementation over-allocated the arrays (and may have stuff left over from
+ * a previous run), so we can't just check the arrays -- the separately-maintained count
+ * field also matters. We assume that a null array will have a count of zero, and that the
+ * array can hold as many elements as the associated count indicates.
+ * <p>
+ * TODO: replace this with Arrays.equals() when the old parser goes away.
+ */
+ private static boolean arraysEqual(int[] array1, int count1, int[] array2, int count2) {
+ if (count1 != count2) {
+ return false;
+ }
+
+ for (int i = 0; i < count1; i++) {
+ if (array1[i] != array2[i])
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof EventRecurrence)) {
+ return false;
+ }
+
+ EventRecurrence er = (EventRecurrence) obj;
+ return (startDate == null ?
+ er.startDate == null : Time.compare(startDate, er.startDate) == 0) &&
+ freq == er.freq &&
+ (until == null ? er.until == null : until.equals(er.until)) &&
+ count == er.count &&
+ interval == er.interval &&
+ wkst == er.wkst &&
+ arraysEqual(bysecond, bysecondCount, er.bysecond, er.bysecondCount) &&
+ arraysEqual(byminute, byminuteCount, er.byminute, er.byminuteCount) &&
+ arraysEqual(byhour, byhourCount, er.byhour, er.byhourCount) &&
+ arraysEqual(byday, bydayCount, er.byday, er.bydayCount) &&
+ arraysEqual(bydayNum, bydayCount, er.bydayNum, er.bydayCount) &&
+ arraysEqual(bymonthday, bymonthdayCount, er.bymonthday, er.bymonthdayCount) &&
+ arraysEqual(byyearday, byyeardayCount, er.byyearday, er.byyeardayCount) &&
+ arraysEqual(byweekno, byweeknoCount, er.byweekno, er.byweeknoCount) &&
+ arraysEqual(bymonth, bymonthCount, er.bymonth, er.bymonthCount) &&
+ arraysEqual(bysetpos, bysetposCount, er.bysetpos, er.bysetposCount);
+ }
+
+ @Override public int hashCode() {
+ // We overrode equals, so we must override hashCode(). Nobody seems to need this though.
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Resets parser-modified fields to their initial state. Does not alter startDate.
+ * <p>
+ * The original parser always set all of the "count" fields, "wkst", and "until",
+ * essentially allowing the same object to be used multiple times by calling parse().
+ * It's unclear whether this behavior was intentional. For now, be paranoid and
+ * preserve the existing behavior by resetting the fields.
+ * <p>
+ * We don't need to touch the integer arrays; they will either be ignored or
+ * overwritten. The "startDate" field is not set by the parser, so we ignore it here.
+ */
+ private void resetFields() {
+ until = null;
+ freq = count = interval = bysecondCount = byminuteCount = byhourCount =
+ bydayCount = bymonthdayCount = byyeardayCount = byweeknoCount = bymonthCount =
+ bysetposCount = 0;
+ }
+
+ /**
+ * Parses an rfc2445 recurrence rule string into its component pieces. Attempting to parse
+ * malformed input will result in an EventRecurrence.InvalidFormatException.
+ *
+ * @param recur The recurrence rule to parse (in un-folded form).
+ */
+ void parse2(String recur) {
+ /*
+ * From RFC 2445 section 4.3.10:
+ *
+ * recur = "FREQ"=freq *(
+ * ; either UNTIL or COUNT may appear in a 'recur',
+ * ; but UNTIL and COUNT MUST NOT occur in the same 'recur'
+ *
+ * ( ";" "UNTIL" "=" enddate ) /
+ * ( ";" "COUNT" "=" 1*DIGIT ) /
+ *
+ * ; the rest of these keywords are optional,
+ * ; but MUST NOT occur more than once
+ *
+ * ( ";" "INTERVAL" "=" 1*DIGIT ) /
+ * ( ";" "BYSECOND" "=" byseclist ) /
+ * ( ";" "BYMINUTE" "=" byminlist ) /
+ * ( ";" "BYHOUR" "=" byhrlist ) /
+ * ( ";" "BYDAY" "=" bywdaylist ) /
+ * ( ";" "BYMONTHDAY" "=" bymodaylist ) /
+ * ( ";" "BYYEARDAY" "=" byyrdaylist ) /
+ * ( ";" "BYWEEKNO" "=" bywknolist ) /
+ * ( ";" "BYMONTH" "=" bymolist ) /
+ * ( ";" "BYSETPOS" "=" bysplist ) /
+ * ( ";" "WKST" "=" weekday ) /
+ * ( ";" x-name "=" text )
+ * )
+ *
+ * Examples:
+ * FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU
+ * FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8
+ *
+ * Strategy:
+ * (1) Split the string at ';' boundaries to get an array of rule "parts".
+ * (2) For each part, find substrings for left/right sides of '=' (name/value).
+ * (3) Call a <name>-specific parsing function to parse the <value> into an
+ * output field.
+ *
+ * By keeping track of which names we've seen in a bit vector, we can verify the
+ * constraints indicated above (FREQ appears first, none of them appear more than once --
+ * though x-[name] would require special treatment), and we have either UNTIL or COUNT
+ * but not both.
+ *
+ * In general, RFC 2445 property names (e.g. "FREQ") and enumerations ("TU") must
+ * be handled in a case-insensitive fashion, but case may be significant for other
+ * properties. We don't have any case-sensitive values in RRULE, except possibly
+ * for the custom "X-" properties, but we ignore those anyway. Thus, we can trivially
+ * convert the entire string to upper case and then use simple comparisons.
+ *
+ * Differences from previous version:
+ * - allows lower-case property and enumeration values [optional]
+ * - enforces that FREQ appears first
+ * - enforces that only one of UNTIL and COUNT may be specified
+ * - allows (but ignores) X-* parts
+ * - improved validation on various values (e.g. UNTIL timestamps)
+ * - error messages are more specific
+ */
+
+ /* TODO: replace with "if (freq != 0) throw" if nothing requires this */
+ resetFields();
+
+ int parseFlags = 0;
+ String[] parts;
+ if (ALLOW_LOWER_CASE) {
+ parts = recur.toUpperCase().split(";");
+ } else {
+ parts = recur.split(";");
+ }
+ for (String part : parts) {
+ int equalIndex = part.indexOf('=');
+ if (equalIndex <= 0) {
+ /* no '=' or no LHS */
+ throw new InvalidFormatException("Missing LHS in " + part);
+ }
+
+ String lhs = part.substring(0, equalIndex);
+ String rhs = part.substring(equalIndex + 1);
+ if (rhs.length() == 0) {
+ throw new InvalidFormatException("Missing RHS in " + part);
+ }
+
+ /*
+ * In lieu of a "switch" statement that allows string arguments, we use a
+ * map from strings to parsing functions.
+ */
+ PartParser parser = sParsePartMap.get(lhs);
+ if (parser == null) {
+ if (lhs.startsWith("X-")) {
+ //Log.d(TAG, "Ignoring custom part " + lhs);
+ continue;
+ }
+ throw new InvalidFormatException("Couldn't find parser for " + lhs);
+ } else {
+ int flag = parser.parsePart(rhs, this);
+ if ((parseFlags & flag) != 0) {
+ throw new InvalidFormatException("Part " + lhs + " was specified twice");
+ }
+ if (parseFlags == 0 && flag != PARSED_FREQ) {
+ throw new InvalidFormatException("FREQ must be specified first");
+ }
+ parseFlags |= flag;
+ }
+ }
+
+ // If not specified, week starts on Monday.
+ if ((parseFlags & PARSED_WKST) == 0) {
+ wkst = MO;
+ }
+
+ // FREQ is mandatory.
+ if ((parseFlags & PARSED_FREQ) == 0) {
+ throw new InvalidFormatException("Must specify a FREQ value");
+ }
+
+ // Can't have both UNTIL and COUNT.
+ if ((parseFlags & (PARSED_UNTIL | PARSED_COUNT)) == (PARSED_UNTIL | PARSED_COUNT)) {
+ if (ONLY_ONE_UNTIL_COUNT) {
+ throw new InvalidFormatException("Must not specify both UNTIL and COUNT: " + recur);
+ } else {
+ Log.w(TAG, "Warning: rrule has both UNTIL and COUNT: " + recur);
+ }
+ }
+ }
+
+ /**
+ * Base class for the RRULE part parsers.
+ */
+ abstract static class PartParser {
+ /**
+ * Parses a single part.
+ *
+ * @param value The right-hand-side of the part.
+ * @param er The EventRecurrence into which the result is stored.
+ * @return A bit value indicating which part was parsed.
+ */
+ public abstract int parsePart(String value, EventRecurrence er);
+
+ /**
+ * Parses an integer, with range-checking.
+ *
+ * @param str The string to parse.
+ * @param minVal Minimum allowed value.
+ * @param maxVal Maximum allowed value.
+ * @param allowZero Is 0 allowed?
+ * @return The parsed value.
+ */
+ public static int parseIntRange(String str, int minVal, int maxVal, boolean allowZero) {
+ try {
+ if (str.charAt(0) == '+') {
+ // Integer.parseInt does not allow a leading '+', so skip it manually.
+ str = str.substring(1);
+ }
+ int val = Integer.parseInt(str);
+ if (val < minVal || val > maxVal || (val == 0 && !allowZero)) {
+ throw new InvalidFormatException("Integer value out of range: " + str);
+ }
+ return val;
+ } catch (NumberFormatException nfe) {
+ throw new InvalidFormatException("Invalid integer value: " + str);
+ }
+ }
+
+ /**
+ * Parses a comma-separated list of integers, with range-checking.
+ *
+ * @param listStr The string to parse.
+ * @param minVal Minimum allowed value.
+ * @param maxVal Maximum allowed value.
+ * @param allowZero Is 0 allowed?
+ * @return A new array with values, sized to hold the exact number of elements.
+ */
+ public static int[] parseNumberList(String listStr, int minVal, int maxVal,
+ boolean allowZero) {
+ int[] values;
+
+ if (listStr.indexOf(",") < 0) {
+ // Common case: only one entry, skip split() overhead.
+ values = new int[1];
+ values[0] = parseIntRange(listStr, minVal, maxVal, allowZero);
+ } else {
+ String[] valueStrs = listStr.split(",");
+ int len = valueStrs.length;
+ values = new int[len];
+ for (int i = 0; i < len; i++) {
+ values[i] = parseIntRange(valueStrs[i], minVal, maxVal, allowZero);
+ }
+ }
+ return values;
+ }
+ }
+
+ /** parses FREQ={SECONDLY,MINUTELY,...} */
+ private static class ParseFreq extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ Integer freq = sParseFreqMap.get(value);
+ if (freq == null) {
+ throw new InvalidFormatException("Invalid FREQ value: " + value);
+ }
+ er.freq = freq;
+ return PARSED_FREQ;
+ }
+ }
+ /** parses UNTIL=enddate, e.g. "19970829T021400" */
+ private static class ParseUntil extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ if (VALIDATE_UNTIL) {
+ try {
+ // Parse the time to validate it. The result isn't retained.
+ Time until = new Time();
+ until.parse(value);
+ } catch (TimeFormatException tfe) {
+ throw new InvalidFormatException("Invalid UNTIL value: " + value);
+ }
+ }
+ er.until = value;
+ return PARSED_UNTIL;
+ }
+ }
+ /** parses COUNT=[non-negative-integer] */
+ private static class ParseCount extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ er.count = parseIntRange(value, 0, Integer.MAX_VALUE, true);
+ return PARSED_COUNT;
+ }
+ }
+ /** parses INTERVAL=[non-negative-integer] */
+ private static class ParseInterval extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ er.interval = parseIntRange(value, 1, Integer.MAX_VALUE, false);
+ return PARSED_INTERVAL;
+ }
+ }
+ /** parses BYSECOND=byseclist */
+ private static class ParseBySecond extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ int[] bysecond = parseNumberList(value, 0, 59, true);
+ er.bysecond = bysecond;
+ er.bysecondCount = bysecond.length;
+ return PARSED_BYSECOND;
+ }
+ }
+ /** parses BYMINUTE=byminlist */
+ private static class ParseByMinute extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ int[] byminute = parseNumberList(value, 0, 59, true);
+ er.byminute = byminute;
+ er.byminuteCount = byminute.length;
+ return PARSED_BYMINUTE;
+ }
+ }
+ /** parses BYHOUR=byhrlist */
+ private static class ParseByHour extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ int[] byhour = parseNumberList(value, 0, 23, true);
+ er.byhour = byhour;
+ er.byhourCount = byhour.length;
+ return PARSED_BYHOUR;
+ }
+ }
+ /** parses BYDAY=bywdaylist, e.g. "1SU,-1SU" */
+ private static class ParseByDay extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ int[] byday;
+ int[] bydayNum;
+ int bydayCount;
+
+ if (value.indexOf(",") < 0) {
+ /* only one entry, skip split() overhead */
+ bydayCount = 1;
+ byday = new int[1];
+ bydayNum = new int[1];
+ parseWday(value, byday, bydayNum, 0);
+ } else {
+ String[] wdays = value.split(",");
+ int len = wdays.length;
+ bydayCount = len;
+ byday = new int[len];
+ bydayNum = new int[len];
+ for (int i = 0; i < len; i++) {
+ parseWday(wdays[i], byday, bydayNum, i);
+ }
+ }
+ er.byday = byday;
+ er.bydayNum = bydayNum;
+ er.bydayCount = bydayCount;
+ return PARSED_BYDAY;
+ }
+
+ /** parses [int]weekday, putting the pieces into parallel array entries */
+ private static void parseWday(String str, int[] byday, int[] bydayNum, int index) {
+ int wdayStrStart = str.length() - 2;
+ String wdayStr;
+
+ if (wdayStrStart > 0) {
+ /* number is included; parse it out and advance to weekday */
+ String numPart = str.substring(0, wdayStrStart);
+ int num = parseIntRange(numPart, -53, 53, false);
+ bydayNum[index] = num;
+ wdayStr = str.substring(wdayStrStart);
+ } else {
+ /* just the weekday string */
+ wdayStr = str;
+ }
+ Integer wday = sParseWeekdayMap.get(wdayStr);
+ if (wday == null) {
+ throw new InvalidFormatException("Invalid BYDAY value: " + str);
+ }
+ byday[index] = wday;
+ }
+ }
+ /** parses BYMONTHDAY=bymodaylist */
+ private static class ParseByMonthDay extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ int[] bymonthday = parseNumberList(value, -31, 31, false);
+ er.bymonthday = bymonthday;
+ er.bymonthdayCount = bymonthday.length;
+ return PARSED_BYMONTHDAY;
+ }
+ }
+ /** parses BYYEARDAY=byyrdaylist */
+ private static class ParseByYearDay extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ int[] byyearday = parseNumberList(value, -366, 366, false);
+ er.byyearday = byyearday;
+ er.byyeardayCount = byyearday.length;
+ return PARSED_BYYEARDAY;
+ }
+ }
+ /** parses BYWEEKNO=bywknolist */
+ private static class ParseByWeekNo extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ int[] byweekno = parseNumberList(value, -53, 53, false);
+ er.byweekno = byweekno;
+ er.byweeknoCount = byweekno.length;
+ return PARSED_BYWEEKNO;
+ }
+ }
+ /** parses BYMONTH=bymolist */
+ private static class ParseByMonth extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ int[] bymonth = parseNumberList(value, 1, 12, false);
+ er.bymonth = bymonth;
+ er.bymonthCount = bymonth.length;
+ return PARSED_BYMONTH;
+ }
+ }
+ /** parses BYSETPOS=bysplist */
+ private static class ParseBySetPos extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ int[] bysetpos = parseNumberList(value, Integer.MIN_VALUE, Integer.MAX_VALUE, true);
+ er.bysetpos = bysetpos;
+ er.bysetposCount = bysetpos.length;
+ return PARSED_BYSETPOS;
+ }
+ }
+ /** parses WKST={SU,MO,...} */
+ private static class ParseWkst extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ Integer wkst = sParseWeekdayMap.get(value);
+ if (wkst == null) {
+ throw new InvalidFormatException("Invalid WKST value: " + value);
+ }
+ er.wkst = wkst;
+ return PARSED_WKST;
+ }
+ }
}
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index 14e7bed..c90de17 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -16,9 +16,6 @@
package android.preference;
-import com.android.internal.util.XmlUtils;
-
-import android.app.ActionBar;
import android.app.Fragment;
import android.app.FragmentBreadCrumbs;
import android.app.FragmentManager;
@@ -26,7 +23,6 @@
import android.app.ListActivity;
import android.content.Context;
import android.content.Intent;
-import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
@@ -41,23 +37,26 @@
import android.util.Xml;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.view.View.OnClickListener;
+import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.ArrayAdapter;
+import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
+import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* This is the base class for an activity to show a hierarchy of preferences
* to the user. Prior to {@link android.os.Build.VERSION_CODES#HONEYCOMB}
@@ -66,7 +65,7 @@
* class. If you are using PreferenceActivity in its old mode, the documentation
* there applies to the deprecated APIs here.
*
- * <p>This activity shows one or more headers of preferences, each of with
+ * <p>This activity shows one or more headers of preferences, each of which
* is associated with a {@link PreferenceFragment} to display the preferences
* of that header. The actual layout and display of these associations can
* however vary; currently there are two major approaches it may take:
@@ -117,7 +116,6 @@
public abstract class PreferenceActivity extends ListActivity implements
PreferenceManager.OnPreferenceTreeClickListener,
PreferenceFragment.OnPreferenceStartFragmentCallback {
- private static final String TAG = "PreferenceActivity";
// Constants for state save/restore
private static final String HEADERS_TAG = ":android:headers";
@@ -182,8 +180,6 @@
private final ArrayList<Header> mHeaders = new ArrayList<Header>();
- private HeaderAdapter mAdapter;
-
private FrameLayout mListFooter;
private ViewGroup mPrefsContainer;
@@ -222,8 +218,8 @@
ArrayList<Header> oldHeaders = new ArrayList<Header>(mHeaders);
mHeaders.clear();
onBuildHeaders(mHeaders);
- if (mAdapter != null) {
- mAdapter.notifyDataSetChanged();
+ if (mAdapter instanceof BaseAdapter) {
+ ((BaseAdapter) mAdapter).notifyDataSetChanged();
}
Header header = onGetNewHeader();
if (header != null && header.fragment != null) {
@@ -387,6 +383,7 @@
public Bundle extras;
public Header() {
+ // Empty
}
/**
@@ -521,7 +518,7 @@
if (headers != null) {
mHeaders.addAll(headers);
int curHeader = savedInstanceState.getInt(CUR_HEADER_TAG,
- (int)HEADER_ID_UNDEFINED);
+ (int) HEADER_ID_UNDEFINED);
if (curHeader >= 0 && curHeader < mHeaders.size()) {
setSelectedHeader(mHeaders.get(curHeader));
}
@@ -567,8 +564,7 @@
findViewById(com.android.internal.R.id.headers).setVisibility(View.GONE);
mPrefsContainer.setVisibility(View.VISIBLE);
} else if (mHeaders.size() > 0) {
- mAdapter = new HeaderAdapter(this, mHeaders);
- setListAdapter(mAdapter);
+ setListAdapter(new HeaderAdapter(this, mHeaders));
if (!mSinglePane) {
// Multi-pane.
getListView().setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
@@ -715,6 +711,7 @@
* @param target The list in which to place the headers.
*/
public void onBuildHeaders(List<Header> target) {
+ // Should be overloaded by subclasses
}
/**
@@ -743,6 +740,7 @@
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& type != XmlPullParser.START_TAG) {
+ // Parse next until start tag is found
}
String nodeName = parser.getName();
@@ -951,7 +949,8 @@
super.onListItemClick(l, v, position, id);
if (mAdapter != null) {
- onHeaderClick(mHeaders.get(position), position);
+ Object item = mAdapter.getItem(position);
+ if (item instanceof Header) onHeaderClick((Header) item, position);
}
}
@@ -993,7 +992,7 @@
* @param fragmentName The name of the fragment to display.
* @param args Optional arguments to supply to the fragment.
* @param titleRes Optional resource ID of title to show for this item.
- * @param titleRes Optional resource ID of short title to show for this item.
+ * @param shortTitleRes Optional resource ID of short title to show for this item.
* @return Returns an Intent that can be launched to display the given
* fragment.
*/
@@ -1032,7 +1031,7 @@
* code in which to report the result.
* @param titleRes Resource ID of string to display for the title of
* this set of preferences.
- * @param titleRes Resource ID of string to display for the short title of
+ * @param shortTitleRes Resource ID of string to display for the short title of
* this set of preferences.
*/
public void startWithFragment(String fragmentName, Bundle args,
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 603edf0..19e9a67 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -300,6 +300,21 @@
"android.settings.INPUT_METHOD_SUBTYPE_SETTINGS";
/**
+ * Activity Action: Show a dialog to select input method.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_SHOW_INPUT_METHOD_PICKER =
+ "android.settings.SHOW_INPUT_METHOD_PICKER";
+
+ /**
* Activity Action: Show settings to manage the user input dictionary.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
diff --git a/core/java/android/server/BluetoothBondState.java b/core/java/android/server/BluetoothBondState.java
index a36cd24..39c3c88 100644
--- a/core/java/android/server/BluetoothBondState.java
+++ b/core/java/android/server/BluetoothBondState.java
@@ -79,6 +79,7 @@
mService = service;
mBluetoothInputProfileHandler =
BluetoothInputProfileHandler.getInstance(mContext, mService);
+ getProfileProxy();
}
synchronized void setPendingOutgoingBonding(String address) {
@@ -131,10 +132,6 @@
if (state == BluetoothDevice.BOND_BONDED) {
mService.addProfileState(address);
- } else if (state == BluetoothDevice.BOND_BONDING) {
- if (mA2dpProxy == null || mHeadsetProxy == null) {
- getProfileProxy();
- }
} else if (state == BluetoothDevice.BOND_NONE) {
mService.removeProfileState(address);
}
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 62792f4..a4588ae 100755
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -1247,18 +1247,9 @@
return true;
}
- boolean ret;
- // Just do the SDP if the device is already created and UUIDs are not
- // NULL, else create the device and then do SDP.
- if (mDeviceProperties.isInCache(address) && getRemoteUuids(address) != null) {
- String path = getObjectPathFromAddress(address);
- if (path == null) return false;
-
- // Use an empty string for the UUID pattern
- ret = discoverServicesNative(path, "");
- } else {
- ret = createDeviceNative(address);
- }
+ // If the device is already created, we will
+ // do the SDP on the callback of createDeviceNative.
+ boolean ret= createDeviceNative(address);
mUuidIntentTracker.add(address);
if (uuid != null) {
diff --git a/core/java/android/view/InputEventConsistencyVerifier.java b/core/java/android/view/InputEventConsistencyVerifier.java
index e14b975..49f3bbe 100644
--- a/core/java/android/view/InputEventConsistencyVerifier.java
+++ b/core/java/android/view/InputEventConsistencyVerifier.java
@@ -32,6 +32,11 @@
public final class InputEventConsistencyVerifier {
private static final boolean IS_ENG_BUILD = "eng".equals(Build.TYPE);
+ private static final String EVENT_TYPE_KEY = "KeyEvent";
+ private static final String EVENT_TYPE_TRACKBALL = "TrackballEvent";
+ private static final String EVENT_TYPE_TOUCH = "TouchEvent";
+ private static final String EVENT_TYPE_GENERIC_MOTION = "GenericMotionEvent";
+
// The number of recent events to log when a problem is detected.
// Can be set to 0 to disable logging recent events but the runtime overhead of
// this feature is negligible on current hardware.
@@ -54,6 +59,7 @@
// It does not make sense to examine the contents of the last event since it may have
// been recycled.
private InputEvent mLastEvent;
+ private String mLastEventType;
private int mLastNestingLevel;
// Copy of the most recent events.
@@ -185,7 +191,7 @@
* and both dispatching methods call into the consistency verifier.
*/
public void onKeyEvent(KeyEvent event, int nestingLevel) {
- if (!startEvent(event, nestingLevel, "KeyEvent")) {
+ if (!startEvent(event, nestingLevel, EVENT_TYPE_KEY)) {
return;
}
@@ -247,7 +253,7 @@
* and both dispatching methods call into the consistency verifier.
*/
public void onTrackballEvent(MotionEvent event, int nestingLevel) {
- if (!startEvent(event, nestingLevel, "TrackballEvent")) {
+ if (!startEvent(event, nestingLevel, EVENT_TYPE_TRACKBALL)) {
return;
}
@@ -310,23 +316,19 @@
* and both dispatching methods call into the consistency verifier.
*/
public void onTouchEvent(MotionEvent event, int nestingLevel) {
- if (!startEvent(event, nestingLevel, "TouchEvent")) {
+ if (!startEvent(event, nestingLevel, EVENT_TYPE_TOUCH)) {
return;
}
final int action = event.getAction();
final boolean newStream = action == MotionEvent.ACTION_DOWN
|| action == MotionEvent.ACTION_CANCEL;
- if (mTouchEventStreamIsTainted || mTouchEventStreamUnhandled) {
- if (newStream) {
- mTouchEventStreamIsTainted = false;
- mTouchEventStreamUnhandled = false;
- mTouchEventStreamPointers = 0;
- } else {
- finishEvent(mTouchEventStreamIsTainted);
- return;
- }
+ if (newStream && (mTouchEventStreamIsTainted || mTouchEventStreamUnhandled)) {
+ mTouchEventStreamIsTainted = false;
+ mTouchEventStreamUnhandled = false;
+ mTouchEventStreamPointers = 0;
}
+ final boolean wasTainted = mTouchEventStreamIsTainted;
try {
ensureMetaStateIsNormalized(event.getMetaState());
@@ -439,7 +441,7 @@
problem("Source was not SOURCE_CLASS_POINTER.");
}
} finally {
- finishEvent(false);
+ finishEvent(wasTainted);
}
}
@@ -453,7 +455,7 @@
* and both dispatching methods call into the consistency verifier.
*/
public void onGenericMotionEvent(MotionEvent event, int nestingLevel) {
- if (!startEvent(event, nestingLevel, "GenericMotionEvent")) {
+ if (!startEvent(event, nestingLevel, EVENT_TYPE_GENERIC_MOTION)) {
return;
}
@@ -568,21 +570,19 @@
}
private boolean startEvent(InputEvent event, int nestingLevel, String eventType) {
- // Ignore the event if it is already tainted.
- if (event.isTainted()) {
- return false;
- }
-
// Ignore the event if we already checked it at a higher nesting level.
- if (event == mLastEvent && nestingLevel < mLastNestingLevel) {
+ if (event == mLastEvent && nestingLevel < mLastNestingLevel
+ && eventType == mLastEventType) {
return false;
}
if (nestingLevel > 0) {
mLastEvent = event;
+ mLastEventType = eventType;
mLastNestingLevel = nestingLevel;
} else {
mLastEvent = null;
+ mLastEventType = null;
mLastNestingLevel = 0;
}
@@ -593,27 +593,30 @@
private void finishEvent(boolean tainted) {
if (mViolationMessage != null && mViolationMessage.length() != 0) {
- mViolationMessage.append("\n in ").append(mCaller);
- mViolationMessage.append("\n ");
- appendEvent(mViolationMessage, 0, mCurrentEvent, false);
+ if (!tainted) {
+ // Write a log message only if the event was not already tainted.
+ mViolationMessage.append("\n in ").append(mCaller);
+ mViolationMessage.append("\n ");
+ appendEvent(mViolationMessage, 0, mCurrentEvent, false);
- if (RECENT_EVENTS_TO_LOG != 0 && mRecentEvents != null) {
- mViolationMessage.append("\n -- recent events --");
- for (int i = 0; i < RECENT_EVENTS_TO_LOG; i++) {
- final int index = (mMostRecentEventIndex + RECENT_EVENTS_TO_LOG - i)
- % RECENT_EVENTS_TO_LOG;
- final InputEvent event = mRecentEvents[index];
- if (event == null) {
- break;
+ if (RECENT_EVENTS_TO_LOG != 0 && mRecentEvents != null) {
+ mViolationMessage.append("\n -- recent events --");
+ for (int i = 0; i < RECENT_EVENTS_TO_LOG; i++) {
+ final int index = (mMostRecentEventIndex + RECENT_EVENTS_TO_LOG - i)
+ % RECENT_EVENTS_TO_LOG;
+ final InputEvent event = mRecentEvents[index];
+ if (event == null) {
+ break;
+ }
+ mViolationMessage.append("\n ");
+ appendEvent(mViolationMessage, i + 1, event, mRecentEventsUnhandled[index]);
}
- mViolationMessage.append("\n ");
- appendEvent(mViolationMessage, i + 1, event, mRecentEventsUnhandled[index]);
}
- }
- Log.d(mLogTag, mViolationMessage.toString());
+ Log.d(mLogTag, mViolationMessage.toString());
+ tainted = true;
+ }
mViolationMessage.setLength(0);
- tainted = true;
}
if (tainted) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 4403591..f70ca90 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2262,6 +2262,8 @@
private OnTouchListener mOnTouchListener;
+ private OnHoverListener mOnHoverListener;
+
private OnGenericMotionListener mOnGenericMotionListener;
private OnDragListener mOnDragListener;
@@ -2484,6 +2486,12 @@
Rect mLocalDirtyRect;
/**
+ * Set to true when the view is sending hover accessibility events because it
+ * is the innermost hovered view.
+ */
+ private boolean mSendingHoverAccessibilityEvents;
+
+ /**
* Consistency verifier for debugging purposes.
* @hide
*/
@@ -2503,6 +2511,9 @@
mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED | LAYOUT_DIRECTION_INHERIT;
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS);
+ mUserPaddingStart = -1;
+ mUserPaddingEnd = -1;
+ mUserPaddingRelative = false;
}
/**
@@ -2864,13 +2875,16 @@
mUserPaddingRelative = (startPadding >= 0 || endPadding >= 0);
+ // Cache user padding as we cannot fully resolve padding here (we dont have yet the resolved
+ // layout direction). Those cached values will be used later during padding resolution.
+ mUserPaddingStart = startPadding;
+ mUserPaddingEnd = endPadding;
+
if (padding >= 0) {
leftPadding = padding;
topPadding = padding;
rightPadding = padding;
bottomPadding = padding;
- startPadding = padding;
- endPadding = padding;
}
// If the user specified the padding (either with android:padding or
@@ -2882,11 +2896,6 @@
rightPadding >= 0 ? rightPadding : mPaddingRight,
bottomPadding >= 0 ? bottomPadding : mPaddingBottom);
- // Cache user padding as we cannot fully resolve padding here (we dont have yet the resolved
- // layout direction). Those cached values will be used later during padding resolution.
- mUserPaddingStart = startPadding;
- mUserPaddingEnd = endPadding;
-
if (viewFlagMasks != 0) {
setFlags(viewFlagValues, viewFlagMasks);
}
@@ -5117,9 +5126,6 @@
return true;
}
- if (mInputEventConsistencyVerifier != null) {
- mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
- }
return false;
}
@@ -5147,6 +5153,12 @@
|| action == MotionEvent.ACTION_HOVER_MOVE
|| action == MotionEvent.ACTION_HOVER_EXIT) {
if (dispatchHoverEvent(event)) {
+ // For compatibility with existing applications that handled HOVER_MOVE
+ // events in onGenericMotionEvent, dispatch the event there. The
+ // onHoverEvent method did not exist at the time.
+ if (action == MotionEvent.ACTION_HOVER_MOVE) {
+ dispatchGenericMotionEventInternal(event);
+ }
return true;
}
} else if (dispatchGenericPointerEvent(event)) {
@@ -5156,6 +5168,17 @@
return true;
}
+ if (dispatchGenericMotionEventInternal(event)) {
+ return true;
+ }
+
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
+ }
+ return false;
+ }
+
+ private boolean dispatchGenericMotionEventInternal(MotionEvent event) {
//noinspection SimplifiableIfStatement
if (mOnGenericMotionListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& mOnGenericMotionListener.onGenericMotion(this, event)) {
@@ -5181,13 +5204,42 @@
*
* @param event The motion event to be dispatched.
* @return True if the event was handled by the view, false otherwise.
- * @hide
*/
protected boolean dispatchHoverEvent(MotionEvent event) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_HOVER_ENTER:
+ if (!hasHoveredChild() && !mSendingHoverAccessibilityEvents) {
+ mSendingHoverAccessibilityEvents = true;
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
+ }
+ break;
+ case MotionEvent.ACTION_HOVER_EXIT:
+ if (mSendingHoverAccessibilityEvents) {
+ mSendingHoverAccessibilityEvents = false;
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
+ }
+ break;
+ }
+
+ if (mOnHoverListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
+ && mOnHoverListener.onHover(this, event)) {
+ return true;
+ }
+
return onHoverEvent(event);
}
/**
+ * Returns true if the view has a child to which it has recently sent
+ * {@link MotionEvent#ACTION_HOVER_ENTER}. If this view is hovered and
+ * it does not have a hovered child, then it must be the innermost hovered view.
+ * @hide
+ */
+ protected boolean hasHoveredChild() {
+ return false;
+ }
+
+ /**
* Dispatch a generic motion event to the view under the first pointer.
* <p>
* Do not call this method directly.
@@ -5196,7 +5248,6 @@
*
* @param event The motion event to be dispatched.
* @return True if the event was handled by the view, false otherwise.
- * @hide
*/
protected boolean dispatchGenericPointerEvent(MotionEvent event) {
return false;
@@ -5211,7 +5262,6 @@
*
* @param event The motion event to be dispatched.
* @return True if the event was handled by the view, false otherwise.
- * @hide
*/
protected boolean dispatchGenericFocusedEvent(MotionEvent event) {
return false;
@@ -5788,71 +5838,129 @@
/**
* Implement this method to handle hover events.
* <p>
- * Hover events are pointer events with action {@link MotionEvent#ACTION_HOVER_ENTER},
- * {@link MotionEvent#ACTION_HOVER_MOVE}, or {@link MotionEvent#ACTION_HOVER_EXIT}.
+ * This method is called whenever a pointer is hovering into, over, or out of the
+ * bounds of a view and the view is not currently being touched.
+ * Hover events are represented as pointer events with action
+ * {@link MotionEvent#ACTION_HOVER_ENTER}, {@link MotionEvent#ACTION_HOVER_MOVE},
+ * or {@link MotionEvent#ACTION_HOVER_EXIT}.
+ * </p>
+ * <ul>
+ * <li>The view receives a hover event with action {@link MotionEvent#ACTION_HOVER_ENTER}
+ * when the pointer enters the bounds of the view.</li>
+ * <li>The view receives a hover event with action {@link MotionEvent#ACTION_HOVER_MOVE}
+ * when the pointer has already entered the bounds of the view and has moved.</li>
+ * <li>The view receives a hover event with action {@link MotionEvent#ACTION_HOVER_EXIT}
+ * when the pointer has exited the bounds of the view or when the pointer is
+ * about to go down due to a button click, tap, or similar user action that
+ * causes the view to be touched.</li>
+ * </ul>
+ * <p>
+ * The view should implement this method to return true to indicate that it is
+ * handling the hover event, such as by changing its drawable state.
* </p><p>
- * The view receives hover enter as the pointer enters the bounds of the view and hover
- * exit as the pointer exits the bound of the view or just before the pointer goes down
- * (which implies that {@link #onTouchEvent(MotionEvent)} will be called soon).
- * </p><p>
- * If the view would like to handle the hover event itself and prevent its children
- * from receiving hover, it should return true from this method. If this method returns
- * true and a child has already received a hover enter event, the child will
- * automatically receive a hover exit event.
- * </p><p>
- * The default implementation sets the hovered state of the view if the view is
- * clickable.
+ * The default implementation calls {@link #setHovered} to update the hovered state
+ * of the view when a hover enter or hover exit event is received, if the view
+ * is enabled and is clickable.
* </p>
*
* @param event The motion event that describes the hover.
- * @return True if this view handled the hover event and does not want its children
- * to receive the hover event.
+ * @return True if the view handled the hover event.
+ *
+ * @see #isHovered
+ * @see #setHovered
+ * @see #onHoverChanged
*/
public boolean onHoverEvent(MotionEvent event) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_HOVER_ENTER:
- setHovered(true);
- break;
+ if (isHoverable()) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_HOVER_ENTER:
+ setHovered(true);
+ break;
+ case MotionEvent.ACTION_HOVER_EXIT:
+ setHovered(false);
+ break;
+ }
+ return true;
+ }
+ return false;
+ }
- case MotionEvent.ACTION_HOVER_EXIT:
- setHovered(false);
- break;
+ /**
+ * Returns true if the view should handle {@link #onHoverEvent}
+ * by calling {@link #setHovered} to change its hovered state.
+ *
+ * @return True if the view is hoverable.
+ */
+ private boolean isHoverable() {
+ final int viewFlags = mViewFlags;
+ if ((viewFlags & ENABLED_MASK) == DISABLED) {
+ return false;
}
- return false;
+ return (viewFlags & CLICKABLE) == CLICKABLE
+ || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE;
}
/**
* Returns true if the view is currently hovered.
*
* @return True if the view is currently hovered.
+ *
+ * @see #setHovered
+ * @see #onHoverChanged
*/
+ @ViewDebug.ExportedProperty
public boolean isHovered() {
return (mPrivateFlags & HOVERED) != 0;
}
/**
* Sets whether the view is currently hovered.
+ * <p>
+ * Calling this method also changes the drawable state of the view. This
+ * enables the view to react to hover by using different drawable resources
+ * to change its appearance.
+ * </p><p>
+ * The {@link #onHoverChanged} method is called when the hovered state changes.
+ * </p>
*
* @param hovered True if the view is hovered.
+ *
+ * @see #isHovered
+ * @see #onHoverChanged
*/
public void setHovered(boolean hovered) {
if (hovered) {
if ((mPrivateFlags & HOVERED) == 0) {
mPrivateFlags |= HOVERED;
refreshDrawableState();
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
+ onHoverChanged(true);
}
} else {
if ((mPrivateFlags & HOVERED) != 0) {
mPrivateFlags &= ~HOVERED;
refreshDrawableState();
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
+ onHoverChanged(false);
}
}
}
/**
+ * Implement this method to handle hover state changes.
+ * <p>
+ * This method is called whenever the hover state changes as a result of a
+ * call to {@link #setHovered}.
+ * </p>
+ *
+ * @param hovered The current hover state, as returned by {@link #isHovered}.
+ *
+ * @see #isHovered
+ * @see #setHovered
+ */
+ public void onHoverChanged(boolean hovered) {
+ }
+
+ /**
* Implement this method to handle touch screen motion events.
*
* @param event The motion event.
@@ -8860,12 +8968,12 @@
// Start user padding override Left user padding. Otherwise, if Left user
// padding is not defined, use the default left padding. If Left user padding
// is defined, just use it.
- if (mUserPaddingStart >= 0) {
+ if (mUserPaddingStart > 0) {
mUserPaddingLeft = mUserPaddingStart;
} else if (mUserPaddingLeft < 0) {
mUserPaddingLeft = mPaddingLeft;
}
- if (mUserPaddingEnd >= 0) {
+ if (mUserPaddingEnd > 0) {
mUserPaddingRight = mUserPaddingEnd;
} else if (mUserPaddingRight < 0) {
mUserPaddingRight = mPaddingRight;
@@ -11026,6 +11134,10 @@
*/
public void setPaddingRelative(int start, int top, int end, int bottom) {
mUserPaddingRelative = true;
+
+ mUserPaddingStart = start;
+ mUserPaddingEnd = end;
+
switch(getResolvedLayoutDirection()) {
case LAYOUT_DIRECTION_RTL:
setPadding(end, top, start, bottom);
@@ -13097,6 +13209,24 @@
}
/**
+ * Interface definition for a callback to be invoked when a hover event is
+ * dispatched to this view. The callback will be invoked before the hover
+ * event is given to the view.
+ */
+ public interface OnHoverListener {
+ /**
+ * Called when a hover event is dispatched to a view. This allows listeners to
+ * get a chance to respond before the target view.
+ *
+ * @param v The view the hover event has been dispatched to.
+ * @param event The MotionEvent object containing full information about
+ * the event.
+ * @return True if the listener has consumed the event, false otherwise.
+ */
+ boolean onHover(View v, MotionEvent event);
+ }
+
+ /**
* Interface definition for a callback to be invoked when a generic motion event is
* dispatched to this view. The callback will be invoked before the generic motion
* event is given to the view.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index a6bce75..e928f80 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -143,8 +143,16 @@
@ViewDebug.ExportedProperty(category = "events")
private float mLastTouchDownY;
- // Child which last received ACTION_HOVER_ENTER and ACTION_HOVER_MOVE.
- private View mHoveredChild;
+ // First hover target in the linked list of hover targets.
+ // The hover targets are children which have received ACTION_HOVER_ENTER.
+ // They might not have actually handled the hover event, but we will
+ // continue sending hover events to them as long as the pointer remains over
+ // their bounds and the view group does not intercept hover.
+ private HoverTarget mFirstHoverTarget;
+
+ // True if the view group itself received a hover event.
+ // It might not have actually handled the hover event.
+ private boolean mHoveredSelf;
/**
* Internal flags.
@@ -1222,56 +1230,31 @@
return false;
}
- /** @hide */
+ /**
+ * {@inheritDoc}
+ */
@Override
protected boolean dispatchHoverEvent(MotionEvent event) {
- // Send the hover enter or hover move event to the view group first.
- // If it handles the event then a hovered child should receive hover exit.
- boolean handled = false;
- final boolean interceptHover;
final int action = event.getAction();
- if (action == MotionEvent.ACTION_HOVER_EXIT) {
- interceptHover = true;
- } else {
- handled = super.dispatchHoverEvent(event);
- interceptHover = handled;
- }
- // Send successive hover events to the hovered child as long as the pointer
- // remains within the child's bounds.
+ // First check whether the view group wants to intercept the hover event.
+ final boolean interceptHover = onInterceptHoverEvent(event);
+ event.setAction(action); // restore action in case it was changed
+
MotionEvent eventNoHistory = event;
- if (mHoveredChild != null) {
+ boolean handled = false;
+
+ // Send events to the hovered children and build a new list of hover targets until
+ // one is found that handles the event.
+ HoverTarget firstOldHoverTarget = mFirstHoverTarget;
+ mFirstHoverTarget = null;
+ if (!interceptHover && action != MotionEvent.ACTION_HOVER_EXIT) {
final float x = event.getX();
final float y = event.getY();
-
- if (interceptHover
- || !isTransformedTouchPointInView(x, y, mHoveredChild, null)) {
- // Pointer exited the child.
- // Send it a hover exit with only the most recent coordinates. We could
- // try to find the exact point in history when the pointer left the view
- // but it is not worth the effort.
- eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
- eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
- handled |= dispatchTransformedGenericPointerEvent(eventNoHistory, mHoveredChild);
- eventNoHistory.setAction(action);
- mHoveredChild = null;
- } else {
- // Pointer is still within the child.
- //noinspection ConstantConditions
- handled |= dispatchTransformedGenericPointerEvent(event, mHoveredChild);
- }
- }
-
- // Find a new hovered child if needed.
- if (!interceptHover && mHoveredChild == null
- && (action == MotionEvent.ACTION_HOVER_ENTER
- || action == MotionEvent.ACTION_HOVER_MOVE)) {
final int childrenCount = mChildrenCount;
if (childrenCount != 0) {
final View[] children = mChildren;
- final float x = event.getX();
- final float y = event.getY();
-
+ HoverTarget lastHoverTarget = null;
for (int i = childrenCount - 1; i >= 0; i--) {
final View child = children[i];
if (!canViewReceivePointerEvents(child)
@@ -1279,24 +1262,140 @@
continue;
}
- // Found the hovered child.
- mHoveredChild = child;
- if (action == MotionEvent.ACTION_HOVER_MOVE) {
- // Pointer was moving within the view group and entered the child.
- // Send it a hover enter and hover move with only the most recent
- // coordinates. We could try to find the exact point in history when
- // the pointer entered the view but it is not worth the effort.
- eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
- eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
- handled |= dispatchTransformedGenericPointerEvent(eventNoHistory, child);
- eventNoHistory.setAction(action);
+ // Obtain a hover target for this child. Dequeue it from the
+ // old hover target list if the child was previously hovered.
+ HoverTarget hoverTarget = firstOldHoverTarget;
+ final boolean wasHovered;
+ for (HoverTarget predecessor = null; ;) {
+ if (hoverTarget == null) {
+ hoverTarget = HoverTarget.obtain(child);
+ wasHovered = false;
+ break;
+ }
- handled |= dispatchTransformedGenericPointerEvent(eventNoHistory, child);
- } else { /* must be ACTION_HOVER_ENTER */
- // Pointer entered the child.
- handled |= dispatchTransformedGenericPointerEvent(event, child);
+ if (hoverTarget.child == child) {
+ if (predecessor != null) {
+ predecessor.next = hoverTarget.next;
+ } else {
+ firstOldHoverTarget = hoverTarget.next;
+ }
+ hoverTarget.next = null;
+ wasHovered = true;
+ break;
+ }
+
+ predecessor = hoverTarget;
+ hoverTarget = hoverTarget.next;
}
- break;
+
+ // Enqueue the hover target onto the new hover target list.
+ if (lastHoverTarget != null) {
+ lastHoverTarget.next = hoverTarget;
+ } else {
+ lastHoverTarget = hoverTarget;
+ mFirstHoverTarget = hoverTarget;
+ }
+
+ // Dispatch the event to the child.
+ if (action == MotionEvent.ACTION_HOVER_ENTER) {
+ if (!wasHovered) {
+ // Send the enter as is.
+ handled |= dispatchTransformedGenericPointerEvent(
+ event, child); // enter
+ }
+ } else if (action == MotionEvent.ACTION_HOVER_MOVE) {
+ if (!wasHovered) {
+ // Synthesize an enter from a move.
+ eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
+ eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
+ handled |= dispatchTransformedGenericPointerEvent(
+ eventNoHistory, child); // enter
+ eventNoHistory.setAction(action);
+
+ handled |= dispatchTransformedGenericPointerEvent(
+ eventNoHistory, child); // move
+ } else {
+ // Send the move as is.
+ handled |= dispatchTransformedGenericPointerEvent(event, child);
+ }
+ }
+ if (handled) {
+ break;
+ }
+ }
+ }
+ }
+
+ // Send exit events to all previously hovered children that are no longer hovered.
+ while (firstOldHoverTarget != null) {
+ final View child = firstOldHoverTarget.child;
+
+ // Exit the old hovered child.
+ if (action == MotionEvent.ACTION_HOVER_EXIT) {
+ // Send the exit as is.
+ handled |= dispatchTransformedGenericPointerEvent(
+ event, child); // exit
+ } else {
+ // Synthesize an exit from a move or enter.
+ // Ignore the result because hover focus has moved to a different view.
+ if (action == MotionEvent.ACTION_HOVER_MOVE) {
+ dispatchTransformedGenericPointerEvent(
+ event, child); // move
+ }
+ eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
+ eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
+ dispatchTransformedGenericPointerEvent(
+ eventNoHistory, child); // exit
+ eventNoHistory.setAction(action);
+ }
+
+ final HoverTarget nextOldHoverTarget = firstOldHoverTarget.next;
+ firstOldHoverTarget.recycle();
+ firstOldHoverTarget = nextOldHoverTarget;
+ }
+
+ // Send events to the view group itself if no children have handled it.
+ boolean newHoveredSelf = !handled;
+ if (newHoveredSelf == mHoveredSelf) {
+ if (newHoveredSelf) {
+ // Send event to the view group as before.
+ handled |= super.dispatchHoverEvent(event);
+ }
+ } else {
+ if (mHoveredSelf) {
+ // Exit the view group.
+ if (action == MotionEvent.ACTION_HOVER_EXIT) {
+ // Send the exit as is.
+ handled |= super.dispatchHoverEvent(event); // exit
+ } else {
+ // Synthesize an exit from a move or enter.
+ // Ignore the result because hover focus is moving to a different view.
+ if (action == MotionEvent.ACTION_HOVER_MOVE) {
+ super.dispatchHoverEvent(event); // move
+ }
+ eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
+ eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
+ super.dispatchHoverEvent(eventNoHistory); // exit
+ eventNoHistory.setAction(action);
+ }
+ mHoveredSelf = false;
+ }
+
+ if (newHoveredSelf) {
+ // Enter the view group.
+ if (action == MotionEvent.ACTION_HOVER_ENTER) {
+ // Send the enter as is.
+ handled |= super.dispatchHoverEvent(event); // enter
+ mHoveredSelf = true;
+ } else if (action == MotionEvent.ACTION_HOVER_MOVE) {
+ // Synthesize an enter from a move.
+ eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
+ eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
+ handled |= super.dispatchHoverEvent(eventNoHistory); // enter
+ eventNoHistory.setAction(action);
+
+ handled |= super.dispatchHoverEvent(eventNoHistory); // move
+ mHoveredSelf = true;
}
}
}
@@ -1306,25 +1405,55 @@
eventNoHistory.recycle();
}
- // Send hover exit to the view group. If there was a child, we will already have
- // sent the hover exit to it.
- if (action == MotionEvent.ACTION_HOVER_EXIT) {
- handled |= super.dispatchHoverEvent(event);
- }
-
// Done.
return handled;
}
+ /** @hide */
@Override
- public boolean onHoverEvent(MotionEvent event) {
- // Handle the event only if leaf. This guarantees that
- // the leafs (or any custom class that returns true from
- // this method) will get a change to process the hover.
- //noinspection SimplifiableIfStatement
- if (getChildCount() == 0) {
- return super.onHoverEvent(event);
- }
+ protected boolean hasHoveredChild() {
+ return mFirstHoverTarget != null;
+ }
+
+ /**
+ * Implement this method to intercept hover events before they are handled
+ * by child views.
+ * <p>
+ * This method is called before dispatching a hover event to a child of
+ * the view group or to the view group's own {@link #onHoverEvent} to allow
+ * the view group a chance to intercept the hover event.
+ * This method can also be used to watch all pointer motions that occur within
+ * the bounds of the view group even when the pointer is hovering over
+ * a child of the view group rather than over the view group itself.
+ * </p><p>
+ * The view group can prevent its children from receiving hover events by
+ * implementing this method and returning <code>true</code> to indicate
+ * that it would like to intercept hover events. The view group must
+ * continuously return <code>true</code> from {@link #onInterceptHoverEvent}
+ * for as long as it wishes to continue intercepting hover events from
+ * its children.
+ * </p><p>
+ * Interception preserves the invariant that at most one view can be
+ * hovered at a time by transferring hover focus from the currently hovered
+ * child to the view group or vice-versa as needed.
+ * </p><p>
+ * If this method returns <code>true</code> and a child is already hovered, then the
+ * child view will first receive a hover exit event and then the view group
+ * itself will receive a hover enter event in {@link #onHoverEvent}.
+ * Likewise, if this method had previously returned <code>true</code> to intercept hover
+ * events and instead returns <code>false</code> while the pointer is hovering
+ * within the bounds of one of a child, then the view group will first receive a
+ * hover exit event in {@link #onHoverEvent} and then the hovered child will
+ * receive a hover enter event.
+ * </p><p>
+ * The default implementation always returns false.
+ * </p>
+ *
+ * @param event The motion event that describes the hover.
+ * @return True if the view group would like to intercept the hover event
+ * and prevent its children from receiving it.
+ */
+ public boolean onInterceptHoverEvent(MotionEvent event) {
return false;
}
@@ -1335,7 +1464,9 @@
return MotionEvent.obtainNoHistory(event);
}
- /** @hide */
+ /**
+ * {@inheritDoc}
+ */
@Override
protected boolean dispatchGenericPointerEvent(MotionEvent event) {
// Send the event to the child under the pointer.
@@ -1362,7 +1493,9 @@
return super.dispatchGenericPointerEvent(event);
}
- /** @hide */
+ /**
+ * {@inheritDoc}
+ */
@Override
protected boolean dispatchGenericFocusedEvent(MotionEvent event) {
// Send the event to the focused child or to this view group if it has focus.
@@ -3337,10 +3470,6 @@
mTransition.removeChild(this, view);
}
- if (view == mHoveredChild) {
- mHoveredChild = null;
- }
-
boolean clearChildFocus = false;
if (view == mFocused) {
view.clearFocusForRemoval();
@@ -3404,7 +3533,6 @@
final OnHierarchyChangeListener onHierarchyChangeListener = mOnHierarchyChangeListener;
final boolean notifyListener = onHierarchyChangeListener != null;
final View focused = mFocused;
- final View hoveredChild = mHoveredChild;
final boolean detach = mAttachInfo != null;
View clearChildFocus = null;
@@ -3418,10 +3546,6 @@
mTransition.removeChild(this, view);
}
- if (view == hoveredChild) {
- mHoveredChild = null;
- }
-
if (view == focused) {
view.clearFocusForRemoval();
clearChildFocus = view;
@@ -3479,7 +3603,6 @@
final OnHierarchyChangeListener listener = mOnHierarchyChangeListener;
final boolean notify = listener != null;
final View focused = mFocused;
- final View hoveredChild = mHoveredChild;
final boolean detach = mAttachInfo != null;
View clearChildFocus = null;
@@ -3492,10 +3615,6 @@
mTransition.removeChild(this, view);
}
- if (view == hoveredChild) {
- mHoveredChild = null;
- }
-
if (view == focused) {
view.clearFocusForRemoval();
clearChildFocus = view;
@@ -5242,4 +5361,50 @@
}
}
}
+
+ /* Describes a hovered view. */
+ private static final class HoverTarget {
+ private static final int MAX_RECYCLED = 32;
+ private static final Object sRecycleLock = new Object();
+ private static HoverTarget sRecycleBin;
+ private static int sRecycledCount;
+
+ // The hovered child view.
+ public View child;
+
+ // The next target in the target list.
+ public HoverTarget next;
+
+ private HoverTarget() {
+ }
+
+ public static HoverTarget obtain(View child) {
+ final HoverTarget target;
+ synchronized (sRecycleLock) {
+ if (sRecycleBin == null) {
+ target = new HoverTarget();
+ } else {
+ target = sRecycleBin;
+ sRecycleBin = target.next;
+ sRecycledCount--;
+ target.next = null;
+ }
+ }
+ target.child = child;
+ return target;
+ }
+
+ public void recycle() {
+ synchronized (sRecycleLock) {
+ if (sRecycledCount < MAX_RECYCLED) {
+ next = sRecycleBin;
+ sRecycleBin = this;
+ sRecycledCount += 1;
+ } else {
+ next = null;
+ }
+ child = null;
+ }
+ }
+ }
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 18ef38a..555667b 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -252,7 +252,8 @@
/**
* Finds {@link AccessibilityNodeInfo}s by text. The match is case
- * insensitive containment.
+ * insensitive containment. The search is relative to this info i.e.
+ * this info is the root of the traversed tree.
*
* @param text The searched text.
* @return A list of node info.
@@ -260,7 +261,7 @@
public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) {
enforceSealed();
if (!canPerformRequestOverConnection(mAccessibilityViewId)) {
- return null;
+ return Collections.emptyList();
}
try {
return mConnection.findAccessibilityNodeInfosByViewText(text, mAccessibilityWindowId,
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index c56e6db..9f632d1 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -5707,8 +5707,8 @@
return false;
}
WebViewCore.CursorData data = cursorDataNoPosition();
- data.mX = viewToContentX((int) event.getX());
- data.mY = viewToContentY((int) event.getY());
+ data.mX = viewToContentX((int) event.getX() + mScrollX);
+ data.mY = viewToContentY((int) event.getY() + mScrollY);
mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
return true;
}
diff --git a/core/java/android/widget/PopupMenu.java b/core/java/android/widget/PopupMenu.java
index 82770ad..17512d8 100644
--- a/core/java/android/widget/PopupMenu.java
+++ b/core/java/android/widget/PopupMenu.java
@@ -18,6 +18,7 @@
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.view.menu.MenuPopupHelper;
+import com.android.internal.view.menu.MenuPresenter;
import com.android.internal.view.menu.SubMenuBuilder;
import android.content.Context;
@@ -32,12 +33,25 @@
* If the IME is visible the popup will not overlap it until it is touched. Touching outside
* of the popup will dismiss it.
*/
-public class PopupMenu implements MenuBuilder.Callback {
+public class PopupMenu implements MenuBuilder.Callback, MenuPresenter.Callback {
private Context mContext;
private MenuBuilder mMenu;
private View mAnchor;
private MenuPopupHelper mPopup;
private OnMenuItemClickListener mMenuItemClickListener;
+ private OnDismissListener mDismissListener;
+
+ /**
+ * Callback interface used to notify the application that the menu has closed.
+ */
+ public interface OnDismissListener {
+ /**
+ * Called when the associated menu has been dismissed.
+ *
+ * @param menu The PopupMenu that was dismissed.
+ */
+ public void onDismiss(PopupMenu menu);
+ }
/**
* Construct a new PopupMenu.
@@ -53,6 +67,7 @@
mMenu.setCallback(this);
mAnchor = anchor;
mPopup = new MenuPopupHelper(context, mMenu, anchor);
+ mPopup.setCallback(this);
}
/**
@@ -77,6 +92,15 @@
}
/**
+ * Inflate a menu resource into this PopupMenu. This is equivalent to calling
+ * popupMenu.getMenuInflater().inflate(menuRes, popupMenu.getMenu()).
+ * @param menuRes Menu resource to inflate
+ */
+ public void inflate(int menuRes) {
+ getMenuInflater().inflate(menuRes, mMenu);
+ }
+
+ /**
* Show the menu popup anchored to the view specified during construction.
* @see #dismiss()
*/
@@ -92,11 +116,25 @@
mPopup.dismiss();
}
+ /**
+ * Set a listener that will be notified when the user selects an item from the menu.
+ *
+ * @param listener Listener to notify
+ */
public void setOnMenuItemClickListener(OnMenuItemClickListener listener) {
mMenuItemClickListener = listener;
}
/**
+ * Set a listener that will be notified when this menu is dismissed.
+ *
+ * @param listener Listener to notify
+ */
+ public void setOnDismissListener(OnDismissListener listener) {
+ mDismissListener = listener;
+ }
+
+ /**
* @hide
*/
public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
@@ -110,12 +148,15 @@
* @hide
*/
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+ if (mDismissListener != null) {
+ mDismissListener.onDismiss(this);
+ }
}
/**
* @hide
*/
- public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
+ public boolean onOpenSubMenu(MenuBuilder subMenu) {
if (!subMenu.hasVisibleItems()) {
return true;
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 02c2b8f..77df7c8 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -7658,12 +7658,17 @@
}
@Override
- public void findViewsWithText(ArrayList<View> outViews, CharSequence text) {
+ public void findViewsWithText(ArrayList<View> outViews, CharSequence searched) {
+ if (TextUtils.isEmpty(searched)) {
+ return;
+ }
CharSequence thisText = getText();
if (TextUtils.isEmpty(thisText)) {
return;
}
- if (thisText.toString().toLowerCase().contains(text)) {
+ String searchedLowerCase = searched.toString().toLowerCase();
+ String thisTextLowerCase = thisText.toString().toLowerCase();
+ if (thisTextLowerCase.contains(searched)) {
outViews.add(this);
}
}
diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
index 98c2747..322a854 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
@@ -336,6 +336,7 @@
if (groupId != 0) {
seenGroups.put(groupId, true);
}
+ item.setIsActionButton(true);
} else if (item.requestsActionButton()) {
// Items in a group with other items that already have an action slot
// can break the max actions rule, but not the width limit.
diff --git a/core/java/com/android/internal/view/menu/ListMenuPresenter.java b/core/java/com/android/internal/view/menu/ListMenuPresenter.java
index cc09927..27e4191 100644
--- a/core/java/com/android/internal/view/menu/ListMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ListMenuPresenter.java
@@ -184,12 +184,12 @@
private class MenuAdapter extends BaseAdapter {
public int getCount() {
- ArrayList<MenuItemImpl> items = mMenu.getVisibleItems();
+ ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
return items.size() - mItemIndexOffset;
}
public MenuItemImpl getItem(int position) {
- ArrayList<MenuItemImpl> items = mMenu.getVisibleItems();
+ ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
return items.get(position + mItemIndexOffset);
}
diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java
index 1a6cc54..253511c 100644
--- a/core/java/com/android/internal/view/menu/MenuItemImpl.java
+++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java
@@ -507,7 +507,7 @@
}
public boolean isActionButton() {
- return (mFlags & IS_ACTION) == IS_ACTION || requiresActionButton();
+ return (mFlags & IS_ACTION) == IS_ACTION;
}
public boolean requestsActionButton() {
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index 8db7e3c..cffbb4e 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -126,6 +126,7 @@
public void onDismiss() {
mPopup = null;
+ mMenu.close();
if (mTreeObserver != null) {
if (!mTreeObserver.isAlive()) mTreeObserver = mAnchorView.getViewTreeObserver();
mTreeObserver.removeGlobalOnLayoutListener(this);
diff --git a/core/jni/android_pim_EventRecurrence.cpp b/core/jni/android_pim_EventRecurrence.cpp
index 44e898d..3e11569 100644
--- a/core/jni/android_pim_EventRecurrence.cpp
+++ b/core/jni/android_pim_EventRecurrence.cpp
@@ -147,7 +147,7 @@
*/
static JNINativeMethod METHODS[] = {
/* name, signature, funcPtr */
- { "parse", "(Ljava/lang/String;)V", (void*)EventRecurrence_parse }
+ { "parseNative", "(Ljava/lang/String;)V", (void*)EventRecurrence_parse }
};
static const char*const CLASS_NAME = "android/pim/EventRecurrence";
diff --git a/core/res/res/drawable-hdpi/ic_notification_ime_default.png b/core/res/res/drawable-hdpi/ic_notification_ime_default.png
new file mode 100644
index 0000000..1a9d88c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_notification_ime_default.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_notification_ime_default.png b/core/res/res/drawable-mdpi/ic_notification_ime_default.png
new file mode 100644
index 0000000..1a9d88c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_notification_ime_default.png
Binary files differ
diff --git a/core/res/res/values-sw600dp/bools.xml b/core/res/res/values-sw600dp/bools.xml
index 734031f..d73ff99 100644
--- a/core/res/res/values-sw600dp/bools.xml
+++ b/core/res/res/values-sw600dp/bools.xml
@@ -16,4 +16,5 @@
<resources>
<bool name="preferences_prefer_dual_pane">true</bool>
+ <bool name="show_ongoing_ime_switcher">false</bool>
</resources>
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index 9d6309d..9647bb7 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -19,4 +19,5 @@
<bool name="action_bar_embed_tabs">false</bool>
<bool name="split_action_bar_is_narrow">true</bool>
<bool name="preferences_prefer_dual_pane">false</bool>
+ <bool name="show_ongoing_ime_switcher">true</bool>
</resources>
diff --git a/core/tests/coretests/src/android/pim/EventRecurrenceTest.java b/core/tests/coretests/src/android/pim/EventRecurrenceTest.java
new file mode 100644
index 0000000..05000f1
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/EventRecurrenceTest.java
@@ -0,0 +1,753 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.pim;
+
+import android.pim.EventRecurrence.InvalidFormatException;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
+
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+
+/**
+ * Test android.pim.EventRecurrence.
+ *
+ * adb shell am instrument -w -e class android.pim.EventRecurrenceTest \
+ * com.android.frameworks.coretests/android.test.InstrumentationTestRunner
+ */
+public class EventRecurrenceTest extends TestCase {
+
+ @SmallTest
+ public void test0() throws Exception {
+ verifyRecurType("FREQ=SECONDLY",
+ /* int freq */ EventRecurrence.SECONDLY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ @SmallTest
+ public void test1() throws Exception {
+ verifyRecurType("FREQ=MINUTELY",
+ /* int freq */ EventRecurrence.MINUTELY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ @SmallTest
+ public void test2() throws Exception {
+ verifyRecurType("FREQ=HOURLY",
+ /* int freq */ EventRecurrence.HOURLY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ @SmallTest
+ public void test3() throws Exception {
+ verifyRecurType("FREQ=DAILY",
+ /* int freq */ EventRecurrence.DAILY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ @SmallTest
+ public void test4() throws Exception {
+ verifyRecurType("FREQ=WEEKLY",
+ /* int freq */ EventRecurrence.WEEKLY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ @SmallTest
+ public void test5() throws Exception {
+ verifyRecurType("FREQ=MONTHLY",
+ /* int freq */ EventRecurrence.MONTHLY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ @SmallTest
+ public void test6() throws Exception {
+ verifyRecurType("FREQ=YEARLY",
+ /* int freq */ EventRecurrence.YEARLY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ @SmallTest
+ public void test7() throws Exception {
+ // with an until
+ verifyRecurType("FREQ=DAILY;UNTIL=112233T223344Z",
+ /* int freq */ EventRecurrence.DAILY,
+ /* String until */ "112233T223344Z",
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ @SmallTest
+ public void test8() throws Exception {
+ // with a count
+ verifyRecurType("FREQ=DAILY;COUNT=334",
+ /* int freq */ EventRecurrence.DAILY,
+ /* String until */ null,
+ /* int count */ 334,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ @SmallTest
+ public void test9() throws Exception {
+ // with a count
+ verifyRecurType("FREQ=DAILY;INTERVAL=5000",
+ /* int freq */ EventRecurrence.DAILY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 5000,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ @SmallTest
+ public void test10() throws Exception {
+ // verifyRecurType all of the BY* ones with one element
+ verifyRecurType("FREQ=DAILY"
+ + ";BYSECOND=0"
+ + ";BYMINUTE=1"
+ + ";BYHOUR=2"
+ + ";BYMONTHDAY=30"
+ + ";BYYEARDAY=300"
+ + ";BYWEEKNO=53"
+ + ";BYMONTH=12"
+ + ";BYSETPOS=-15"
+ + ";WKST=SU",
+ /* int freq */ EventRecurrence.DAILY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ new int[]{0},
+ /* int[] byminute */ new int[]{1},
+ /* int[] byhour */ new int[]{2},
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ new int[]{30},
+ /* int[] byyearday */ new int[]{300},
+ /* int[] byweekno */ new int[]{53},
+ /* int[] bymonth */ new int[]{12},
+ /* int[] bysetpos */ new int[]{-15},
+ /* int wkst */ EventRecurrence.SU
+ );
+ }
+
+ @SmallTest
+ public void test11() throws Exception {
+ // verifyRecurType all of the BY* ones with one element
+ verifyRecurType("FREQ=DAILY"
+ + ";BYSECOND=0,30,59"
+ + ";BYMINUTE=0,41,59"
+ + ";BYHOUR=0,4,23"
+ + ";BYMONTHDAY=-31,-1,1,31"
+ + ";BYYEARDAY=-366,-1,1,366"
+ + ";BYWEEKNO=-53,-1,1,53"
+ + ";BYMONTH=1,12"
+ + ";BYSETPOS=1,2,3,4,500,10000"
+ + ";WKST=SU",
+ /* int freq */ EventRecurrence.DAILY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ new int[]{0, 30, 59},
+ /* int[] byminute */ new int[]{0, 41, 59},
+ /* int[] byhour */ new int[]{0, 4, 23},
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ new int[]{-31, -1, 1, 31},
+ /* int[] byyearday */ new int[]{-366, -1, 1, 366},
+ /* int[] byweekno */ new int[]{-53, -1, 1, 53},
+ /* int[] bymonth */ new int[]{1, 12},
+ /* int[] bysetpos */ new int[]{1, 2, 3, 4, 500, 10000},
+ /* int wkst */ EventRecurrence.SU
+ );
+ }
+
+ private static class Check {
+ Check(String k, int... v) {
+ key = k;
+ values = v;
+ }
+
+ String key;
+ int[] values;
+ }
+
+ // this is a negative verifyRecurType case to verifyRecurType the range of the numbers accepted
+ @SmallTest
+ public void test12() throws Exception {
+ Check[] checks = new Check[]{
+ new Check("BYSECOND", -100, -1, 60, 100),
+ new Check("BYMINUTE", -100, -1, 60, 100),
+ new Check("BYHOUR", -100, -1, 24, 100),
+ new Check("BYMONTHDAY", -100, -32, 0, 32, 100),
+ new Check("BYYEARDAY", -400, -367, 0, 367, 400),
+ new Check("BYWEEKNO", -100, -54, 0, 54, 100),
+ new Check("BYMONTH", -100, -5, 0, 13, 100)
+ };
+
+ for (Check ck : checks) {
+ for (int n : ck.values) {
+ String recur = "FREQ=DAILY;" + ck.key + "=" + n;
+ try {
+ EventRecurrence er = new EventRecurrence();
+ er.parse(recur);
+ fail("Negative verifyRecurType failed. "
+ + " parse failed to throw an exception for '"
+ + recur + "'");
+ } catch (EventRecurrence.InvalidFormatException e) {
+ // expected
+ }
+ }
+ }
+ }
+
+ // verifyRecurType BYDAY
+ @SmallTest
+ public void test13() throws Exception {
+ verifyRecurType("FREQ=DAILY;BYDAY=1SU,-2MO,+33TU,WE,TH,FR,SA",
+ /* int freq */ EventRecurrence.DAILY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ new int[] {
+ EventRecurrence.SU,
+ EventRecurrence.MO,
+ EventRecurrence.TU,
+ EventRecurrence.WE,
+ EventRecurrence.TH,
+ EventRecurrence.FR,
+ EventRecurrence.SA
+ },
+ /* int[] bydayNum */ new int[]{1, -2, 33, 0, 0, 0, 0},
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ @Suppress
+ // Repro bug #2331761 - this should fail because of the last comma into BYDAY
+ public void test14() throws Exception {
+ verifyRecurType("FREQ=WEEKLY;WKST=MO;UNTIL=20100129T130000Z;INTERVAL=1;BYDAY=MO,TU,WE,",
+ /* int freq */ EventRecurrence.WEEKLY,
+ /* String until */ "20100129T130000Z",
+ /* int count */ 0,
+ /* int interval */ 1,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ new int[] {
+ EventRecurrence.MO,
+ EventRecurrence.TU,
+ EventRecurrence.WE,
+ },
+ /* int[] bydayNum */ new int[]{0, 0, 0},
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ // This test should pass
+ public void test15() throws Exception {
+ verifyRecurType("FREQ=WEEKLY;WKST=MO;UNTIL=20100129T130000Z;INTERVAL=1;"
+ + "BYDAY=MO,TU,WE,TH,FR,SA,SU",
+ /* int freq */ EventRecurrence.WEEKLY,
+ /* String until */ "20100129T130000Z",
+ /* int count */ 0,
+ /* int interval */ 1,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ new int[] {
+ EventRecurrence.MO,
+ EventRecurrence.TU,
+ EventRecurrence.WE,
+ EventRecurrence.TH,
+ EventRecurrence.FR,
+ EventRecurrence.SA,
+ EventRecurrence.SU
+ },
+ /* int[] bydayNum */ new int[]{0, 0, 0, 0, 0, 0, 0},
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ // Sample coming from RFC2445
+ public void test16() throws Exception {
+ verifyRecurType("FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1",
+ /* int freq */ EventRecurrence.MONTHLY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ new int[] {
+ EventRecurrence.MO,
+ EventRecurrence.TU,
+ EventRecurrence.WE,
+ EventRecurrence.TH,
+ EventRecurrence.FR
+ },
+ /* int[] bydayNum */ new int[] {0, 0, 0, 0, 0},
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ new int[] { -1 },
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ // Sample coming from RFC2445
+ public void test17() throws Exception {
+ verifyRecurType("FREQ=DAILY;COUNT=10;INTERVAL=2",
+ /* int freq */ EventRecurrence.DAILY,
+ /* String until */ null,
+ /* int count */ 10,
+ /* int interval */ 2,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ // Sample coming from RFC2445
+ public void test18() throws Exception {
+ verifyRecurType("FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10",
+ /* int freq */ EventRecurrence.YEARLY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ new int[] {
+ EventRecurrence.SU
+ },
+ /* int[] bydayNum */ new int[] { -1 },
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ new int[] { 10 },
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ // Sample coming from bug #1640517
+ public void test19() throws Exception {
+ verifyRecurType("FREQ=YEARLY;BYMONTH=3;BYDAY=TH",
+ /* int freq */ EventRecurrence.YEARLY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ new int[] {
+ EventRecurrence.TH
+ },
+ /* int[] bydayNum */ new int[] { 0 },
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ new int[] { 3 },
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ // for your copying pleasure
+ public void fakeTestXX() throws Exception {
+ verifyRecurType("FREQ=DAILY;",
+ /* int freq */ EventRecurrence.DAILY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ private static void cmp(int vlen, int[] v, int[] correct, String name) {
+ if ((correct == null && v != null)
+ || (correct != null && v == null)) {
+ throw new RuntimeException("One is null, one isn't for " + name
+ + ": correct=" + Arrays.toString(correct)
+ + " actual=" + Arrays.toString(v));
+ }
+ if ((correct == null && vlen != 0)
+ || (vlen != (correct == null ? 0 : correct.length))) {
+ throw new RuntimeException("Reported length mismatch for " + name
+ + ": correct=" + ((correct == null) ? "null" : correct.length)
+ + " actual=" + vlen);
+ }
+ if (correct == null) {
+ return;
+ }
+ if (v.length < correct.length) {
+ throw new RuntimeException("Array length mismatch for " + name
+ + ": correct=" + Arrays.toString(correct)
+ + " actual=" + Arrays.toString(v));
+ }
+ for (int i = 0; i < correct.length; i++) {
+ if (v[i] != correct[i]) {
+ throw new RuntimeException("Array value mismatch for " + name
+ + ": correct=" + Arrays.toString(correct)
+ + " actual=" + Arrays.toString(v));
+ }
+ }
+ }
+
+ private static boolean eq(String a, String b) {
+ if ((a == null && b != null) || (a != null && b == null)) {
+ return false;
+ } else {
+ return a == b || a.equals(b);
+ }
+ }
+
+ private static void verifyRecurType(String recur,
+ int freq, String until, int count, int interval,
+ int[] bysecond, int[] byminute, int[] byhour,
+ int[] byday, int[] bydayNum, int[] bymonthday,
+ int[] byyearday, int[] byweekno, int[] bymonth,
+ int[] bysetpos, int wkst) {
+ EventRecurrence eventRecurrence = new EventRecurrence();
+ eventRecurrence.parse(recur);
+ if (eventRecurrence.freq != freq
+ || !eq(eventRecurrence.until, until)
+ || eventRecurrence.count != count
+ || eventRecurrence.interval != interval
+ || eventRecurrence.wkst != wkst) {
+ System.out.println("Error... got:");
+ print(eventRecurrence);
+ System.out.println("expected:");
+ System.out.println("{");
+ System.out.println(" freq=" + freq);
+ System.out.println(" until=" + until);
+ System.out.println(" count=" + count);
+ System.out.println(" interval=" + interval);
+ System.out.println(" wkst=" + wkst);
+ System.out.println(" bysecond=" + Arrays.toString(bysecond));
+ System.out.println(" byminute=" + Arrays.toString(byminute));
+ System.out.println(" byhour=" + Arrays.toString(byhour));
+ System.out.println(" byday=" + Arrays.toString(byday));
+ System.out.println(" bydayNum=" + Arrays.toString(bydayNum));
+ System.out.println(" bymonthday=" + Arrays.toString(bymonthday));
+ System.out.println(" byyearday=" + Arrays.toString(byyearday));
+ System.out.println(" byweekno=" + Arrays.toString(byweekno));
+ System.out.println(" bymonth=" + Arrays.toString(bymonth));
+ System.out.println(" bysetpos=" + Arrays.toString(bysetpos));
+ System.out.println("}");
+ throw new RuntimeException("Mismatch in fields");
+ }
+ cmp(eventRecurrence.bysecondCount, eventRecurrence.bysecond, bysecond, "bysecond");
+ cmp(eventRecurrence.byminuteCount, eventRecurrence.byminute, byminute, "byminute");
+ cmp(eventRecurrence.byhourCount, eventRecurrence.byhour, byhour, "byhour");
+ cmp(eventRecurrence.bydayCount, eventRecurrence.byday, byday, "byday");
+ cmp(eventRecurrence.bydayCount, eventRecurrence.bydayNum, bydayNum, "bydayNum");
+ cmp(eventRecurrence.bymonthdayCount, eventRecurrence.bymonthday, bymonthday, "bymonthday");
+ cmp(eventRecurrence.byyeardayCount, eventRecurrence.byyearday, byyearday, "byyearday");
+ cmp(eventRecurrence.byweeknoCount, eventRecurrence.byweekno, byweekno, "byweekno");
+ cmp(eventRecurrence.bymonthCount, eventRecurrence.bymonth, bymonth, "bymonth");
+ cmp(eventRecurrence.bysetposCount, eventRecurrence.bysetpos, bysetpos, "bysetpos");
+ }
+
+ private static void print(EventRecurrence er) {
+ System.out.println("{");
+ System.out.println(" freq=" + er.freq);
+ System.out.println(" until=" + er.until);
+ System.out.println(" count=" + er.count);
+ System.out.println(" interval=" + er.interval);
+ System.out.println(" wkst=" + er.wkst);
+ System.out.println(" bysecond=" + Arrays.toString(er.bysecond));
+ System.out.println(" bysecondCount=" + er.bysecondCount);
+ System.out.println(" byminute=" + Arrays.toString(er.byminute));
+ System.out.println(" byminuteCount=" + er.byminuteCount);
+ System.out.println(" byhour=" + Arrays.toString(er.byhour));
+ System.out.println(" byhourCount=" + er.byhourCount);
+ System.out.println(" byday=" + Arrays.toString(er.byday));
+ System.out.println(" bydayNum=" + Arrays.toString(er.bydayNum));
+ System.out.println(" bydayCount=" + er.bydayCount);
+ System.out.println(" bymonthday=" + Arrays.toString(er.bymonthday));
+ System.out.println(" bymonthdayCount=" + er.bymonthdayCount);
+ System.out.println(" byyearday=" + Arrays.toString(er.byyearday));
+ System.out.println(" byyeardayCount=" + er.byyeardayCount);
+ System.out.println(" byweekno=" + Arrays.toString(er.byweekno));
+ System.out.println(" byweeknoCount=" + er.byweeknoCount);
+ System.out.println(" bymonth=" + Arrays.toString(er.bymonth));
+ System.out.println(" bymonthCount=" + er.bymonthCount);
+ System.out.println(" bysetpos=" + Arrays.toString(er.bysetpos));
+ System.out.println(" bysetposCount=" + er.bysetposCount);
+ System.out.println("}");
+ }
+
+
+ /** A list of valid rules. The parser must accept these. */
+ private static final String[] GOOD_RRULES = {
+ /* extracted wholesale from from RFC 2445 section 4.8.5.4 */
+ "FREQ=DAILY;COUNT=10",
+ "FREQ=DAILY;UNTIL=19971224T000000Z",
+ "FREQ=DAILY;INTERVAL=2",
+ "FREQ=DAILY;INTERVAL=10;COUNT=5",
+ "FREQ=YEARLY;UNTIL=20000131T090000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA",
+ "FREQ=DAILY;UNTIL=20000131T090000Z;BYMONTH=1",
+ "FREQ=WEEKLY;COUNT=10",
+ "FREQ=WEEKLY;UNTIL=19971224T000000Z",
+ "FREQ=WEEKLY;INTERVAL=2;WKST=SU",
+ "FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH",
+ "FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH",
+ "FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR",
+ "FREQ=WEEKLY;INTERVAL=2;COUNT=8;WKST=SU;BYDAY=TU,TH",
+ "FREQ=MONTHLY;COUNT=10;BYDAY=1FR",
+ "FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR",
+ "FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU",
+ "FREQ=MONTHLY;COUNT=6;BYDAY=-2MO",
+ "FREQ=MONTHLY;BYMONTHDAY=-3",
+ "FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15",
+ "FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1",
+ "FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15",
+ "FREQ=MONTHLY;INTERVAL=2;BYDAY=TU",
+ "FREQ=YEARLY;COUNT=10;BYMONTH=6,7",
+ "FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3",
+ "FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200",
+ "FREQ=YEARLY;BYDAY=20MO",
+ "FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO",
+ "FREQ=YEARLY;BYMONTH=3;BYDAY=TH",
+ "FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8",
+ "FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13",
+ "FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13",
+ "FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8",
+ "FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3",
+ "FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2",
+ "FREQ=HOURLY;INTERVAL=3;UNTIL=19970902T170000Z",
+ "FREQ=MINUTELY;INTERVAL=15;COUNT=6",
+ "FREQ=MINUTELY;INTERVAL=90;COUNT=4",
+ "FREQ=DAILY;BYHOUR=9,10,11,12,13,14,15,16;BYMINUTE=0,20,40",
+ "FREQ=MINUTELY;INTERVAL=20;BYHOUR=9,10,11,12,13,14,15,16",
+ "FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=MO",
+ "FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=SU",
+ /* a few more */
+ "FREQ=SECONDLY;BYSECOND=0,15,59",
+ "FREQ=MINUTELY;BYMINUTE=0,15,59",
+ "FREQ=HOURLY;BYHOUR=+0,+15,+23",
+ "FREQ=DAILY;X-WHATEVER=blah", // fails on old parser
+ //"freq=daily;wkst=su", // fails on old parser
+ };
+
+ /** The parser must reject these. */
+ private static final String[] BAD_RRULES = {
+ "INTERVAL=4;FREQ=YEARLY", // FREQ must come first
+ "FREQ=MONTHLY;FREQ=MONTHLY", // can't specify twice
+ "FREQ=MONTHLY;COUNT=1;COUNT=1", // can't specify twice
+ "FREQ=SECONDLY;BYSECOND=60", // range
+ "FREQ=MINUTELY;BYMINUTE=-1", // range
+ "FREQ=HOURLY;BYHOUR=24", // range
+ "FREQ=YEARLY;BYMONTHDAY=0", // zero not valid
+ //"FREQ=YEARLY;COUNT=1;UNTIL=12345", // can't have both COUNT and UNTIL
+ //"FREQ=DAILY;UNTIL=19970829T021400e", // invalid date
+ };
+
+ /**
+ * Simple test of good/bad rules.
+ */
+ @SmallTest
+ public void testBasicParse() {
+ for (String rule : GOOD_RRULES) {
+ EventRecurrence recur = new EventRecurrence();
+ recur.parse(rule);
+ }
+
+ for (String rule : BAD_RRULES) {
+ EventRecurrence recur = new EventRecurrence();
+ boolean didThrow = false;
+
+ try {
+ recur.parse(rule);
+ } catch (InvalidFormatException ife) {
+ didThrow = true;
+ }
+
+ assertTrue("Expected throw on " + rule, didThrow);
+ }
+ }
+}
diff --git a/docs/html/guide/topics/usb/adk.jd b/docs/html/guide/topics/usb/adk.jd
index e4e1215..0e35637 100644
--- a/docs/html/guide/topics/usb/adk.jd
+++ b/docs/html/guide/topics/usb/adk.jd
@@ -55,7 +55,6 @@
</ol>
-
<h2>See also</h2>
<ol>
@@ -66,6 +65,12 @@
<h2>Where to buy</h2>
<ol>
+ <li><a href="http://shop.moderndevice.com/products/freeduino-usb-host-board">
+ Modern Device</a></li>
+
+ <li><a href="http://www.seeedstudio.com/depot/seeeduino-adk-main-board-p-846.html">
+ Seeed Studio</a></li>
+
<li><a href=
"http://www.rt-net.jp/shop/index.php?main_page=product_info&cPath=3_4&products_id=1">
RT Corp</a></li>
@@ -77,8 +82,6 @@
<li><a href="https://store.diydrones.com/ProductDetails.asp?ProductCode=BR-PhoneDrone">
DIY Drones</a></li>
- <li><a href="http://shop.moderndevice.com/products/freeduino-usb-host-board">
- Modern Device</a></li>
</ol>
</div>
</div>
@@ -106,15 +109,22 @@
development boards:</p>
<ul>
+ <li><a href="http://shop.moderndevice.com/products/freeduino-usb-host-board">Modern
+ Device</a> provides an Arduino-compatible board that supports the ADK firmware.</li>
+
+ <li><a href="http://www.seeedstudio.com/depot/seeeduino-adk-main-board-p-846.html">
+ Seeed Studio</a> provides an Arduino-compatible board that supports the ADK firmware.</li>
+
<li><a href="http://www.rt-net.jp/shop/index.php?main_page=product_info&cPath=3_4&products_id=1">
RT Corp</a> provides an Arduino-compatible board based on the Android ADK board design.</li>
+
<li><a href="http://www.microchip.com/android">Microchip</a> provides a A PIC based USB
microcontroller board.</li>
+
<li><a href="https://store.diydrones.com/ProductDetails.asp?ProductCode=BR-PhoneDrone">DIY
Drones</a> provides an Arduino-compatible board geared towards RC (radio controlled) and UAV
(unmanned aerial vehicle) enthusiasts.</li>
- <li><a href="http://shop.moderndevice.com/products/freeduino-usb-host-board">Modern
- Device</a> provides an Arduino-compatible board that supports the ADK firmware.</li>
+
</ul>
<p>We expect more hardware distributers to create a variety of kits, so please stay tuned for
@@ -125,7 +135,7 @@
accessory that is based on the <a href="http://www.arduino.cc/">Arduino open source electronics
prototyping platform</a>, the accessory's hardware design files, code that implements the
accessory's firmware, and the Android application that interacts with the accessory. The hardware
- design files and firmware code are contained in the <a href=
+ design files and firmware code are contained in the <a href=ctive
"https://dl-ssl.google.com/android/adk/adk_release_0512.zip">ADK package download</a>.</p>
<p>The main hardware and software components of the ADK include:</p>
diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs
index 5750c81..d02c13d 100644
--- a/docs/html/sdk/sdk_toc.cs
+++ b/docs/html/sdk/sdk_toc.cs
@@ -175,7 +175,8 @@
<span style="display:none" class="zh-TW"></span>
</h2>
<ul>
- <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r5c</a>
+ <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r5c <span
+ class="new">new!</span></a>
</li>
<li><a href="<?cs var:toroot ?>sdk/ndk/overview.html">What is the NDK?</a></li>
</ul>
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
index 07b3b47..31c3c14 100644
--- a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
@@ -512,6 +512,19 @@
}
}
+ if (NULL != decryptHandle) {
+ if (NULL != decryptHandle->decryptInfo) {
+ delete decryptHandle->decryptInfo;
+ decryptHandle->decryptInfo = NULL;
+ }
+
+ decryptHandle->copyControlVector.clear();
+ decryptHandle->extendedData.clear();
+
+ delete decryptHandle;
+ decryptHandle = NULL;
+ }
+
LOGV("FwdLockEngine::onCloseDecryptSession Exit");
return result;
}
diff --git a/graphics/java/android/renderscript/AllocationAdapter.java b/graphics/java/android/renderscript/AllocationAdapter.java
index 61f2e1f..77dd86a 100644
--- a/graphics/java/android/renderscript/AllocationAdapter.java
+++ b/graphics/java/android/renderscript/AllocationAdapter.java
@@ -27,8 +27,14 @@
*
**/
public class AllocationAdapter extends Allocation {
+ private boolean mConstrainedLOD;
+ private boolean mConstrainedFace;
+ private boolean mConstrainedY;
+ private boolean mConstrainedZ;
+
private int mSelectedDimX;
private int mSelectedDimY;
+ private int mSelectedDimZ;
private int mSelectedCount;
private Allocation mAlloc;
@@ -37,13 +43,14 @@
AllocationAdapter(int id, RenderScript rs, Allocation alloc) {
super(id, rs, null, alloc.mUsage);
- Type t = alloc.getType();
- mSelectedDimX = t.getX();
- mSelectedDimY = t.getY();
- mSelectedCount = t.getCount();
+ mAlloc = alloc;
}
+ int getID() {
+ return mAlloc.getID();
+ }
+
public void copyFrom(BaseObj[] d) {
mRS.validate();
if (d.length != mSelectedCount) {
@@ -54,7 +61,7 @@
for (int ct=0; ct < d.length; ct++) {
i[ct] = d[ct].getID();
}
- subData1D(0, mType.getCount(), i);
+ subData1D(0, mAlloc.mType.getCount(), i);
}
void validateBitmap(Bitmap b) {
@@ -93,7 +100,7 @@
public void subData(int xoff, FieldPacker fp) {
- int eSize = mType.mElement.getSizeBytes();
+ int eSize = mAlloc.mType.mElement.getSizeBytes();
final byte[] data = fp.getData();
int count = data.length / eSize;
@@ -107,7 +114,7 @@
public void subElementData(int xoff, int component_number, FieldPacker fp) {
- if (component_number >= mType.mElement.mElements.length) {
+ if (component_number >= mAlloc.mType.mElement.mElements.length) {
throw new RSIllegalArgumentException("Component_number " + component_number + " out of range.");
}
if(xoff < 0) {
@@ -115,7 +122,7 @@
}
final byte[] data = fp.getData();
- int eSize = mType.mElement.mElements[component_number].getSizeBytes();
+ int eSize = mAlloc.mType.mElement.mElements[component_number].getSizeBytes();
if (data.length != eSize) {
throw new RSIllegalArgumentException("Field packer sizelength " + data.length +
@@ -133,12 +140,13 @@
if(count < 1) {
throw new RSIllegalArgumentException("Count must be >= 1.");
}
- if((off + count) > mSelectedDimX * mSelectedDimY) {
- throw new RSIllegalArgumentException("Overflow, Available count " + mType.getCount() +
+ if((off + count) > mSelectedCount) {
+ throw new RSIllegalArgumentException("Overflow, Available count " + mAlloc.mType.getCount() +
", got " + count + " at offset " + off + ".");
}
if((len) < dataSize) {
- throw new RSIllegalArgumentException("Array too small for allocation type.");
+ throw new RSIllegalArgumentException("Array too small for allocation type. len = " +
+ len + ", dataSize = " + dataSize);
}
}
@@ -223,8 +231,51 @@
mRS.nAllocationRead(getID(), d);
}
+ private void initLOD(int lod) {
+ if (lod < 0) {
+ throw new RSIllegalArgumentException("Attempting to set negative lod (" + lod + ").");
+ }
+
+ int tx = mAlloc.mType.getX();
+ int ty = mAlloc.mType.getY();
+ int tz = mAlloc.mType.getZ();
+
+ for (int ct=0; ct < lod; ct++) {
+ if ((tx==1) && (ty == 1) && (tz == 1)) {
+ throw new RSIllegalArgumentException("Attempting to set lod (" + lod + ") out of range.");
+ }
+
+ if (tx > 1) tx >>= 1;
+ if (ty > 1) ty >>= 1;
+ if (tz > 1) tz >>= 1;
+ }
+
+ mSelectedDimX = tx;
+ mSelectedDimY = ty;
+ mSelectedCount = tx;
+ if (ty > 1) {
+ mSelectedCount *= ty;
+ }
+ if (tz > 1) {
+ mSelectedCount *= tz;
+ }
+ }
+
+ /**
+ * Set the active LOD. The LOD must be within the range for the
+ * type being adapted.
+ *
+ * @param lod The LOD to make active.
+ */
public void setLOD(int lod) {
- mSelectedLOD = lod;
+ if (!mAlloc.getType().hasMipmaps()) {
+ throw new RSInvalidStateException("Cannot set LOD when the allocation type does not include mipmaps.");
+ }
+ if (!mConstrainedLOD) {
+ throw new RSInvalidStateException("Cannot set LOD when the adapter includes mipmaps.");
+ }
+
+ initLOD(lod);
}
public void setFace(Type.CubemapFace cf) {
@@ -245,6 +296,11 @@
static public AllocationAdapter create2D(RenderScript rs, Allocation a) {
rs.validate();
AllocationAdapter aa = new AllocationAdapter(0, rs, a);
+ aa.mConstrainedLOD = true;
+ aa.mConstrainedFace = true;
+ aa.mConstrainedY = false;
+ aa.mConstrainedZ = true;
+ aa.initLOD(0);
return aa;
}
diff --git a/graphics/java/android/renderscript/FieldPacker.java b/graphics/java/android/renderscript/FieldPacker.java
index fac7144..2739a4b8 100644
--- a/graphics/java/android/renderscript/FieldPacker.java
+++ b/graphics/java/android/renderscript/FieldPacker.java
@@ -165,6 +165,22 @@
addF32(v.w);
}
+ public void addF64(Double2 v) {
+ addF64(v.x);
+ addF64(v.y);
+ }
+ public void addF64(Double3 v) {
+ addF64(v.x);
+ addF64(v.y);
+ addF64(v.z);
+ }
+ public void addF64(Double4 v) {
+ addF64(v.x);
+ addF64(v.y);
+ addF64(v.z);
+ addF64(v.w);
+ }
+
public void addI8(Byte2 v) {
addI8(v.x);
addI8(v.y);
@@ -261,6 +277,38 @@
addU32(v.w);
}
+ public void addI64(Long2 v) {
+ addI64(v.x);
+ addI64(v.y);
+ }
+ public void addI64(Long3 v) {
+ addI64(v.x);
+ addI64(v.y);
+ addI64(v.z);
+ }
+ public void addI64(Long4 v) {
+ addI64(v.x);
+ addI64(v.y);
+ addI64(v.z);
+ addI64(v.w);
+ }
+
+ public void addU64(Long2 v) {
+ addU64(v.x);
+ addU64(v.y);
+ }
+ public void addU64(Long3 v) {
+ addU64(v.x);
+ addU64(v.y);
+ addU64(v.z);
+ }
+ public void addU64(Long4 v) {
+ addU64(v.x);
+ addU64(v.y);
+ addU64(v.z);
+ addU64(v.w);
+ }
+
public void addMatrix(Matrix4f v) {
for (int i=0; i < v.mMat.length; i++) {
addF32(v.mMat[i]);
diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp
index f6cefa6..f219639 100644
--- a/libs/gui/tests/SurfaceTexture_test.cpp
+++ b/libs/gui/tests/SurfaceTexture_test.cpp
@@ -173,11 +173,11 @@
}
virtual EGLint getSurfaceWidth() {
- return 64;
+ return 512;
}
virtual EGLint getSurfaceHeight() {
- return 64;
+ return 512;
}
void loadShader(GLenum shaderType, const char* pSource, GLuint* outShader) {
@@ -526,18 +526,19 @@
glClearColor(0.2, 0.2, 0.2, 0.2);
glClear(GL_COLOR_BUFFER_BIT);
+ glViewport(0, 0, texWidth, texHeight);
drawTexture();
EXPECT_TRUE(checkPixel( 0, 0, 255, 127, 255, 255));
EXPECT_TRUE(checkPixel(63, 0, 0, 133, 0, 255));
- EXPECT_TRUE(checkPixel(63, 63, 0, 133, 0, 255));
- EXPECT_TRUE(checkPixel( 0, 63, 255, 127, 255, 255));
+ EXPECT_TRUE(checkPixel(63, 65, 0, 133, 0, 255));
+ EXPECT_TRUE(checkPixel( 0, 65, 255, 127, 255, 255));
- EXPECT_TRUE(checkPixel(22, 44, 247, 70, 255, 255));
- EXPECT_TRUE(checkPixel(45, 52, 209, 32, 235, 255));
- EXPECT_TRUE(checkPixel(52, 51, 100, 255, 73, 255));
+ EXPECT_TRUE(checkPixel(22, 44, 255, 127, 255, 255));
+ EXPECT_TRUE(checkPixel(45, 52, 255, 127, 255, 255));
+ EXPECT_TRUE(checkPixel(52, 51, 98, 255, 73, 255));
EXPECT_TRUE(checkPixel( 7, 31, 155, 0, 118, 255));
- EXPECT_TRUE(checkPixel(31, 9, 148, 71, 110, 255));
+ EXPECT_TRUE(checkPixel(31, 9, 107, 24, 87, 255));
EXPECT_TRUE(checkPixel(29, 35, 255, 127, 255, 255));
EXPECT_TRUE(checkPixel(36, 22, 155, 29, 0, 255));
}
@@ -570,6 +571,7 @@
glClearColor(0.2, 0.2, 0.2, 0.2);
glClear(GL_COLOR_BUFFER_BIT);
+ glViewport(0, 0, texWidth, texHeight);
drawTexture();
EXPECT_TRUE(checkPixel( 0, 0, 0, 133, 0, 255));
@@ -628,6 +630,7 @@
glClearColor(0.2, 0.2, 0.2, 0.2);
glClear(GL_COLOR_BUFFER_BIT);
+ glViewport(0, 0, 64, 64);
drawTexture();
EXPECT_TRUE(checkPixel( 0, 0, 82, 255, 35, 255));
@@ -675,28 +678,29 @@
glClearColor(0.2, 0.2, 0.2, 0.2);
glClear(GL_COLOR_BUFFER_BIT);
+ glViewport(0, 0, texWidth, texHeight);
drawTexture();
EXPECT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35));
EXPECT_TRUE(checkPixel(63, 0, 231, 231, 231, 231));
- EXPECT_TRUE(checkPixel(63, 63, 231, 231, 231, 231));
- EXPECT_TRUE(checkPixel( 0, 63, 35, 35, 35, 35));
+ EXPECT_TRUE(checkPixel(63, 65, 231, 231, 231, 231));
+ EXPECT_TRUE(checkPixel( 0, 65, 35, 35, 35, 35));
EXPECT_TRUE(checkPixel(15, 10, 35, 231, 231, 231));
- EXPECT_TRUE(checkPixel(24, 63, 35, 231, 231, 35));
- EXPECT_TRUE(checkPixel(19, 40, 87, 179, 35, 35));
+ EXPECT_TRUE(checkPixel(24, 63, 38, 228, 231, 35));
+ EXPECT_TRUE(checkPixel(19, 40, 35, 231, 35, 35));
EXPECT_TRUE(checkPixel(38, 30, 231, 35, 35, 35));
EXPECT_TRUE(checkPixel(42, 54, 35, 35, 35, 231));
- EXPECT_TRUE(checkPixel(37, 33, 35, 231, 231, 231));
+ EXPECT_TRUE(checkPixel(37, 33, 228, 38, 38, 38));
EXPECT_TRUE(checkPixel(31, 8, 231, 35, 35, 231));
- EXPECT_TRUE(checkPixel(36, 47, 231, 35, 231, 231));
- EXPECT_TRUE(checkPixel(24, 63, 35, 231, 231, 35));
- EXPECT_TRUE(checkPixel(48, 3, 231, 231, 35, 35));
+ EXPECT_TRUE(checkPixel(36, 47, 228, 35, 231, 231));
+ EXPECT_TRUE(checkPixel(24, 63, 38, 228, 231, 35));
+ EXPECT_TRUE(checkPixel(48, 3, 228, 228, 38, 35));
EXPECT_TRUE(checkPixel(54, 50, 35, 231, 231, 231));
- EXPECT_TRUE(checkPixel(24, 25, 191, 191, 231, 231));
- EXPECT_TRUE(checkPixel(10, 9, 93, 93, 231, 231));
+ EXPECT_TRUE(checkPixel(24, 25, 41, 41, 231, 231));
+ EXPECT_TRUE(checkPixel(10, 9, 38, 38, 231, 231));
EXPECT_TRUE(checkPixel(29, 4, 35, 35, 35, 231));
- EXPECT_TRUE(checkPixel(56, 31, 35, 231, 231, 35));
+ EXPECT_TRUE(checkPixel(56, 31, 38, 228, 231, 35));
EXPECT_TRUE(checkPixel(58, 55, 35, 35, 231, 231));
}
@@ -730,6 +734,7 @@
glClearColor(0.2, 0.2, 0.2, 0.2);
glClear(GL_COLOR_BUFFER_BIT);
+ glViewport(0, 0, texWidth, texHeight);
drawTexture();
EXPECT_TRUE(checkPixel( 0, 0, 231, 231, 231, 231));
@@ -803,6 +808,7 @@
glClearColor(0.2, 0.2, 0.2, 0.2);
glClear(GL_COLOR_BUFFER_BIT);
+ glViewport(0, 0, texWidth, texHeight);
drawTexture();
EXPECT_TRUE(checkPixel( 0, 0, 153, 153, 153, 153));
diff --git a/libs/rs/driver/rsdRuntimeMath.cpp b/libs/rs/driver/rsdRuntimeMath.cpp
index acb990d..d29da7e 100644
--- a/libs/rs/driver/rsdRuntimeMath.cpp
+++ b/libs/rs/driver/rsdRuntimeMath.cpp
@@ -268,6 +268,87 @@
}
+static int32_t SC_AtomicCas(volatile int32_t *ptr, int32_t expectedValue, int32_t newValue) {
+ int32_t prev;
+
+ do {
+ int32_t ret = android_atomic_release_cas(expectedValue, newValue, ptr);
+ if (!ret) {
+ // The android cas return 0 if it wrote the value. This means the
+ // previous value was the expected value and we can return.
+ return expectedValue;
+ }
+ // We didn't write the value and need to load the "previous" value.
+ prev = *ptr;
+
+ // A race condition exists where the expected value could appear after our cas failed
+ // above. In this case loop until we have a legit previous value or the
+ // write passes.
+ } while (prev == expectedValue);
+ return prev;
+}
+
+
+static int32_t SC_AtomicInc(volatile int32_t *ptr) {
+ return android_atomic_inc(ptr);
+}
+
+static int32_t SC_AtomicDec(volatile int32_t *ptr) {
+ return android_atomic_dec(ptr);
+}
+
+static int32_t SC_AtomicAdd(volatile int32_t *ptr, int32_t value) {
+ return android_atomic_add(value, ptr);
+}
+
+static int32_t SC_AtomicSub(volatile int32_t *ptr, int32_t value) {
+ int32_t prev, status;
+ do {
+ prev = *ptr;
+ status = android_atomic_release_cas(prev, prev - value, ptr);
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+
+static int32_t SC_AtomicAnd(volatile int32_t *ptr, int32_t value) {
+ return android_atomic_and(value, ptr);
+}
+
+static int32_t SC_AtomicOr(volatile int32_t *ptr, int32_t value) {
+ return android_atomic_or(value, ptr);
+}
+
+static int32_t SC_AtomicXor(volatile int32_t *ptr, int32_t value) {
+ int32_t prev, status;
+ do {
+ prev = *ptr;
+ status = android_atomic_release_cas(prev, prev ^ value, ptr);
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+
+static int32_t SC_AtomicMin(volatile int32_t *ptr, int32_t value) {
+ int32_t prev, status;
+ do {
+ prev = *ptr;
+ int32_t n = rsMin(value, prev);
+ status = android_atomic_release_cas(prev, n, ptr);
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+
+static int32_t SC_AtomicMax(volatile int32_t *ptr, int32_t value) {
+ int32_t prev, status;
+ do {
+ prev = *ptr;
+ int32_t n = rsMax(value, prev);
+ status = android_atomic_release_cas(prev, n, ptr);
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+
+
+
//////////////////////////////////////////////////////////////////////////////
// Class implementation
//////////////////////////////////////////////////////////////////////////////
@@ -425,6 +506,28 @@
{ "_Z6rsRandff", (void *)&SC_randf2, true },
{ "_Z6rsFracf", (void *)&SC_frac, true },
+ // Atomics
+ { "_Z11rsAtomicIncPVi", (void *)&SC_AtomicInc, true },
+ { "_Z11rsAtomicIncPVj", (void *)&SC_AtomicInc, true },
+ { "_Z11rsAtomicDecPVi", (void *)&SC_AtomicDec, true },
+ { "_Z11rsAtomicDecPVj", (void *)&SC_AtomicDec, true },
+ { "_Z11rsAtomicAddPVii", (void *)&SC_AtomicAdd, true },
+ { "_Z11rsAtomicAddPVjj", (void *)&SC_AtomicAdd, true },
+ { "_Z11rsAtomicSubPVii", (void *)&SC_AtomicSub, true },
+ { "_Z11rsAtomicSubPVjj", (void *)&SC_AtomicSub, true },
+ { "_Z11rsAtomicAndPVii", (void *)&SC_AtomicAnd, true },
+ { "_Z11rsAtomicAndPVjj", (void *)&SC_AtomicAnd, true },
+ { "_Z10rsAtomicOrPVii", (void *)&SC_AtomicOr, true },
+ { "_Z10rsAtomicOrPVjj", (void *)&SC_AtomicOr, true },
+ { "_Z11rsAtomicXorPVii", (void *)&SC_AtomicXor, true },
+ { "_Z11rsAtomicXorPVjj", (void *)&SC_AtomicXor, true },
+ { "_Z11rsAtomicMinPVii", (void *)&SC_AtomicMin, true },
+ { "_Z11rsAtomicMinPVjj", (void *)&SC_AtomicMin, true },
+ { "_Z11rsAtomicMaxPVii", (void *)&SC_AtomicMax, true },
+ { "_Z11rsAtomicMaxPVjj", (void *)&SC_AtomicMax, true },
+ { "_Z11rsAtomicCasPViii", (void *)&SC_AtomicCas, true },
+ { "_Z11rsAtomicCasPVjjj", (void *)&SC_AtomicCas, true },
+
{ NULL, NULL, false }
};
diff --git a/libs/rs/scriptc/rs_math.rsh b/libs/rs/scriptc/rs_math.rsh
index 584317e..f38f72c 100644
--- a/libs/rs/scriptc/rs_math.rsh
+++ b/libs/rs/scriptc/rs_math.rsh
@@ -258,4 +258,226 @@
rs_allocation output, const void * usrData,
const rs_script_call_t *);
+
+
+/**
+ * Atomic add one to the value at addr.
+ * Equal to rsAtomicAdd(addr, 1)
+ *
+ * @param addr Address of value to increment
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+ rsAtomicInc(volatile int32_t* addr);
+/**
+ * Atomic add one to the value at addr.
+ * Equal to rsAtomicAdd(addr, 1)
+ *
+ * @param addr Address of value to increment
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+ rsAtomicInc(volatile uint32_t* addr);
+
+/**
+ * Atomic subtract one from the value at addr. Equal to rsAtomicSub(addr, 1)
+ *
+ * @param addr Address of value to decrement
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+ rsAtomicDec(volatile int32_t* addr);
+/**
+ * Atomic subtract one from the value at addr. Equal to rsAtomicSub(addr, 1)
+ *
+ * @param addr Address of value to decrement
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+ rsAtomicDec(volatile uint32_t* addr);
+
+/**
+ * Atomic add a value to the value at addr. addr[0] += value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to add to the value at addr
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+ rsAtomicAdd(volatile int32_t* addr, int32_t value);
+/**
+ * Atomic add a value to the value at addr. addr[0] += value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to add to the value at addr
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+ rsAtomicAdd(volatile uint32_t* addr, uint32_t value);
+
+/**
+ * Atomic Subtract a value from the value at addr. addr[0] -= value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to subtract from the value at addr
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+ rsAtomicSub(volatile int32_t* addr, int32_t value);
+/**
+ * Atomic Subtract a value from the value at addr. addr[0] -= value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to subtract from the value at addr
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+ rsAtomicSub(volatile uint32_t* addr, uint32_t value);
+
+/**
+ * Atomic Bitwise and a value from the value at addr. addr[0] &= value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to and with the value at addr
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+ rsAtomicAnd(volatile int32_t* addr, int32_t value);
+/**
+ * Atomic Bitwise and a value from the value at addr. addr[0] &= value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to and with the value at addr
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+ rsAtomicAnd(volatile uint32_t* addr, uint32_t value);
+
+/**
+ * Atomic Bitwise or a value from the value at addr. addr[0] |= value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to or with the value at addr
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+ rsAtomicOr(volatile int32_t* addr, int32_t value);
+/**
+ * Atomic Bitwise or a value from the value at addr. addr[0] |= value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to or with the value at addr
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+ rsAtomicOr(volatile uint32_t* addr, uint32_t value);
+
+/**
+ * Atomic Bitwise xor a value from the value at addr. addr[0] ^= value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to xor with the value at addr
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+ rsAtomicXor(volatile uint32_t* addr, uint32_t value);
+/**
+ * Atomic Bitwise xor a value from the value at addr. addr[0] ^= value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to xor with the value at addr
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+ rsAtomicXor(volatile int32_t* addr, int32_t value);
+
+/**
+ * Atomic Set the value at addr to the min of addr and value
+ * addr[0] = rsMin(addr[0], value)
+ *
+ * @param addr Address of value to modify
+ * @param value comparison value
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+ rsAtomicMin(volatile uint32_t* addr, uint32_t value);
+/**
+ * Atomic Set the value at addr to the min of addr and value
+ * addr[0] = rsMin(addr[0], value)
+ *
+ * @param addr Address of value to modify
+ * @param value comparison value
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+ rsAtomicMin(volatile int32_t* addr, int32_t value);
+
+/**
+ * Atomic Set the value at addr to the max of addr and value
+ * addr[0] = rsMax(addr[0], value)
+ *
+ * @param addr Address of value to modify
+ * @param value comparison value
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+ rsAtomicMax(volatile uint32_t* addr, uint32_t value);
+/**
+ * Atomic Set the value at addr to the max of addr and value
+ * addr[0] = rsMin(addr[0], value)
+ *
+ * @param addr Address of value to modify
+ * @param value comparison value
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+ rsAtomicMax(volatile int32_t* addr, int32_t value);
+
+/**
+ * Compare-and-set operation with a full memory barrier.
+ *
+ * If the value at addr matches compareValue then newValue is written.
+ *
+ * @param addr The address to compare and replace if the compare passes.
+ * @param compareValue The value to test addr[0] against.
+ * @param newValue The value to write if the test passes.
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+ rsAtomicCas(volatile int32_t* addr, int32_t compareValue, int32_t newValue);
+
+/**
+ * Compare-and-set operation with a full memory barrier.
+ *
+ * If the value at addr matches compareValue then newValue is written.
+ *
+ * @param addr The address to compare and replace if the compare passes.
+ * @param compareValue The value to test addr[0] against.
+ * @param newValue The value to write if the test passes.
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+ rsAtomicCas(volatile uint32_t* addr, int32_t compareValue, int32_t newValue);
+
+
#endif
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 58f6699..ea9911c 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -47,10 +47,6 @@
static const uint8_t kNalUnitTypePicParamSet = 0x08;
static const int64_t kInitialDelayTimeUs = 700000LL;
-// Using longer adjustment period to suppress fluctuations in
-// the audio encoding paths
-static const int64_t kVideoMediaTimeAdjustPeriodTimeUs = 600000000LL; // 10 minutes
-
class MPEG4Writer::Track {
public:
Track(MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId);
@@ -88,8 +84,6 @@
int64_t mTrackDurationUs;
int64_t mMaxChunkDurationUs;
- // For realtime applications, we need to adjust the media clock
- // for video track based on the audio media clock
bool mIsRealTimeRecording;
int64_t mMaxTimeStampUs;
int64_t mEstimatedTrackSizeBytes;
@@ -175,28 +169,9 @@
int64_t mPreviousTrackTimeUs;
int64_t mTrackEveryTimeDurationUs;
- // Has the media time adjustment for video started?
- bool mIsMediaTimeAdjustmentOn;
- // The time stamp when previous media time adjustment period starts
- int64_t mPrevMediaTimeAdjustTimestampUs;
- // Number of vidoe frames whose time stamp may be adjusted
- int64_t mMediaTimeAdjustNumFrames;
- // The sample number when previous meida time adjustmnet period starts
- int64_t mPrevMediaTimeAdjustSample;
- // The total accumulated drift time within a period of
- // kVideoMediaTimeAdjustPeriodTimeUs.
- int64_t mTotalDriftTimeToAdjustUs;
- // The total accumalated drift time since the start of the recording
- // excluding the current time adjustment period
- int64_t mPrevTotalAccumDriftTimeUs;
-
// Update the audio track's drift information.
void updateDriftTime(const sp<MetaData>& meta);
- // Adjust the time stamp of the video track according to
- // the drift time information from the audio track.
- void adjustMediaTime(int64_t *timestampUs);
-
static void *ThreadWrapper(void *me);
status_t threadEntry();
@@ -1512,12 +1487,7 @@
mNumSttsTableEntries = 0;
mNumCttsTableEntries = 0;
mMdatSizeBytes = 0;
- mIsMediaTimeAdjustmentOn = false;
- mPrevMediaTimeAdjustTimestampUs = 0;
- mMediaTimeAdjustNumFrames = 0;
- mPrevMediaTimeAdjustSample = 0;
- mTotalDriftTimeToAdjustUs = 0;
- mPrevTotalAccumDriftTimeUs = 0;
+
mMaxChunkDurationUs = 0;
mHasNegativeCttsDeltaDuration = false;
@@ -1816,128 +1786,6 @@
}
/*
-* The video track's media time adjustment for real-time applications
-* is described as follows:
-*
-* First, the media time adjustment is done for every period of
-* kVideoMediaTimeAdjustPeriodTimeUs. kVideoMediaTimeAdjustPeriodTimeUs
-* is currently a fixed value chosen heuristically. The value of
-* kVideoMediaTimeAdjustPeriodTimeUs should not be very large or very small
-* for two considerations: on one hand, a relatively large value
-* helps reduce large fluctuation of drift time in the audio encoding
-* path; while on the other hand, a relatively small value helps keep
-* restoring synchronization in audio/video more frequently. Note for the
-* very first period of kVideoMediaTimeAdjustPeriodTimeUs, there is
-* no media time adjustment for the video track.
-*
-* Second, the total accumulated audio track time drift found
-* in a period of kVideoMediaTimeAdjustPeriodTimeUs is distributed
-* over a stream of incoming video frames. The number of video frames
-* affected is determined based on the number of recorded video frames
-* within the past kVideoMediaTimeAdjustPeriodTimeUs period.
-* We choose to distribute the drift time over only a portion
-* (rather than all) of the total number of recorded video frames
-* in order to make sure that the video track media time adjustment is
-* completed for the current period before the next video track media
-* time adjustment period starts. Currently, the portion chosen is a
-* half (0.5).
-*
-* Last, various additional checks are performed to ensure that
-* the actual audio encoding path does not have too much drift.
-* In particular, 1) we want to limit the average incremental time
-* adjustment for each video frame to be less than a threshold
-* for a single period of kVideoMediaTimeAdjustPeriodTimeUs.
-* Currently, the threshold is set to 5 ms. If the average incremental
-* media time adjustment for a video frame is larger than the
-* threshold, the audio encoding path has too much time drift.
-* 2) We also want to limit the total time drift in the audio
-* encoding path to be less than a threshold for a period of
-* kVideoMediaTimeAdjustPeriodTimeUs. Currently, the threshold
-* is 0.5% of kVideoMediaTimeAdjustPeriodTimeUs. If the time drift of
-* the audio encoding path is larger than the threshold, the audio
-* encoding path has too much time drift. We treat the large time
-* drift of the audio encoding path as errors, since there is no
-* way to keep audio/video in synchronization for real-time
-* applications if the time drift is too large unless we drop some
-* video frames, which has its own problems that we don't want
-* to get into for the time being.
-*/
-void MPEG4Writer::Track::adjustMediaTime(int64_t *timestampUs) {
- if (*timestampUs - mPrevMediaTimeAdjustTimestampUs >=
- kVideoMediaTimeAdjustPeriodTimeUs) {
-
- LOGV("New media time adjustment period at %lld us", *timestampUs);
- mIsMediaTimeAdjustmentOn = true;
- mMediaTimeAdjustNumFrames =
- (mNumSamples - mPrevMediaTimeAdjustSample) >> 1;
-
- mPrevMediaTimeAdjustTimestampUs = *timestampUs;
- mPrevMediaTimeAdjustSample = mNumSamples;
- int64_t totalAccumDriftTimeUs = mOwner->getDriftTimeUs();
- mTotalDriftTimeToAdjustUs =
- totalAccumDriftTimeUs - mPrevTotalAccumDriftTimeUs;
-
- mPrevTotalAccumDriftTimeUs = totalAccumDriftTimeUs;
-
- // Check on incremental adjusted time per frame
- int64_t adjustTimePerFrameUs =
- mTotalDriftTimeToAdjustUs / mMediaTimeAdjustNumFrames;
-
- if (adjustTimePerFrameUs < 0) {
- adjustTimePerFrameUs = -adjustTimePerFrameUs;
- }
- if (adjustTimePerFrameUs >= 5000) {
- LOGE("Adjusted time per video frame is %lld us",
- adjustTimePerFrameUs);
- CHECK(!"Video frame time adjustment is too large!");
- }
-
- // Check on total accumulated time drift within a period of
- // kVideoMediaTimeAdjustPeriodTimeUs.
- int64_t driftPercentage = (mTotalDriftTimeToAdjustUs * 1000)
- / kVideoMediaTimeAdjustPeriodTimeUs;
-
- if (driftPercentage < 0) {
- driftPercentage = -driftPercentage;
- }
- if (driftPercentage > 5) {
- LOGE("Audio track has time drift %lld us over %lld us",
- mTotalDriftTimeToAdjustUs,
- kVideoMediaTimeAdjustPeriodTimeUs);
-
- CHECK(!"The audio track media time drifts too much!");
- }
-
- }
-
- if (mIsMediaTimeAdjustmentOn) {
- if (mNumSamples - mPrevMediaTimeAdjustSample <=
- mMediaTimeAdjustNumFrames) {
-
- // Do media time incremental adjustment
- int64_t incrementalAdjustTimeUs =
- (mTotalDriftTimeToAdjustUs *
- (mNumSamples - mPrevMediaTimeAdjustSample))
- / mMediaTimeAdjustNumFrames;
-
- *timestampUs +=
- (incrementalAdjustTimeUs + mPrevTotalAccumDriftTimeUs);
-
- LOGV("Incremental video frame media time adjustment: %lld us",
- (incrementalAdjustTimeUs + mPrevTotalAccumDriftTimeUs));
- } else {
- // Within the remaining adjustment period,
- // no incremental adjustment is needed.
- *timestampUs +=
- (mTotalDriftTimeToAdjustUs + mPrevTotalAccumDriftTimeUs);
-
- LOGV("Fixed video frame media time adjustment: %lld us",
- (mTotalDriftTimeToAdjustUs + mPrevTotalAccumDriftTimeUs));
- }
- }
-}
-
-/*
* Updates the drift time from the audio track so that
* the video track can get the updated drift time information
* from the file writer. The fluctuation of the drift time of the audio
@@ -2080,32 +1928,6 @@
int32_t isSync = false;
meta_data->findInt32(kKeyIsSyncFrame, &isSync);
-
- /*
- * The original timestamp found in the data buffer will be modified as below:
- *
- * There is a playback offset into this track if the track's start time
- * is not the same as the movie start time, which will be recorded in edst
- * box of the output file. The playback offset is to make sure that the
- * starting time of the audio/video tracks are synchronized. Although the
- * track's media timestamp may be subject to various modifications
- * as outlined below, the track's playback offset time remains unchanged
- * once the first data buffer of the track is received.
- *
- * The media time stamp will be calculated by subtracting the playback offset
- * (and potential pause durations) from the original timestamp in the buffer.
- *
- * If this track is a video track for a real-time recording application with
- * both audio and video tracks, its media timestamp will subject to further
- * modification based on the media clock of the audio track. This modification
- * is needed for the purpose of maintaining good audio/video synchronization.
- *
- * If the recording session is paused and resumed multiple times, the track
- * media timestamp will be modified as if the recording session had never been
- * paused at all during playback of the recorded output file. In other words,
- * the output file will have no memory of pause/resume durations.
- *
- */
CHECK(meta_data->findInt64(kKeyTime, ×tampUs));
////////////////////////////////////////////////////////////////////////////////
@@ -2146,31 +1968,13 @@
timestampUs, cttsDeltaTimeUs);
}
- // Media time adjustment for real-time applications
if (mIsRealTimeRecording) {
if (mIsAudio) {
updateDriftTime(meta_data);
- } else {
- adjustMediaTime(×tampUs);
}
}
CHECK(timestampUs >= 0);
- if (mNumSamples > 1) {
- if (timestampUs <= lastTimestampUs) {
- LOGW("Frame arrives too late!");
- // Don't drop the late frame, since dropping a frame may cause
- // problems later during playback
-
- // The idea here is to avoid having two or more samples with the
- // same timestamp in the output file.
- if (mTimeScale >= 1000000LL) {
- timestampUs = lastTimestampUs + 1;
- } else {
- timestampUs = lastTimestampUs + (1000000LL + (mTimeScale >> 1)) / mTimeScale;
- }
- }
- }
LOGV("%s media time stamp: %lld and previous paused duration %lld",
mIsAudio? "Audio": "Video", timestampUs, previousPausedDurationUs);
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index eb135ab..a8a094e 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -416,12 +416,16 @@
uint32_t delta = mTimeToSample[2 * i + 1];
for (uint32_t j = 0; j < n; ++j) {
- CHECK(sampleIndex < mNumSampleSizes);
+ if (sampleIndex < mNumSampleSizes) {
+ // Technically this should always be the case if the file
+ // is well-formed, but you know... there's (gasp) malformed
+ // content out there.
- mSampleTimeEntries[sampleIndex].mSampleIndex = sampleIndex;
+ mSampleTimeEntries[sampleIndex].mSampleIndex = sampleIndex;
- mSampleTimeEntries[sampleIndex].mCompositionTime =
- sampleTime + getCompositionTimeOffset(sampleIndex);
+ mSampleTimeEntries[sampleIndex].mCompositionTime =
+ sampleTime + getCompositionTimeOffset(sampleIndex);
+ }
++sampleIndex;
sampleTime += delta;
diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
index e9ce719..7e83163 100644
--- a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
+++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
@@ -117,11 +117,27 @@
addPort(def);
}
+static int GetCPUCoreCount() {
+ int cpuCoreCount = 1;
+#if defined(_SC_NPROCESSORS_ONLN)
+ cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
+#else
+ // _SC_NPROC_ONLN must be defined...
+ cpuCoreCount = sysconf(_SC_NPROC_ONLN);
+#endif
+ CHECK(cpuCoreCount >= 1);
+ LOGV("Number of CPU cores: %d", cpuCoreCount);
+ return cpuCoreCount;
+}
+
status_t SoftVPX::initDecoder() {
mCtx = new vpx_codec_ctx_t;
vpx_codec_err_t vpx_err;
+ vpx_codec_dec_cfg_t cfg;
+ memset(&cfg, 0, sizeof(vpx_codec_dec_cfg_t));
+ cfg.threads = GetCPUCoreCount();
if ((vpx_err = vpx_codec_dec_init(
- (vpx_codec_ctx_t *)mCtx, &vpx_codec_vp8_dx_algo, NULL, 0))) {
+ (vpx_codec_ctx_t *)mCtx, &vpx_codec_vp8_dx_algo, &cfg, 0))) {
LOGE("on2 decoder failed to initialize. (%d)", vpx_err);
return UNKNOWN_ERROR;
}
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 012d9ad..165683e 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -296,6 +296,8 @@
new M3UParser(url, buffer->data(), buffer->size());
if (playlist->initCheck() != OK) {
+ LOGE("failed to parse .m3u8 playlist");
+
return NULL;
}
diff --git a/media/libstagefright/httplive/M3UParser.cpp b/media/libstagefright/httplive/M3UParser.cpp
index 2eb180a..765f795 100644
--- a/media/libstagefright/httplive/M3UParser.cpp
+++ b/media/libstagefright/httplive/M3UParser.cpp
@@ -179,7 +179,7 @@
if (mIsVariantPlaylist) {
return ERROR_MALFORMED;
}
- err = parseMetaData(line, &itemMeta, "duration");
+ err = parseMetaDataDuration(line, &itemMeta, "durationUs");
} else if (line.startsWith("#EXT-X-DISCONTINUITY")) {
if (mIsVariantPlaylist) {
return ERROR_MALFORMED;
@@ -203,9 +203,9 @@
if (!line.startsWith("#")) {
if (!mIsVariantPlaylist) {
- int32_t durationSecs;
+ int64_t durationUs;
if (itemMeta == NULL
- || !itemMeta->findInt32("duration", &durationSecs)) {
+ || !itemMeta->findInt64("durationUs", &durationUs)) {
return ERROR_MALFORMED;
}
}
@@ -252,6 +252,30 @@
}
// static
+status_t M3UParser::parseMetaDataDuration(
+ const AString &line, sp<AMessage> *meta, const char *key) {
+ ssize_t colonPos = line.find(":");
+
+ if (colonPos < 0) {
+ return ERROR_MALFORMED;
+ }
+
+ double x;
+ status_t err = ParseDouble(line.c_str() + colonPos + 1, &x);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (meta->get() == NULL) {
+ *meta = new AMessage;
+ }
+ (*meta)->setInt64(key, (int64_t)x * 1E6);
+
+ return OK;
+}
+
+// static
status_t M3UParser::parseStreamInf(
const AString &line, sp<AMessage> *meta) {
ssize_t colonPos = line.find(":");
@@ -412,4 +436,18 @@
return OK;
}
+// static
+status_t M3UParser::ParseDouble(const char *s, double *x) {
+ char *end;
+ double dval = strtod(s, &end);
+
+ if (end == s || (*end != '\0' && *end != ',')) {
+ return ERROR_MALFORMED;
+ }
+
+ *x = dval;
+
+ return OK;
+}
+
} // namespace android
diff --git a/media/libstagefright/include/M3UParser.h b/media/libstagefright/include/M3UParser.h
index 63895b4..478582d 100644
--- a/media/libstagefright/include/M3UParser.h
+++ b/media/libstagefright/include/M3UParser.h
@@ -63,6 +63,9 @@
static status_t parseMetaData(
const AString &line, sp<AMessage> *meta, const char *key);
+ static status_t parseMetaDataDuration(
+ const AString &line, sp<AMessage> *meta, const char *key);
+
static status_t parseStreamInf(
const AString &line, sp<AMessage> *meta);
@@ -70,6 +73,7 @@
const AString &line, sp<AMessage> *meta, const AString &baseURI);
static status_t ParseInt32(const char *s, int32_t *x);
+ static status_t ParseDouble(const char *s, double *x);
DISALLOW_EVIL_CONSTRUCTORS(M3UParser);
};
diff --git a/media/libstagefright/omx/OMXMaster.cpp b/media/libstagefright/omx/OMXMaster.cpp
index 545e6d4..504d470 100644
--- a/media/libstagefright/omx/OMXMaster.cpp
+++ b/media/libstagefright/omx/OMXMaster.cpp
@@ -86,7 +86,11 @@
mPluginByComponentName.add(name8, plugin);
}
- CHECK_EQ(err, OMX_ErrorNoMore);
+
+ if (err != OMX_ErrorNoMore) {
+ LOGE("OMX plugin failed w/ error 0x%08x after registering %d "
+ "components", err, mPluginByComponentName.size());
+ }
}
void OMXMaster::clearPlugins() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 0b82123..f3c2623 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1721,7 +1721,7 @@
|| Intent.ACTION_SCREEN_OFF.equals(action)) {
boolean excludeRecents = false;
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
- String reason = intent.getExtras().getString("reason");
+ String reason = intent.getStringExtra("reason");
if (reason != null) {
excludeRecents = reason.equals("recentapps");
}
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index fbde9d1..8037d7a 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -35,6 +35,8 @@
import android.app.ActivityManagerNative;
import android.app.AlertDialog;
+import android.app.Notification;
+import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -52,6 +54,7 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.ContentObserver;
+import android.graphics.BitmapFactory;
import android.inputmethodservice.InputMethodService;
import android.os.Binder;
import android.os.Environment;
@@ -154,6 +157,13 @@
private final LruCache<SuggestionSpan, InputMethodInfo> mSecureSuggestionSpans =
new LruCache<SuggestionSpan, InputMethodInfo>(SECURE_SUGGESTION_SPANS_MAX_SIZE);
+ // Ongoing notification
+ private final NotificationManager mNotificationManager;
+ private final Notification mImeSwitcherNotification;
+ private final PendingIntent mImeSwitchPendingIntent;
+ private final boolean mShowOngoingImeSwitcherForPhones;
+ private boolean mNotificationShown;
+
class SessionState {
final ClientState client;
final IInputMethod method;
@@ -508,6 +518,25 @@
handleMessage(msg);
}
});
+
+ mNotificationManager = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ mImeSwitcherNotification = new Notification();
+ mImeSwitcherNotification.icon = com.android.internal.R.drawable.ic_notification_ime_default;
+ mImeSwitcherNotification.when = 0;
+ mImeSwitcherNotification.flags = Notification.FLAG_ONGOING_EVENT;
+ mImeSwitcherNotification.tickerText = null;
+ mImeSwitcherNotification.defaults = 0; // please be quiet
+ mImeSwitcherNotification.sound = null;
+ mImeSwitcherNotification.vibrate = null;
+ Intent intent = new Intent(Settings.ACTION_SHOW_INPUT_METHOD_PICKER);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ mImeSwitchPendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
+ mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
+ com.android.internal.R.bool.show_ongoing_ime_switcher);
+
synchronized (mMethodMap) {
mFileManager = new InputMethodFileManager(mMethodMap);
}
@@ -522,6 +551,7 @@
mStatusBar = statusBar;
statusBar.setIconVisibility("ime", false);
+ mNotificationShown = false;
// mSettings should be created before buildInputMethodListLocked
mSettings = new InputMethodSettings(
@@ -1022,6 +1052,32 @@
}
}
+ private boolean needsToShowImeSwitchOngoingNotification() {
+ if (!mShowOngoingImeSwitcherForPhones) return false;
+ synchronized (mMethodMap) {
+ List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
+ final int N = imis.size();
+ int count = 0;
+ for(int i = 0; i < N; ++i) {
+ final InputMethodInfo imi = imis.get(i);
+ final List<InputMethodSubtype> subtypes = getEnabledInputMethodSubtypeListLocked(
+ imi, true);
+ final int subtypeCount = subtypes.size();
+ if (subtypeCount == 0) {
+ ++count;
+ } else {
+ for (int j = 0; j < subtypeCount; ++j) {
+ if (!subtypes.get(j).isAuxiliary()) {
+ ++count;
+ }
+ }
+ }
+ if (count > 1) return true;
+ }
+ }
+ return false;
+ }
+
@Override
public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
int uid = Binder.getCallingUid();
@@ -1036,6 +1092,25 @@
mImeWindowVis = vis;
mBackDisposition = backDisposition;
mStatusBar.setImeWindowStatus(token, vis, backDisposition);
+ final boolean iconVisibility = (vis & InputMethodService.IME_ACTIVE) != 0;
+ if (iconVisibility && needsToShowImeSwitchOngoingNotification()) {
+ final PackageManager pm = mContext.getPackageManager();
+ final CharSequence label = mMethodMap.get(mCurMethodId).loadLabel(pm);
+ final CharSequence title = mRes.getText(
+ com.android.internal.R.string.select_input_method);
+ mImeSwitcherNotification.setLatestEventInfo(
+ mContext, title, label, mImeSwitchPendingIntent);
+ mNotificationManager.notify(
+ com.android.internal.R.string.select_input_method,
+ mImeSwitcherNotification);
+ mNotificationShown = true;
+ } else {
+ if (mNotificationShown) {
+ mNotificationManager.cancel(
+ com.android.internal.R.string.select_input_method);
+ mNotificationShown = false;
+ }
+ }
}
} finally {
Binder.restoreCallingIdentity(ident);
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java
index 541bf22..e77998e 100644
--- a/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2008-2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -65,6 +65,7 @@
unitTests = new ArrayList<UnitTest>();
unitTests.add(new UT_primitives(this, mRes, mCtx));
+ unitTests.add(new UT_vector(this, mRes, mCtx));
unitTests.add(new UT_rsdebug(this, mRes, mCtx));
unitTests.add(new UT_rstime(this, mRes, mCtx));
unitTests.add(new UT_rstypes(this, mRes, mCtx));
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_vector.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_vector.java
new file mode 100644
index 0000000..748701d
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_vector.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rs.test;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.renderscript.*;
+
+public class UT_vector extends UnitTest {
+ private Resources mRes;
+
+ protected UT_vector(RSTestCore rstc, Resources res, Context ctx) {
+ super(rstc, "Vector", ctx);
+ mRes = res;
+ }
+
+ private boolean initializeGlobals(ScriptC_vector s) {
+ Float2 F2 = s.get_f2();
+ if (F2.x != 1.0f || F2.y != 2.0f) {
+ return false;
+ }
+ F2.x = 2.99f;
+ F2.y = 3.99f;
+ s.set_f2(F2);
+
+ Float3 F3 = s.get_f3();
+ if (F3.x != 1.0f || F3.y != 2.0f || F3.z != 3.0f) {
+ return false;
+ }
+ F3.x = 2.99f;
+ F3.y = 3.99f;
+ F3.z = 4.99f;
+ s.set_f3(F3);
+
+ Float4 F4 = s.get_f4();
+ if (F4.x != 1.0f || F4.y != 2.0f || F4.z != 3.0f || F4.w != 4.0f) {
+ return false;
+ }
+ F4.x = 2.99f;
+ F4.y = 3.99f;
+ F4.z = 4.99f;
+ F4.w = 5.99f;
+ s.set_f4(F4);
+
+ Double2 D2 = s.get_d2();
+ if (D2.x != 1.0 || D2.y != 2.0) {
+ return false;
+ }
+ D2.x = 2.99;
+ D2.y = 3.99;
+ s.set_d2(D2);
+
+ Double3 D3 = s.get_d3();
+ if (D3.x != 1.0 || D3.y != 2.0 || D3.z != 3.0) {
+ return false;
+ }
+ D3.x = 2.99;
+ D3.y = 3.99;
+ D3.z = 4.99;
+ s.set_d3(D3);
+
+ Double4 D4 = s.get_d4();
+ if (D4.x != 1.0 || D4.y != 2.0 || D4.z != 3.0 || D4.w != 4.0) {
+ return false;
+ }
+ D4.x = 2.99;
+ D4.y = 3.99;
+ D4.z = 4.99;
+ D4.w = 5.99;
+ s.set_d4(D4);
+
+ Byte2 B2 = s.get_i8_2();
+ if (B2.x != 1 || B2.y != 2) {
+ return false;
+ }
+ B2.x = 2;
+ B2.y = 3;
+ s.set_i8_2(B2);
+
+ Byte3 B3 = s.get_i8_3();
+ if (B3.x != 1 || B3.y != 2 || B3.z != 3) {
+ return false;
+ }
+ B3.x = 2;
+ B3.y = 3;
+ B3.z = 4;
+ s.set_i8_3(B3);
+
+ Byte4 B4 = s.get_i8_4();
+ if (B4.x != 1 || B4.y != 2 || B4.z != 3 || B4.w != 4) {
+ return false;
+ }
+ B4.x = 2;
+ B4.y = 3;
+ B4.z = 4;
+ B4.w = 5;
+ s.set_i8_4(B4);
+
+ Short2 S2 = s.get_u8_2();
+ if (S2.x != 1 || S2.y != 2) {
+ return false;
+ }
+ S2.x = 2;
+ S2.y = 3;
+ s.set_u8_2(S2);
+
+ Short3 S3 = s.get_u8_3();
+ if (S3.x != 1 || S3.y != 2 || S3.z != 3) {
+ return false;
+ }
+ S3.x = 2;
+ S3.y = 3;
+ S3.z = 4;
+ s.set_u8_3(S3);
+
+ Short4 S4 = s.get_u8_4();
+ if (S4.x != 1 || S4.y != 2 || S4.z != 3 || S4.w != 4) {
+ return false;
+ }
+ S4.x = 2;
+ S4.y = 3;
+ S4.z = 4;
+ S4.w = 5;
+ s.set_u8_4(S4);
+
+ S2 = s.get_i16_2();
+ if (S2.x != 1 || S2.y != 2) {
+ return false;
+ }
+ S2.x = 2;
+ S2.y = 3;
+ s.set_i16_2(S2);
+
+ S3 = s.get_i16_3();
+ if (S3.x != 1 || S3.y != 2 || S3.z != 3) {
+ return false;
+ }
+ S3.x = 2;
+ S3.y = 3;
+ S3.z = 4;
+ s.set_i16_3(S3);
+
+ S4 = s.get_i16_4();
+ if (S4.x != 1 || S4.y != 2 || S4.z != 3 || S4.w != 4) {
+ return false;
+ }
+ S4.x = 2;
+ S4.y = 3;
+ S4.z = 4;
+ S4.w = 5;
+ s.set_i16_4(S4);
+
+ Int2 I2 = s.get_u16_2();
+ if (I2.x != 1 || I2.y != 2) {
+ return false;
+ }
+ I2.x = 2;
+ I2.y = 3;
+ s.set_u16_2(I2);
+
+ Int3 I3 = s.get_u16_3();
+ if (I3.x != 1 || I3.y != 2 || I3.z != 3) {
+ return false;
+ }
+ I3.x = 2;
+ I3.y = 3;
+ I3.z = 4;
+ s.set_u16_3(I3);
+
+ Int4 I4 = s.get_u16_4();
+ if (I4.x != 1 || I4.y != 2 || I4.z != 3 || I4.w != 4) {
+ return false;
+ }
+ I4.x = 2;
+ I4.y = 3;
+ I4.z = 4;
+ I4.w = 5;
+ s.set_u16_4(I4);
+
+ I2 = s.get_i32_2();
+ if (I2.x != 1 || I2.y != 2) {
+ return false;
+ }
+ I2.x = 2;
+ I2.y = 3;
+ s.set_i32_2(I2);
+
+ I3 = s.get_i32_3();
+ if (I3.x != 1 || I3.y != 2 || I3.z != 3) {
+ return false;
+ }
+ I3.x = 2;
+ I3.y = 3;
+ I3.z = 4;
+ s.set_i32_3(I3);
+
+ I4 = s.get_i32_4();
+ if (I4.x != 1 || I4.y != 2 || I4.z != 3 || I4.w != 4) {
+ return false;
+ }
+ I4.x = 2;
+ I4.y = 3;
+ I4.z = 4;
+ I4.w = 5;
+ s.set_i32_4(I4);
+
+ Long2 L2 = s.get_u32_2();
+ if (L2.x != 1 || L2.y != 2) {
+ return false;
+ }
+ L2.x = 2;
+ L2.y = 3;
+ s.set_u32_2(L2);
+
+ Long3 L3 = s.get_u32_3();
+ if (L3.x != 1 || L3.y != 2 || L3.z != 3) {
+ return false;
+ }
+ L3.x = 2;
+ L3.y = 3;
+ L3.z = 4;
+ s.set_u32_3(L3);
+
+ Long4 L4 = s.get_u32_4();
+ if (L4.x != 1 || L4.y != 2 || L4.z != 3 || L4.w != 4) {
+ return false;
+ }
+ L4.x = 2;
+ L4.y = 3;
+ L4.z = 4;
+ L4.w = 5;
+ s.set_u32_4(L4);
+
+ L2 = s.get_i64_2();
+ if (L2.x != 1 || L2.y != 2) {
+ return false;
+ }
+ L2.x = 2;
+ L2.y = 3;
+ s.set_i64_2(L2);
+
+ L3 = s.get_i64_3();
+ if (L3.x != 1 || L3.y != 2 || L3.z != 3) {
+ return false;
+ }
+ L3.x = 2;
+ L3.y = 3;
+ L3.z = 4;
+ s.set_i64_3(L3);
+
+ L4 = s.get_i64_4();
+ if (L4.x != 1 || L4.y != 2 || L4.z != 3 || L4.w != 4) {
+ return false;
+ }
+ L4.x = 2;
+ L4.y = 3;
+ L4.z = 4;
+ L4.w = 5;
+ s.set_i64_4(L4);
+
+ L2 = s.get_u64_2();
+ if (L2.x != 1 || L2.y != 2) {
+ return false;
+ }
+ L2.x = 2;
+ L2.y = 3;
+ s.set_u64_2(L2);
+
+ L3 = s.get_u64_3();
+ if (L3.x != 1 || L3.y != 2 || L3.z != 3) {
+ return false;
+ }
+ L3.x = 2;
+ L3.y = 3;
+ L3.z = 4;
+ s.set_u64_3(L3);
+
+ L4 = s.get_u64_4();
+ if (L4.x != 1 || L4.y != 2 || L4.z != 3 || L4.w != 4) {
+ return false;
+ }
+ L4.x = 2;
+ L4.y = 3;
+ L4.z = 4;
+ L4.w = 5;
+ s.set_u64_4(L4);
+
+ return true;
+ }
+
+ public void run() {
+ RenderScript pRS = RenderScript.create(mCtx);
+ ScriptC_vector s = new ScriptC_vector(pRS, mRes, R.raw.vector);
+ pRS.setMessageHandler(mRsMessage);
+ if (!initializeGlobals(s)) {
+ result = -1;
+ } else {
+ s.invoke_vector_test();
+ pRS.finish();
+ waitForMessage();
+ }
+ pRS.destroy();
+ }
+}
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/vector.rs b/tests/RenderScriptTests/tests/src/com/android/rs/test/vector.rs
new file mode 100644
index 0000000..0430a2f
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/vector.rs
@@ -0,0 +1,198 @@
+#include "shared.rsh"
+
+// Testing vector types
+float2 f2 = { 1.0f, 2.0f };
+float3 f3 = { 1.0f, 2.0f, 3.0f };
+float4 f4 = { 1.0f, 2.0f, 3.0f, 4.0f };
+
+double2 d2 = { 1.0, 2.0 };
+double3 d3 = { 1.0, 2.0, 3.0 };
+double4 d4 = { 1.0, 2.0, 3.0, 4.0 };
+
+char2 i8_2 = { 1, 2 };
+char3 i8_3 = { 1, 2, 3 };
+char4 i8_4 = { 1, 2, 3, 4 };
+
+uchar2 u8_2 = { 1, 2 };
+uchar3 u8_3 = { 1, 2, 3 };
+uchar4 u8_4 = { 1, 2, 3, 4 };
+
+short2 i16_2 = { 1, 2 };
+short3 i16_3 = { 1, 2, 3 };
+short4 i16_4 = { 1, 2, 3, 4 };
+
+ushort2 u16_2 = { 1, 2 };
+ushort3 u16_3 = { 1, 2, 3 };
+ushort4 u16_4 = { 1, 2, 3, 4 };
+
+int2 i32_2 = { 1, 2 };
+int3 i32_3 = { 1, 2, 3 };
+int4 i32_4 = { 1, 2, 3, 4 };
+
+uint2 u32_2 = { 1, 2 };
+uint3 u32_3 = { 1, 2, 3 };
+uint4 u32_4 = { 1, 2, 3, 4 };
+
+long2 i64_2 = { 1, 2 };
+long3 i64_3 = { 1, 2, 3 };
+long4 i64_4 = { 1, 2, 3, 4 };
+
+ulong2 u64_2 = { 1, 2 };
+ulong3 u64_3 = { 1, 2, 3 };
+ulong4 u64_4 = { 1, 2, 3, 4 };
+
+static bool test_vector_types() {
+ bool failed = false;
+
+ rsDebug("Testing F32", 0);
+ _RS_ASSERT(f2.x == 2.99f);
+ _RS_ASSERT(f2.y == 3.99f);
+
+ _RS_ASSERT(f3.x == 2.99f);
+ _RS_ASSERT(f3.y == 3.99f);
+ _RS_ASSERT(f3.z == 4.99f);
+
+ _RS_ASSERT(f4.x == 2.99f);
+ _RS_ASSERT(f4.y == 3.99f);
+ _RS_ASSERT(f4.z == 4.99f);
+ _RS_ASSERT(f4.w == 5.99f);
+
+ rsDebug("Testing F64", 0);
+ _RS_ASSERT(d2.x == 2.99);
+ _RS_ASSERT(d2.y == 3.99);
+
+ _RS_ASSERT(d3.x == 2.99);
+ _RS_ASSERT(d3.y == 3.99);
+ _RS_ASSERT(d3.z == 4.99);
+
+ _RS_ASSERT(d4.x == 2.99);
+ _RS_ASSERT(d4.y == 3.99);
+ _RS_ASSERT(d4.z == 4.99);
+ _RS_ASSERT(d4.w == 5.99);
+
+ rsDebug("Testing I8", 0);
+ _RS_ASSERT(i8_2.x == 2);
+ _RS_ASSERT(i8_2.y == 3);
+
+ _RS_ASSERT(i8_3.x == 2);
+ _RS_ASSERT(i8_3.y == 3);
+ _RS_ASSERT(i8_3.z == 4);
+
+ _RS_ASSERT(i8_4.x == 2);
+ _RS_ASSERT(i8_4.y == 3);
+ _RS_ASSERT(i8_4.z == 4);
+ _RS_ASSERT(i8_4.w == 5);
+
+ rsDebug("Testing U8", 0);
+ _RS_ASSERT(u8_2.x == 2);
+ _RS_ASSERT(u8_2.y == 3);
+
+ _RS_ASSERT(u8_3.x == 2);
+ _RS_ASSERT(u8_3.y == 3);
+ _RS_ASSERT(u8_3.z == 4);
+
+ _RS_ASSERT(u8_4.x == 2);
+ _RS_ASSERT(u8_4.y == 3);
+ _RS_ASSERT(u8_4.z == 4);
+ _RS_ASSERT(u8_4.w == 5);
+
+ rsDebug("Testing I16", 0);
+ _RS_ASSERT(i16_2.x == 2);
+ _RS_ASSERT(i16_2.y == 3);
+
+ _RS_ASSERT(i16_3.x == 2);
+ _RS_ASSERT(i16_3.y == 3);
+ _RS_ASSERT(i16_3.z == 4);
+
+ _RS_ASSERT(i16_4.x == 2);
+ _RS_ASSERT(i16_4.y == 3);
+ _RS_ASSERT(i16_4.z == 4);
+ _RS_ASSERT(i16_4.w == 5);
+
+ rsDebug("Testing U16", 0);
+ _RS_ASSERT(u16_2.x == 2);
+ _RS_ASSERT(u16_2.y == 3);
+
+ _RS_ASSERT(u16_3.x == 2);
+ _RS_ASSERT(u16_3.y == 3);
+ _RS_ASSERT(u16_3.z == 4);
+
+ _RS_ASSERT(u16_4.x == 2);
+ _RS_ASSERT(u16_4.y == 3);
+ _RS_ASSERT(u16_4.z == 4);
+ _RS_ASSERT(u16_4.w == 5);
+
+ rsDebug("Testing I32", 0);
+ _RS_ASSERT(i32_2.x == 2);
+ _RS_ASSERT(i32_2.y == 3);
+
+ _RS_ASSERT(i32_3.x == 2);
+ _RS_ASSERT(i32_3.y == 3);
+ _RS_ASSERT(i32_3.z == 4);
+
+ _RS_ASSERT(i32_4.x == 2);
+ _RS_ASSERT(i32_4.y == 3);
+ _RS_ASSERT(i32_4.z == 4);
+ _RS_ASSERT(i32_4.w == 5);
+
+ rsDebug("Testing U32", 0);
+ _RS_ASSERT(u32_2.x == 2);
+ _RS_ASSERT(u32_2.y == 3);
+
+ _RS_ASSERT(u32_3.x == 2);
+ _RS_ASSERT(u32_3.y == 3);
+ _RS_ASSERT(u32_3.z == 4);
+
+ _RS_ASSERT(u32_4.x == 2);
+ _RS_ASSERT(u32_4.y == 3);
+ _RS_ASSERT(u32_4.z == 4);
+ _RS_ASSERT(u32_4.w == 5);
+
+ rsDebug("Testing I64", 0);
+ _RS_ASSERT(i64_2.x == 2);
+ _RS_ASSERT(i64_2.y == 3);
+
+ _RS_ASSERT(i64_3.x == 2);
+ _RS_ASSERT(i64_3.y == 3);
+ _RS_ASSERT(i64_3.z == 4);
+
+ _RS_ASSERT(i64_4.x == 2);
+ _RS_ASSERT(i64_4.y == 3);
+ _RS_ASSERT(i64_4.z == 4);
+ _RS_ASSERT(i64_4.w == 5);
+
+ rsDebug("Testing U64", 0);
+ _RS_ASSERT(u64_2.x == 2);
+ _RS_ASSERT(u64_2.y == 3);
+
+ _RS_ASSERT(u64_3.x == 2);
+ _RS_ASSERT(u64_3.y == 3);
+ _RS_ASSERT(u64_3.z == 4);
+
+ _RS_ASSERT(u64_4.x == 2);
+ _RS_ASSERT(u64_4.y == 3);
+ _RS_ASSERT(u64_4.z == 4);
+ _RS_ASSERT(u64_4.w == 5);
+
+ if (failed) {
+ rsDebug("test_vector FAILED", 0);
+ }
+ else {
+ rsDebug("test_vector PASSED", 0);
+ }
+
+ return failed;
+}
+
+void vector_test() {
+ bool failed = false;
+ failed |= test_vector_types();
+
+ if (failed) {
+ rsSendToClientBlocking(RS_MSG_TEST_FAILED);
+ }
+ else {
+ rsSendToClientBlocking(RS_MSG_TEST_PASSED);
+ }
+}
+
diff --git a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
index 98f8529..a45e879 100644
--- a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
+++ b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
@@ -138,6 +138,10 @@
return "";
}
+ @LayoutlibDelegate
+ /*package*/ static String getScript(String locale) {
+ return "";
+ }
@LayoutlibDelegate
/*package*/ static String[] getISOLanguagesNative() {
diff --git a/tools/layoutlib/bridge/tests/Android.mk b/tools/layoutlib/bridge/tests/Android.mk
index e303638..98cade9 100644
--- a/tools/layoutlib/bridge/tests/Android.mk
+++ b/tools/layoutlib/bridge/tests/Android.mk
@@ -19,6 +19,8 @@
# Only compile source java files in this lib.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_RESOURCE_DIRS := res
+
LOCAL_MODULE := layoutlib-tests
LOCAL_MODULE_TAGS := optional
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/testdata/layout1.xml b/tools/layoutlib/bridge/tests/res/com/android/layoutlib/testdata/layout1.xml
similarity index 100%
rename from tools/layoutlib/bridge/tests/src/com/android/layoutlib/testdata/layout1.xml
rename to tools/layoutlib/bridge/tests/res/com/android/layoutlib/testdata/layout1.xml
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
index 96436fe..865a008 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
@@ -38,7 +38,7 @@
public void testXmlBlockParser() throws Exception {
XmlPullParser parser = ParserFactory.create(
- getClass().getResourceAsStream("com/android/layoutlib/testdata/layout1.xml"),
+ getClass().getResourceAsStream("/com/android/layoutlib/testdata/layout1.xml"),
"layout1.xml");
parser = new BridgeXmlBlockParser(parser, null, false /* platformResourceFlag */);