diff options
38 files changed, 681 insertions, 519 deletions
diff --git a/api/current.txt b/api/current.txt index 073bf91..4a35785 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1516,12 +1516,13 @@ package android { field public static final int Theme_Holo_Light_Dialog_NoActionBar = 16973941; // 0x1030075 field public static final int Theme_Holo_Light_Dialog_NoActionBar_MinWidth = 16973942; // 0x1030076 field public static final int Theme_Holo_Light_NoActionBar = 16974064; // 0x10300f0 + field public static final int Theme_Holo_Light_NoActionBar_Fullscreen = 16974065; // 0x10300f1 field public static final int Theme_Holo_Light_Panel = 16973948; // 0x103007c - field public static final int Theme_Holo_Light_SplitActionBarWhenNarrow = 16974067; // 0x10300f3 + field public static final int Theme_Holo_Light_SplitActionBarWhenNarrow = 16974068; // 0x10300f4 field public static final int Theme_Holo_NoActionBar = 16973932; // 0x103006c field public static final int Theme_Holo_NoActionBar_Fullscreen = 16973933; // 0x103006d field public static final int Theme_Holo_Panel = 16973947; // 0x103007b - field public static final int Theme_Holo_SplitActionBarWhenNarrow = 16974066; // 0x10300f2 + field public static final int Theme_Holo_SplitActionBarWhenNarrow = 16974067; // 0x10300f3 field public static final int Theme_Holo_Wallpaper = 16973949; // 0x103007d field public static final int Theme_Holo_Wallpaper_NoTitleBar = 16973950; // 0x103007e field public static final int Theme_InputMethod = 16973908; // 0x1030054 diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index 36bb046..5919150 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -110,7 +110,21 @@ public class ViewConfiguration { * double-tap. */ private static final int DOUBLE_TAP_TIMEOUT = 300; - + + /** + * Defines the maximum duration in milliseconds between a touch pad + * touch and release for a given touch to be considered a tap (click) as + * opposed to a hover movement gesture. + */ + private static final int HOVER_TAP_TIMEOUT = 150; + + /** + * Defines the maximum distance in pixels that a touch pad touch can move + * before being released for it to be considered a tap (click) as opposed + * to a hover movement gesture. + */ + private static final int HOVER_TAP_SLOP = 20; + /** * Defines the duration in milliseconds we want to display zoom controls in response * to a user panning within an application. @@ -369,7 +383,7 @@ public class ViewConfiguration { public static int getTapTimeout() { return TAP_TIMEOUT; } - + /** * @return the duration in milliseconds we will wait to see if a touch event * is a jump tap. If the user does not move within this interval, it is @@ -387,7 +401,27 @@ public class ViewConfiguration { public static int getDoubleTapTimeout() { return DOUBLE_TAP_TIMEOUT; } - + + /** + * @return the maximum duration in milliseconds between a touch pad + * touch and release for a given touch to be considered a tap (click) as + * opposed to a hover movement gesture. + * @hide + */ + public static int getHoverTapTimeout() { + return HOVER_TAP_TIMEOUT; + } + + /** + * @return the maximum distance in pixels that a touch pad touch can move + * before being released for it to be considered a tap (click) as opposed + * to a hover movement gesture. + * @hide + */ + public static int getHoverTapSlop() { + return HOVER_TAP_SLOP; + } + /** * @return Inset in pixels to look for touchable content when the user touches the edge of the * screen diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index a68ca60..4aad022 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -6461,7 +6461,7 @@ public class WebView extends AbsoluteLayout if (hscroll != 0 || vscroll != 0) { final int vdelta = (int) (vscroll * getVerticalScrollFactor()); final int hdelta = (int) (hscroll * getHorizontalScrollFactor()); - if (pinScrollBy(hdelta, vdelta, true, 0)) { + if (pinScrollBy(hdelta, vdelta, false, 0)) { return true; } } diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index f2bd7cb..55312e3 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1657,6 +1657,9 @@ <public type="attr" name="compatibleWidthLimitDp" /> <public type="attr" name="largestWidthLimitDp" /> + <public type="style" name="Theme.Holo.Light.NoActionBar" /> + <public type="style" name="Theme.Holo.Light.NoActionBar.Fullscreen" /> + <!-- =============================================================== Resources added in version 13 of the platform (Ice Cream Sandwich) =============================================================== --> @@ -1666,7 +1669,6 @@ <public type="attr" name="state_drag_hovered" /> <public type="attr" name="stopWithTask" /> - <public type="style" name="Theme.Holo.Light.NoActionBar" /> <public type="style" name="TextAppearance.SuggestionHighlight" /> <public type="style" name="Theme.Holo.SplitActionBarWhenNarrow" /> <public type="style" name="Theme.Holo.Light.SplitActionBarWhenNarrow" /> @@ -1704,4 +1706,5 @@ <public type="attr" name="notificationTimeout" /> <public type="attr" name="accessibilityFlags" /> <public type="attr" name="canRetrieveWindowContent" /> + </resources> diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk index b02d904..00f47fa 100644 --- a/core/tests/coretests/Android.mk +++ b/core/tests/coretests/Android.mk @@ -12,7 +12,7 @@ LOCAL_SRC_FILES := \ $(call all-java-files-under, EnabledTestApp/src) LOCAL_DX_FLAGS := --core-library -LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib +LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib mockwebserver LOCAL_JAVA_LIBRARIES := android.test.runner LOCAL_PACKAGE_NAME := FrameworksCoreTests diff --git a/core/tests/coretests/src/android/net/http/AbstractProxyTest.java b/core/tests/coretests/src/android/net/http/AbstractProxyTest.java index ee89414..ee4ce95 100644 --- a/core/tests/coretests/src/android/net/http/AbstractProxyTest.java +++ b/core/tests/coretests/src/android/net/http/AbstractProxyTest.java @@ -16,6 +16,10 @@ package android.net.http; +import com.google.mockwebserver.MockResponse; +import com.google.mockwebserver.MockWebServer; +import com.google.mockwebserver.RecordedRequest; +import com.google.mockwebserver.SocketPolicy; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; @@ -33,10 +37,6 @@ import org.apache.http.conn.params.ConnRouteParams; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.ssl.AllowAllHostnameVerifier; import org.apache.http.conn.ssl.SSLSocketFactory; -import tests.http.MockResponse; -import tests.http.MockWebServer; -import tests.http.RecordedRequest; -import tests.http.SocketPolicy; public abstract class AbstractProxyTest extends TestCase { diff --git a/core/tests/coretests/src/android/net/http/CookiesTest.java b/core/tests/coretests/src/android/net/http/CookiesTest.java index e736bc9..29e590f 100644 --- a/core/tests/coretests/src/android/net/http/CookiesTest.java +++ b/core/tests/coretests/src/android/net/http/CookiesTest.java @@ -16,6 +16,9 @@ package android.net.http; +import com.google.mockwebserver.MockResponse; +import com.google.mockwebserver.MockWebServer; +import com.google.mockwebserver.RecordedRequest; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URISyntaxException; @@ -30,9 +33,6 @@ import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.conn.params.ConnRoutePNames; import org.apache.http.impl.client.DefaultHttpClient; -import tests.http.MockResponse; -import tests.http.MockWebServer; -import tests.http.RecordedRequest; public final class CookiesTest extends TestCase { diff --git a/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java b/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java index ad3ec3d..da77298 100644 --- a/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java +++ b/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java @@ -16,6 +16,12 @@ package android.net.http; +import com.google.mockwebserver.MockResponse; +import com.google.mockwebserver.MockWebServer; +import com.google.mockwebserver.SocketPolicy; +import static com.google.mockwebserver.SocketPolicy.DISCONNECT_AT_END; +import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_INPUT_AT_END; +import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_OUTPUT_AT_END; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; @@ -24,12 +30,6 @@ import junit.framework.TestCase; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; -import tests.http.MockResponse; -import tests.http.MockWebServer; -import tests.http.SocketPolicy; -import static tests.http.SocketPolicy.DISCONNECT_AT_END; -import static tests.http.SocketPolicy.SHUTDOWN_INPUT_AT_END; -import static tests.http.SocketPolicy.SHUTDOWN_OUTPUT_AT_END; public final class DefaultHttpClientTest extends TestCase { diff --git a/include/ui/Input.h b/include/ui/Input.h index 3b5aba4..c9f694a 100644 --- a/include/ui/Input.h +++ b/include/ui/Input.h @@ -655,11 +655,6 @@ private: // Oldest sample to consider when calculating the velocity. static const nsecs_t MAX_AGE = 200 * 1000000; // 200 ms - // When the total duration of the window of samples being averaged is less - // than the window size, the resulting velocity is scaled to reduce the impact - // of overestimation in short traces. - static const nsecs_t MIN_WINDOW = 100 * 1000000; // 100 ms - // The minimum duration between samples when estimating velocity. static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp index e95dbe4..0af7f80 100644 --- a/libs/ui/Input.cpp +++ b/libs/ui/Input.cpp @@ -700,7 +700,6 @@ bool MotionEvent::isTouchEvent(int32_t source, int32_t action) { const uint32_t VelocityTracker::HISTORY_SIZE; const nsecs_t VelocityTracker::MAX_AGE; -const nsecs_t VelocityTracker::MIN_WINDOW; const nsecs_t VelocityTracker::MIN_DURATION; VelocityTracker::VelocityTracker() { @@ -891,14 +890,6 @@ bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const // Make sure we used at least one sample. if (samplesUsed != 0) { - // Scale the velocity linearly if the window of samples is small. - nsecs_t totalDuration = newestMovement.eventTime - oldestMovement.eventTime; - if (totalDuration < MIN_WINDOW) { - float scale = float(totalDuration) / float(MIN_WINDOW); - accumVx *= scale; - accumVy *= scale; - } - *outVx = accumVx; *outVy = accumVy; return true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index 70a78df..3175a99 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -446,12 +446,14 @@ public class NetworkController extends BroadcastReceiver { } boolean isCdmaEri() { - final int iconIndex = mServiceState.getCdmaEriIconIndex(); - if (iconIndex != EriInfo.ROAMING_INDICATOR_OFF) { - final int iconMode = mServiceState.getCdmaEriIconMode(); - if (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL - || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH) { - return true; + if (mServiceState != null) { + final int iconIndex = mServiceState.getCdmaEriIconIndex(); + if (iconIndex != EriInfo.ROAMING_INDICATOR_OFF) { + final int iconMode = mServiceState.getCdmaEriIconMode(); + if (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL + || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH) { + return true; + } } } return false; @@ -854,7 +856,7 @@ public class NetworkController extends BroadcastReceiver { pw.print(" mDataActivity="); pw.println(mDataActivity); pw.print(" mServiceState="); - pw.println(mServiceState.toString()); + pw.println(mServiceState); pw.print(" mNetworkName="); pw.println(mNetworkName); pw.print(" mNetworkNameDefault="); diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp index c42e3ab..09b24a8 100644 --- a/services/input/InputReader.cpp +++ b/services/input/InputReader.cpp @@ -200,23 +200,6 @@ static int32_t calculateEdgeFlagsUsingPointerBounds( return edgeFlags; } -static void clampPositionUsingPointerBounds( - const sp<PointerControllerInterface>& pointerController, float* x, float* y) { - float minX, minY, maxX, maxY; - if (pointerController->getBounds(&minX, &minY, &maxX, &maxY)) { - if (*x < minX) { - *x = minX; - } else if (*x > maxX) { - *x = maxX; - } - if (*y < minY) { - *y = minY; - } else if (*y > maxY) { - *y = maxY; - } - } -} - static float calculateCommonVector(float a, float b) { if (a > 0 && b > 0) { return a < b ? a : b; @@ -787,8 +770,8 @@ void InputReader::dump(String8& dump) { mConfig.pointerGestureTapSlop); dump.appendFormat(INDENT3 "MultitouchSettleInterval: %0.1fms\n", mConfig.pointerGestureMultitouchSettleInterval * 0.000001f); - dump.appendFormat(INDENT3 "MultitouchMinSpeed: %0.1fpx/s\n", - mConfig.pointerGestureMultitouchMinSpeed); + dump.appendFormat(INDENT3 "MultitouchMinDistance: %0.1fpx\n", + mConfig.pointerGestureMultitouchMinDistance); dump.appendFormat(INDENT3 "SwipeTransitionAngleCosine: %0.1f\n", mConfig.pointerGestureSwipeTransitionAngleCosine); dump.appendFormat(INDENT3 "SwipeMaxWidthRatio: %0.1f\n", @@ -3509,11 +3492,18 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag cancelPreviousGesture = false; } - // Switch pointer presentation. - mPointerController->setPresentation( - mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS - ? PointerControllerInterface::PRESENTATION_SPOT - : PointerControllerInterface::PRESENTATION_POINTER); + // Update the pointer presentation and spots. + if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT); + if (finishPreviousGesture || cancelPreviousGesture) { + mPointerController->clearSpots(); + } + mPointerController->setSpots(mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, + mPointerGesture.currentGestureIdBits); + } else { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + } // Show or hide the pointer if needed. switch (mPointerGesture.currentGestureMode) { @@ -3712,12 +3702,6 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.currentGestureIdBits.clear(); mPointerGesture.pointerVelocityControl.reset(); - - if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { - mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL; - mPointerGesture.spotIdBits.clear(); - moveSpotsLocked(); - } return true; } } @@ -3798,22 +3782,18 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, if (isQuietTime) { // Case 1: Quiet time. (QUIET) #if DEBUG_GESTURES - LOGD("Gestures: QUIET for next %0.3fms", - (mPointerGesture.quietTime + QUIET_INTERVAL - when) * 0.000001f); + LOGD("Gestures: QUIET for next %0.3fms", (mPointerGesture.quietTime + + mConfig->pointerGestureQuietInterval - when) * 0.000001f); #endif - *outFinishPreviousGesture = true; + if (mPointerGesture.lastGestureMode != PointerGesture::QUIET) { + *outFinishPreviousGesture = true; + } mPointerGesture.activeGestureId = -1; mPointerGesture.currentGestureMode = PointerGesture::QUIET; mPointerGesture.currentGestureIdBits.clear(); mPointerGesture.pointerVelocityControl.reset(); - - if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { - mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL; - mPointerGesture.spotIdBits.clear(); - moveSpotsLocked(); - } } else if (isPointerDown(mCurrentTouch.buttonState)) { // Case 2: Button is pressed. (BUTTON_CLICK_OR_DRAG) // The pointer follows the active touch point. @@ -3899,32 +3879,11 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - - if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { - if (activeTouchId >= 0) { - // Collapse all spots into one point at the pointer location. - mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_BUTTON_DRAG; - mPointerGesture.spotIdBits.clear(); - for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) { - uint32_t id = mCurrentTouch.pointers[i].id; - mPointerGesture.spotIdBits.markBit(id); - mPointerGesture.spotIdToIndex[id] = i; - mPointerGesture.spotCoords[i] = mPointerGesture.currentGestureCoords[0]; - } - } else { - // No fingers. Generate a spot at the pointer location so the - // anchor appears to be pressed. - mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_BUTTON_CLICK; - mPointerGesture.spotIdBits.clear(); - mPointerGesture.spotIdBits.markBit(0); - mPointerGesture.spotIdToIndex[0] = 0; - mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0]; - } - moveSpotsLocked(); - } } else if (mCurrentTouch.pointerCount == 0) { // Case 3. No fingers down and button is not pressed. (NEUTRAL) - *outFinishPreviousGesture = true; + if (mPointerGesture.lastGestureMode != PointerGesture::NEUTRAL) { + *outFinishPreviousGesture = true; + } // Watch for taps coming out of HOVER or TAP_DRAG mode. // Checking for taps after TAP_DRAG allows us to detect double-taps. @@ -3965,15 +3924,6 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.currentGestureCoords[0].setAxisValue( AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { - mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_TAP; - mPointerGesture.spotIdBits.clear(); - mPointerGesture.spotIdBits.markBit(lastActiveTouchId); - mPointerGesture.spotIdToIndex[lastActiveTouchId] = 0; - mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0]; - moveSpotsLocked(); - } - tapped = true; } else { #if DEBUG_GESTURES @@ -3999,12 +3949,6 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.activeGestureId = -1; mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL; mPointerGesture.currentGestureIdBits.clear(); - - if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { - mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL; - mPointerGesture.spotIdBits.clear(); - moveSpotsLocked(); - } } } else if (mCurrentTouch.pointerCount == 1) { // Case 4. Exactly one finger down, button is not pressed. (HOVER or TAP_DRAG) @@ -4067,7 +4011,9 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, #if DEBUG_GESTURES LOGD("Gestures: HOVER"); #endif - *outFinishPreviousGesture = true; + if (mPointerGesture.lastGestureMode != PointerGesture::HOVER) { + *outFinishPreviousGesture = true; + } mPointerGesture.activeGestureId = 0; down = false; } @@ -4094,16 +4040,6 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.tapX = x; mPointerGesture.tapY = y; } - - if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { - mPointerGesture.spotGesture = down ? PointerControllerInterface::SPOT_GESTURE_DRAG - : PointerControllerInterface::SPOT_GESTURE_HOVER; - mPointerGesture.spotIdBits.clear(); - mPointerGesture.spotIdBits.markBit(activeTouchId); - mPointerGesture.spotIdToIndex[activeTouchId] = 0; - mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0]; - moveSpotsLocked(); - } } else { // Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM) // We need to provide feedback for each finger that goes down so we cannot wait @@ -4131,8 +4067,8 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, // Reset the gesture. #if DEBUG_GESTURES LOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, " - "settle time remaining %0.3fms", - (mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL - when) + "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime + + mConfig->pointerGestureMultitouchSettleInterval - when) * 0.000001f); #endif *outCancelPreviousGesture = true; @@ -4147,101 +4083,134 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.referenceIdBits.clear(); mPointerGesture.pointerVelocityControl.reset(); - if (settled && mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS - && mLastTouch.idBits.hasBit(mPointerGesture.activeTouchId)) { - // The spot is already visible and has settled, use it as the reference point - // for the gesture. Other spots will be positioned relative to this one. + // Use the centroid and pointer location as the reference points for the gesture. #if DEBUG_GESTURES - LOGD("Gestures: Using active spot as reference for MULTITOUCH, " - "settle time expired %0.3fms ago", - (when - mPointerGesture.firstTouchTime - MULTITOUCH_SETTLE_INTERVAL) - * 0.000001f); + LOGD("Gestures: Using centroid as reference for MULTITOUCH, " + "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime + + mConfig->pointerGestureMultitouchSettleInterval - when) + * 0.000001f); #endif - const PointerData& d = mLastTouch.pointers[mLastTouch.idToIndex[ - mPointerGesture.activeTouchId]]; - mPointerGesture.referenceTouchX = d.x; - mPointerGesture.referenceTouchY = d.y; - const PointerCoords& c = mPointerGesture.spotCoords[mPointerGesture.spotIdToIndex[ - mPointerGesture.activeTouchId]]; - mPointerGesture.referenceGestureX = c.getAxisValue(AMOTION_EVENT_AXIS_X); - mPointerGesture.referenceGestureY = c.getAxisValue(AMOTION_EVENT_AXIS_Y); + mCurrentTouch.getCentroid(&mPointerGesture.referenceTouchX, + &mPointerGesture.referenceTouchY); + mPointerController->getPosition(&mPointerGesture.referenceGestureX, + &mPointerGesture.referenceGestureY); + } + + // Clear the reference deltas for fingers not yet included in the reference calculation. + for (BitSet32 idBits(mCurrentTouch.idBits.value & ~mPointerGesture.referenceIdBits.value); + !idBits.isEmpty(); ) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + + mPointerGesture.referenceDeltas[id].dx = 0; + mPointerGesture.referenceDeltas[id].dy = 0; + } + mPointerGesture.referenceIdBits = mCurrentTouch.idBits; + + // Add delta for all fingers and calculate a common movement delta. + float commonDeltaX = 0, commonDeltaY = 0; + BitSet32 commonIdBits(mLastTouch.idBits.value & mCurrentTouch.idBits.value); + for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) { + bool first = (idBits == commonIdBits); + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + + const PointerData& cpd = mCurrentTouch.pointers[mCurrentTouch.idToIndex[id]]; + const PointerData& lpd = mLastTouch.pointers[mLastTouch.idToIndex[id]]; + PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; + delta.dx += cpd.x - lpd.x; + delta.dy += cpd.y - lpd.y; + + if (first) { + commonDeltaX = delta.dx; + commonDeltaY = delta.dy; } else { - // Use the centroid and pointer location as the reference points for the gesture. -#if DEBUG_GESTURES - LOGD("Gestures: Using centroid as reference for MULTITOUCH, " - "settle time remaining %0.3fms", - (mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL - when) - * 0.000001f); -#endif - mCurrentTouch.getCentroid(&mPointerGesture.referenceTouchX, - &mPointerGesture.referenceTouchY); - mPointerController->getPosition(&mPointerGesture.referenceGestureX, - &mPointerGesture.referenceGestureY); + commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx); + commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy); } } + // Consider transitions from PRESS to SWIPE or MULTITOUCH. if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) { - float d; - if (mCurrentTouch.pointerCount > 2) { - // There are more than two pointers, switch to FREEFORM. + float dist[MAX_POINTER_ID + 1]; + int32_t distOverThreshold = 0; + for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty(); ) { + uint32_t id = idBits.firstMarkedBit(); + idBits.clearBit(id); + + PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; + dist[id] = hypotf(delta.dx * mLocked.pointerGestureXZoomScale, + delta.dy * mLocked.pointerGestureYZoomScale); + if (dist[id] > mConfig->pointerGestureMultitouchMinDistance) { + distOverThreshold += 1; + } + } + + // Only transition when at least two pointers have moved further than + // the minimum distance threshold. + if (distOverThreshold >= 2) { + float d; + if (mCurrentTouch.pointerCount > 2) { + // There are more than two pointers, switch to FREEFORM. #if DEBUG_GESTURES - LOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2", - mCurrentTouch.pointerCount); + LOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2", + mCurrentTouch.pointerCount); #endif - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; - } else if (((d = distance( - mCurrentTouch.pointers[0].x, mCurrentTouch.pointers[0].y, - mCurrentTouch.pointers[1].x, mCurrentTouch.pointers[1].y)) - > mLocked.pointerGestureMaxSwipeWidth)) { - // There are two pointers but they are too far apart, switch to FREEFORM. + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + } else if (((d = distance( + mCurrentTouch.pointers[0].x, mCurrentTouch.pointers[0].y, + mCurrentTouch.pointers[1].x, mCurrentTouch.pointers[1].y)) + > mLocked.pointerGestureMaxSwipeWidth)) { + // There are two pointers but they are too far apart for a SWIPE, + // switch to FREEFORM. #if DEBUG_GESTURES - LOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f", - d, mLocked.pointerGestureMaxSwipeWidth); + LOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f", + d, mLocked.pointerGestureMaxSwipeWidth); #endif - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; - } else { - // There are two pointers. Wait for both pointers to start moving - // before deciding whether this is a SWIPE or FREEFORM gesture. - uint32_t id1 = mCurrentTouch.pointers[0].id; - uint32_t id2 = mCurrentTouch.pointers[1].id; - - float vx1, vy1, vx2, vy2; - mPointerGesture.velocityTracker.getVelocity(id1, &vx1, &vy1); - mPointerGesture.velocityTracker.getVelocity(id2, &vx2, &vy2); - - float speed1 = hypotf(vx1, vy1); - float speed2 = hypotf(vx2, vy2); - if (speed1 >= mConfig->pointerGestureMultitouchMinSpeed - && speed2 >= mConfig->pointerGestureMultitouchMinSpeed) { - // Calculate the dot product of the velocity vectors. - // When the vectors are oriented in approximately the same direction, - // the angle betweeen them is near zero and the cosine of the angle - // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) * mag(v2). - float dot = vx1 * vx2 + vy1 * vy2; - float cosine = dot / (speed1 * speed2); // denominator always > 0 - if (cosine >= mConfig->pointerGestureSwipeTransitionAngleCosine) { - // Pointers are moving in the same direction. Switch to SWIPE. + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + } else { + // There are two pointers. Wait for both pointers to start moving + // before deciding whether this is a SWIPE or FREEFORM gesture. + uint32_t id1 = mCurrentTouch.pointers[0].id; + uint32_t id2 = mCurrentTouch.pointers[1].id; + float dist1 = dist[id1]; + float dist2 = dist[id2]; + if (dist1 >= mConfig->pointerGestureMultitouchMinDistance + && dist2 >= mConfig->pointerGestureMultitouchMinDistance) { + // Calculate the dot product of the displacement vectors. + // When the vectors are oriented in approximately the same direction, + // the angle betweeen them is near zero and the cosine of the angle + // approches 1.0. Recall that dot(v1, v2) = cos(angle) * mag(v1) * mag(v2). + PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1]; + PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2]; + float dot = delta1.dx * delta2.dx + delta1.dy * delta2.dy; + float cosine = dot / (dist1 * dist2); // denominator always > 0 + if (cosine >= mConfig->pointerGestureSwipeTransitionAngleCosine) { + // Pointers are moving in the same direction. Switch to SWIPE. #if DEBUG_GESTURES - LOGD("Gestures: PRESS transitioned to SWIPE, " - "speed1 %0.3f >= %0.3f, speed2 %0.3f >= %0.3f, " - "cosine %0.3f >= %0.3f", - speed1, MULTITOUCH_MIN_SPEED, speed2, MULTITOUCH_MIN_SPEED, - cosine, SWIPE_TRANSITION_ANGLE_COSINE); + LOGD("Gestures: PRESS transitioned to SWIPE, " + "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " + "cosine %0.3f >= %0.3f", + dist1, mConfig->pointerGestureMultitouchMinDistance, + dist2, mConfig->pointerGestureMultitouchMinDistance, + cosine, mConfig->pointerGestureSwipeTransitionAngleCosine); #endif - mPointerGesture.currentGestureMode = PointerGesture::SWIPE; - } else { - // Pointers are moving in different directions. Switch to FREEFORM. + mPointerGesture.currentGestureMode = PointerGesture::SWIPE; + } else { + // Pointers are moving in different directions. Switch to FREEFORM. #if DEBUG_GESTURES - LOGD("Gestures: PRESS transitioned to FREEFORM, " - "speed1 %0.3f >= %0.3f, speed2 %0.3f >= %0.3f, " - "cosine %0.3f < %0.3f", - speed1, MULTITOUCH_MIN_SPEED, speed2, MULTITOUCH_MIN_SPEED, - cosine, SWIPE_TRANSITION_ANGLE_COSINE); + LOGD("Gestures: PRESS transitioned to FREEFORM, " + "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " + "cosine %0.3f < %0.3f", + dist1, mConfig->pointerGestureMultitouchMinDistance, + dist2, mConfig->pointerGestureMultitouchMinDistance, + cosine, mConfig->pointerGestureSwipeTransitionAngleCosine); #endif - *outCancelPreviousGesture = true; - mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + *outCancelPreviousGesture = true; + mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; + } } } } @@ -4258,67 +4227,28 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, } } - // Clear the reference deltas for fingers not yet included in the reference calculation. - for (BitSet32 idBits(mCurrentTouch.idBits.value & ~mPointerGesture.referenceIdBits.value); - !idBits.isEmpty(); ) { - uint32_t id = idBits.firstMarkedBit(); - idBits.clearBit(id); - - mPointerGesture.referenceDeltas[id].dx = 0; - mPointerGesture.referenceDeltas[id].dy = 0; - } - mPointerGesture.referenceIdBits = mCurrentTouch.idBits; - - // Move the reference points based on the overall group motion of the fingers. - // The objective is to calculate a vector delta that is common to the movement - // of all fingers. - BitSet32 commonIdBits(mLastTouch.idBits.value & mCurrentTouch.idBits.value); - if (!commonIdBits.isEmpty()) { - float commonDeltaX = 0, commonDeltaY = 0; - for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) { - bool first = (idBits == commonIdBits); + // Move the reference points based on the overall group motion of the fingers + // except in PRESS mode while waiting for a transition to occur. + if (mPointerGesture.currentGestureMode != PointerGesture::PRESS + && (commonDeltaX || commonDeltaY)) { + for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty(); ) { uint32_t id = idBits.firstMarkedBit(); idBits.clearBit(id); - const PointerData& cpd = mCurrentTouch.pointers[mCurrentTouch.idToIndex[id]]; - const PointerData& lpd = mLastTouch.pointers[mLastTouch.idToIndex[id]]; PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; - delta.dx += cpd.x - lpd.x; - delta.dy += cpd.y - lpd.y; - - if (first) { - commonDeltaX = delta.dx; - commonDeltaY = delta.dy; - } else { - commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx); - commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy); - } + delta.dx = 0; + delta.dy = 0; } - if (commonDeltaX || commonDeltaY) { - for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) { - uint32_t id = idBits.firstMarkedBit(); - idBits.clearBit(id); - - PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; - delta.dx = 0; - delta.dy = 0; - } - - mPointerGesture.referenceTouchX += commonDeltaX; - mPointerGesture.referenceTouchY += commonDeltaY; + mPointerGesture.referenceTouchX += commonDeltaX; + mPointerGesture.referenceTouchY += commonDeltaY; - commonDeltaX *= mLocked.pointerGestureXMovementScale; - commonDeltaY *= mLocked.pointerGestureYMovementScale; - mPointerGesture.pointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY); + commonDeltaX *= mLocked.pointerGestureXMovementScale; + commonDeltaY *= mLocked.pointerGestureYMovementScale; + mPointerGesture.pointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY); - mPointerGesture.referenceGestureX += commonDeltaX; - mPointerGesture.referenceGestureY += commonDeltaY; - - clampPositionUsingPointerBounds(mPointerController, - &mPointerGesture.referenceGestureX, - &mPointerGesture.referenceGestureY); - } + mPointerGesture.referenceGestureX += commonDeltaX; + mPointerGesture.referenceGestureY += commonDeltaY; } // Report gestures. @@ -4344,10 +4274,6 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, mPointerGesture.referenceGestureY); mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - - if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { - mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_PRESS; - } } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) { // SWIPE mode. #if DEBUG_GESTURES @@ -4370,10 +4296,6 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, mPointerGesture.referenceGestureY); mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - - if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { - mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_SWIPE; - } } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) { // FREEFORM mode. #if DEBUG_GESTURES @@ -4475,33 +4397,6 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, "activeGestureId=%d", mPointerGesture.activeGestureId); #endif } - - if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { - mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_FREEFORM; - } - } - - // Update spot locations for PRESS, SWIPE and FREEFORM. - // We use the same calculation as we do to calculate the gesture pointers - // for FREEFORM so that the spots smoothly track gestures. - if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { - mPointerGesture.spotIdBits.clear(); - for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) { - uint32_t id = mCurrentTouch.pointers[i].id; - mPointerGesture.spotIdBits.markBit(id); - mPointerGesture.spotIdToIndex[id] = i; - - float x = (mCurrentTouch.pointers[i].x - mPointerGesture.referenceTouchX) - * mLocked.pointerGestureXZoomScale + mPointerGesture.referenceGestureX; - float y = (mCurrentTouch.pointers[i].y - mPointerGesture.referenceTouchY) - * mLocked.pointerGestureYZoomScale + mPointerGesture.referenceGestureY; - - mPointerGesture.spotCoords[i].clear(); - mPointerGesture.spotCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, x); - mPointerGesture.spotCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, y); - mPointerGesture.spotCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); - } - moveSpotsLocked(); } } @@ -4544,11 +4439,6 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, return true; } -void TouchInputMapper::moveSpotsLocked() { - mPointerController->setSpots(mPointerGesture.spotGesture, - mPointerGesture.spotCoords, mPointerGesture.spotIdToIndex, mPointerGesture.spotIdBits); -} - void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, int32_t action, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags, const PointerProperties* properties, const PointerCoords* coords, diff --git a/services/input/InputReader.h b/services/input/InputReader.h index 1d4ad87..36cd89c 100644 --- a/services/input/InputReader.h +++ b/services/input/InputReader.h @@ -101,8 +101,8 @@ struct InputReaderConfiguration { nsecs_t pointerGestureMultitouchSettleInterval; // The transition from PRESS to SWIPE or FREEFORM gesture mode is made when - // both of the pointers are moving at least this fast. - float pointerGestureMultitouchMinSpeed; // in pixels per second + // at least two pointers have moved at least this far from their starting place. + float pointerGestureMultitouchMinDistance; // in pixels // The transition from PRESS to SWIPE gesture mode can only occur when the // cosine of the angle between the two vectors is greater than or equal to than this value @@ -134,7 +134,7 @@ struct InputReaderConfiguration { filterTouchEvents(false), filterJumpyTouchEvents(false), virtualKeyQuietTime(0), - pointerVelocityControlParameters(1.0f, 80.0f, 400.0f, 4.0f), + pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f), wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f), pointerGestureQuietInterval(100 * 1000000LL), // 100 ms pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second @@ -142,10 +142,10 @@ struct InputReaderConfiguration { pointerGestureTapDragInterval(150 * 1000000LL), // 150 ms pointerGestureTapSlop(10.0f), // 10 pixels pointerGestureMultitouchSettleInterval(100 * 1000000LL), // 100 ms - pointerGestureMultitouchMinSpeed(150.0f), // 150 pixels per second + pointerGestureMultitouchMinDistance(15), // 15 pixels pointerGestureSwipeTransitionAngleCosine(0.5f), // cosine of 45degrees - pointerGestureSwipeMaxWidthRatio(0.333f), - pointerGestureMovementSpeedRatio(0.3f), + pointerGestureSwipeMaxWidthRatio(0.25f), + pointerGestureMovementSpeedRatio(0.8f), pointerGestureZoomSpeedRatio(0.3f) { } }; @@ -1140,12 +1140,6 @@ private: PointerProperties lastGestureProperties[MAX_POINTERS]; PointerCoords lastGestureCoords[MAX_POINTERS]; - // Pointer coords and ids for the current spots. - PointerControllerInterface::SpotGesture spotGesture; - BitSet32 spotIdBits; // same set of ids as touch ids - uint32_t spotIdToIndex[MAX_POINTER_ID + 1]; - PointerCoords spotCoords[MAX_POINTERS]; - // Time the pointer gesture last went down. nsecs_t downTime; @@ -1192,8 +1186,6 @@ private: currentGestureIdBits.clear(); lastGestureMode = NEUTRAL; lastGestureIdBits.clear(); - spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL; - spotIdBits.clear(); downTime = 0; velocityTracker.clear(); resetTap(); @@ -1219,7 +1211,6 @@ private: void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout); bool preparePointerGestures(nsecs_t when, bool* outCancelPreviousGesture, bool* outFinishPreviousGesture, bool isTimeout); - void moveSpotsLocked(); // Dispatches a motion event. // If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the diff --git a/services/input/PointerController.cpp b/services/input/PointerController.cpp index c18ebcf..12c7cba 100644 --- a/services/input/PointerController.cpp +++ b/services/input/PointerController.cpp @@ -240,15 +240,15 @@ void PointerController::setPresentation(Presentation presentation) { } } -void PointerController::setSpots(SpotGesture spotGesture, - const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits) { +void PointerController::setSpots(const PointerCoords* spotCoords, + const uint32_t* spotIdToIndex, BitSet32 spotIdBits) { #if DEBUG_POINTER_UPDATES - LOGD("setSpots: spotGesture=%d", spotGesture); + LOGD("setSpots: idBits=%08x", spotIdBits.value); for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) { uint32_t id = idBits.firstMarkedBit(); idBits.clearBit(id); const PointerCoords& c = spotCoords[spotIdToIndex[id]]; - LOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f", id, + LOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f", id, c.getAxisValue(AMOTION_EVENT_AXIS_X), c.getAxisValue(AMOTION_EVENT_AXIS_Y), c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); diff --git a/services/input/PointerController.h b/services/input/PointerController.h index 1c21db1..700ef72 100644 --- a/services/input/PointerController.h +++ b/services/input/PointerController.h @@ -90,38 +90,6 @@ public: /* Sets the mode of the pointer controller. */ virtual void setPresentation(Presentation presentation) = 0; - // Describes the current gesture. - enum SpotGesture { - // No gesture. - // Do not display any spots. - SPOT_GESTURE_NEUTRAL, - // Tap at current location. - // Briefly display one spot at the tapped location. - SPOT_GESTURE_TAP, - // Drag at current location. - // Display spot at pressed location. - SPOT_GESTURE_DRAG, - // Button pressed but no finger is down. - // Display spot at pressed location. - SPOT_GESTURE_BUTTON_CLICK, - // Button pressed and a finger is down. - // Display spot at pressed location. - SPOT_GESTURE_BUTTON_DRAG, - // One finger down and hovering. - // Display spot at the hovered location. - SPOT_GESTURE_HOVER, - // Two fingers down but not sure in which direction they are moving so we consider - // it a press at the pointer location. - // Display two spots near the pointer location. - SPOT_GESTURE_PRESS, - // Two fingers down and moving in same direction. - // Display two spots near the pointer location. - SPOT_GESTURE_SWIPE, - // Two or more fingers down and moving in arbitrary directions. - // Display two or more spots near the pointer location, one for each finger. - SPOT_GESTURE_FREEFORM, - }; - /* Sets the spots for the current gesture. * The spots are not subject to the inactivity timeout like the pointer * itself it since they are expected to remain visible for so long as @@ -131,8 +99,7 @@ public: * For spotCoords, pressure != 0 indicates that the spot's location is being * pressed (not hovering). */ - virtual void setSpots(SpotGesture spotGesture, - const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, + virtual void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits) = 0; /* Removes all spots. */ @@ -198,8 +165,8 @@ public: virtual void unfade(Transition transition); virtual void setPresentation(Presentation presentation); - virtual void setSpots(SpotGesture spotGesture, - const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits); + virtual void setSpots(const PointerCoords* spotCoords, + const uint32_t* spotIdToIndex, BitSet32 spotIdBits); virtual void clearSpots(); void setDisplaySize(int32_t width, int32_t height); diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp index 00b4222..d04c9e7 100644 --- a/services/input/tests/InputReader_test.cpp +++ b/services/input/tests/InputReader_test.cpp @@ -101,8 +101,8 @@ private: virtual void setPresentation(Presentation presentation) { } - virtual void setSpots(SpotGesture spotGesture, - const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits) { + virtual void setSpots(const PointerCoords* spotCoords, + const uint32_t* spotIdToIndex, BitSet32 spotIdBits) { } virtual void clearSpots() { diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index dd76eb8..b559fb9 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -458,6 +458,12 @@ public class ConnectivityService extends IConnectivityManager.Stub { mTethering.getTetherableBluetoothRegexs().length != 0) && mTethering.getUpstreamIfaceRegexs().length != 0); + try { + nmService.registerObserver(mTethering); + } catch (RemoteException e) { + loge("Error registering observer :" + e); + } + if (DBG) { mInetLog = new ArrayList(); } diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 41ec63a..4e2501b 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -321,6 +321,13 @@ public class WifiService extends IWifiManager.Stub { } break; } + case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { + Slog.e(TAG, "WifiStateMachine channel lost, msg.arg1 =" + msg.arg1); + mWifiStateMachineChannel = null; + //Re-establish connection to state machine + mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler()); + break; + } default: { Slog.d(TAG, "WifiStateMachineHandler.handleMessage ignoring msg=" + msg); break; @@ -593,7 +600,12 @@ public class WifiService extends IWifiManager.Stub { */ public WifiConfiguration getWifiApConfiguration() { enforceAccessPermission(); - return mWifiStateMachine.syncGetWifiApConfiguration(mWifiStateMachineChannel); + if (mWifiStateMachineChannel != null) { + return mWifiStateMachine.syncGetWifiApConfiguration(mWifiStateMachineChannel); + } else { + Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); + return null; + } } /** diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index c2a8a1d..da9f1d6 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -92,7 +92,6 @@ import android.os.FileObserver; import android.os.FileUtils; import android.os.Handler; import android.os.IBinder; -import android.os.IInterface; import android.os.IPermissionController; import android.os.Looper; import android.os.Message; diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java index 7ea0591..5fa26ef 100644 --- a/services/java/com/android/server/connectivity/Tethering.java +++ b/services/java/com/android/server/connectivity/Tethering.java @@ -129,13 +129,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub { mNMService = nmService; mLooper = looper; - // register for notifications from NetworkManagement Service - try { - mNMService.registerObserver(this); - } catch (RemoteException e) { - Log.e(TAG, "Error registering observer :" + e); - } - mIfaces = new HashMap<String, TetherInterfaceSM>(); // make our own thread so we don't anr the system diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java index 3095c37..ee69311 100644 --- a/services/java/com/android/server/wm/InputManager.java +++ b/services/java/com/android/server/wm/InputManager.java @@ -617,8 +617,13 @@ public class InputManager { } @SuppressWarnings("unused") - public int getTapTimeout() { - return ViewConfiguration.getTapTimeout(); + public int getHoverTapTimeout() { + return ViewConfiguration.getHoverTapTimeout(); + } + + @SuppressWarnings("unused") + public int getHoverTapSlop() { + return ViewConfiguration.getHoverTapSlop(); } @SuppressWarnings("unused") @@ -632,11 +637,6 @@ public class InputManager { } @SuppressWarnings("unused") - public int getTouchSlop() { - return ViewConfiguration.get(mContext).getScaledTouchSlop(); - } - - @SuppressWarnings("unused") public int getMaxEventsPerSecond() { int result = 0; try { diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index d95d4c5..819c16e 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -153,6 +153,7 @@ public class WindowManagerService extends IWindowManager.Stub static final boolean DEBUG_WINDOW_MOVEMENT = false; static final boolean DEBUG_TOKEN_MOVEMENT = false; static final boolean DEBUG_ORIENTATION = false; + static final boolean DEBUG_APP_ORIENTATION = false; static final boolean DEBUG_CONFIGURATION = false; static final boolean DEBUG_APP_TRANSITIONS = false; static final boolean DEBUG_STARTING_WINDOW = false; @@ -427,6 +428,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean mWindowsFreezingScreen = false; long mFreezeGcPending = 0; int mAppsFreezingScreen = 0; + int mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; int mLayoutSeq = 0; @@ -3187,6 +3189,15 @@ public class WindowManagerService extends IWindowManager.Stub } public int getOrientationFromWindowsLocked() { + if (mDisplayFrozen || mOpeningApps.size() > 0 || mClosingApps.size() > 0) { + // If the display is frozen, some activities may be in the middle + // of restarting, and thus have removed their old window. If the + // window has the flag to hide the lock screen, then the lock screen + // can re-appear and inflict its own orientation on us. Keep the + // orientation stable until this all settles down. + return mLastWindowForcedOrientation; + } + int pos = mWindows.size() - 1; while (pos >= 0) { WindowState wtoken = mWindows.get(pos); @@ -3194,7 +3205,7 @@ public class WindowManagerService extends IWindowManager.Stub if (wtoken.mAppToken != null) { // We hit an application window. so the orientation will be determined by the // app window. No point in continuing further. - return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + return (mLastWindowForcedOrientation=ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); } if (!wtoken.isVisibleLw() || !wtoken.mPolicyVisibilityAfterAnim) { continue; @@ -3204,10 +3215,10 @@ public class WindowManagerService extends IWindowManager.Stub (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){ continue; } else { - return req; + return (mLastWindowForcedOrientation=req); } } - return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + return (mLastWindowForcedOrientation=ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); } public int getOrientationFromAppTokensLocked() { @@ -3220,16 +3231,23 @@ public class WindowManagerService extends IWindowManager.Stub while (pos >= 0) { AppWindowToken wtoken = mAppTokens.get(pos); pos--; + + if (DEBUG_APP_ORIENTATION) Slog.v(TAG, "Checking app orientation: " + wtoken); + // if we're about to tear down this window and not seek for // the behind activity, don't use it for orientation if (!findingBehind && (!wtoken.hidden && wtoken.hiddenRequested)) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + wtoken + + " -- going to hide"); continue; } if (!haveGroup) { // We ignore any hidden applications on the top. if (wtoken.hiddenRequested || wtoken.willBeHidden) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + wtoken + + " -- hidden on top"); continue; } haveGroup = true; @@ -3243,6 +3261,8 @@ public class WindowManagerService extends IWindowManager.Stub // user's orientation. if (lastOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND && lastFullscreen) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + wtoken + + " -- end of group, return " + lastOrientation); return lastOrientation; } } @@ -3253,16 +3273,21 @@ public class WindowManagerService extends IWindowManager.Stub lastFullscreen = wtoken.appFullscreen; if (lastFullscreen && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + wtoken + + " -- full screen, return " + or); return or; } // If this application has requested an explicit orientation, // then use it. if (or != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) { + if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + wtoken + + " -- explicitly set, return " + or); return or; } findingBehind |= (or == ActivityInfo.SCREEN_ORIENTATION_BEHIND); } + if (DEBUG_ORIENTATION) Slog.v(TAG, "No app is requesting an orientation"); return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } @@ -3335,15 +3360,6 @@ public class WindowManagerService extends IWindowManager.Stub * android.os.IBinder) */ boolean updateOrientationFromAppTokensLocked(boolean inTransaction) { - if (mDisplayFrozen || mOpeningApps.size() > 0 || mClosingApps.size() > 0) { - // If the display is frozen, some activities may be in the middle - // of restarting, and thus have removed their old window. If the - // window has the flag to hide the lock screen, then the lock screen - // can re-appear and inflict its own orientation on us. Keep the - // orientation stable until this all settles down. - return false; - } - boolean changed = false; long ident = Binder.clearCallingIdentity(); try { @@ -8939,9 +8955,10 @@ public class WindowManagerService extends IWindowManager.Stub pw.print(" mAppsFreezingScreen="); pw.print(mAppsFreezingScreen); pw.print(" mWaitingForConfig="); pw.println(mWaitingForConfig); pw.print(" mRotation="); pw.print(mRotation); - pw.print(" mForcedAppOrientation="); pw.print(mForcedAppOrientation); pw.print(" mRequestedRotation="); pw.print(mRequestedRotation); pw.print(" mAltOrientation="); pw.println(mAltOrientation); + pw.print(" mLastWindowForcedOrientation"); pw.print(mLastWindowForcedOrientation); + pw.print(" mForcedAppOrientation="); pw.println(mForcedAppOrientation); pw.print(" mDeferredRotation="); pw.print(mDeferredRotation); pw.print(", mDeferredRotationAnimFlags="); pw.println(mDeferredRotationAnimFlags); pw.print(" mAnimationPending="); pw.print(mAnimationPending); diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index 881882f..7c5084f 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -56,7 +56,7 @@ namespace android { // The exponent used to calculate the pointer speed scaling factor. // The scaling factor is calculated as 2 ^ (speed * exponent), // where the speed ranges from -7 to + 7 and is supplied by the user. -static const float POINTER_SPEED_EXPONENT = 1.0f / 3; +static const float POINTER_SPEED_EXPONENT = 1.0f / 4; static struct { jmethodID notifyConfigurationChanged; @@ -76,10 +76,10 @@ static struct { jmethodID getKeyRepeatTimeout; jmethodID getKeyRepeatDelay; jmethodID getMaxEventsPerSecond; - jmethodID getTapTimeout; + jmethodID getHoverTapTimeout; + jmethodID getHoverTapSlop; jmethodID getDoubleTapTimeout; jmethodID getLongPressTimeout; - jmethodID getTouchSlop; jmethodID getPointerLayer; jmethodID getPointerIcon; } gCallbacksClassInfo; @@ -410,32 +410,32 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon env->DeleteLocalRef(excludedDeviceNames); } - jint tapTimeout = env->CallIntMethod(mCallbacksObj, - gCallbacksClassInfo.getTapTimeout); - if (!checkAndClearExceptionFromCallback(env, "getTapTimeout")) { + jint hoverTapTimeout = env->CallIntMethod(mCallbacksObj, + gCallbacksClassInfo.getHoverTapTimeout); + if (!checkAndClearExceptionFromCallback(env, "getHoverTapTimeout")) { jint doubleTapTimeout = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.getDoubleTapTimeout); if (!checkAndClearExceptionFromCallback(env, "getDoubleTapTimeout")) { jint longPressTimeout = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.getLongPressTimeout); if (!checkAndClearExceptionFromCallback(env, "getLongPressTimeout")) { - outConfig->pointerGestureTapInterval = milliseconds_to_nanoseconds(tapTimeout); + outConfig->pointerGestureTapInterval = milliseconds_to_nanoseconds(hoverTapTimeout); // We must ensure that the tap-drag interval is significantly shorter than // the long-press timeout because the tap is held down for the entire duration // of the double-tap timeout. jint tapDragInterval = max(min(longPressTimeout - 100, - doubleTapTimeout), tapTimeout); + doubleTapTimeout), hoverTapTimeout); outConfig->pointerGestureTapDragInterval = milliseconds_to_nanoseconds(tapDragInterval); } } } - jint touchSlop = env->CallIntMethod(mCallbacksObj, - gCallbacksClassInfo.getTouchSlop); - if (!checkAndClearExceptionFromCallback(env, "getTouchSlop")) { - outConfig->pointerGestureTapSlop = touchSlop; + jint hoverTapSlop = env->CallIntMethod(mCallbacksObj, + gCallbacksClassInfo.getHoverTapSlop); + if (!checkAndClearExceptionFromCallback(env, "getHoverTapSlop")) { + outConfig->pointerGestureTapSlop = hoverTapSlop; } { // acquire lock @@ -1394,8 +1394,11 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gCallbacksClassInfo.getKeyRepeatDelay, clazz, "getKeyRepeatDelay", "()I"); - GET_METHOD_ID(gCallbacksClassInfo.getTapTimeout, clazz, - "getTapTimeout", "()I"); + GET_METHOD_ID(gCallbacksClassInfo.getHoverTapTimeout, clazz, + "getHoverTapTimeout", "()I"); + + GET_METHOD_ID(gCallbacksClassInfo.getHoverTapSlop, clazz, + "getHoverTapSlop", "()I"); GET_METHOD_ID(gCallbacksClassInfo.getDoubleTapTimeout, clazz, "getDoubleTapTimeout", "()I"); @@ -1403,9 +1406,6 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gCallbacksClassInfo.getLongPressTimeout, clazz, "getLongPressTimeout", "()I"); - GET_METHOD_ID(gCallbacksClassInfo.getTouchSlop, clazz, - "getTouchSlop", "()I"); - GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, clazz, "getMaxEventsPerSecond", "()I"); diff --git a/telephony/java/com/android/internal/telephony/BaseCommands.java b/telephony/java/com/android/internal/telephony/BaseCommands.java index 0c4581b..8427d14 100644 --- a/telephony/java/com/android/internal/telephony/BaseCommands.java +++ b/telephony/java/com/android/internal/telephony/BaseCommands.java @@ -96,8 +96,10 @@ public abstract class BaseCommands implements CommandsInterface { protected Registrant mRestrictedStateRegistrant; protected Registrant mGsmBroadcastSmsRegistrant; - // Network Mode received from PhoneFactory - protected int mNetworkMode; + // Preferred network type received from PhoneFactory. + // This is used when establishing a connection to the + // vendor ril so it starts up in the correct mode. + protected int mPreferredNetworkType; // CDMA subscription received from PhoneFactory protected int mCdmaSubscription; // Type of Phone, GSM or CDMA. Set by CDMAPhone or GSMPhone. diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java index 2c04b30..572bbaa 100644 --- a/telephony/java/com/android/internal/telephony/RIL.java +++ b/telephony/java/com/android/internal/telephony/RIL.java @@ -611,14 +611,14 @@ public final class RIL extends BaseCommands implements CommandsInterface { //***** Constructors - public RIL(Context context, int networkMode, int cdmaSubscription) { + public RIL(Context context, int preferredNetworkType, int cdmaSubscription) { super(context); if (RILJ_LOGD) { - riljLog("RIL(context, networkMode=" + networkMode + + riljLog("RIL(context, preferredNetworkType=" + preferredNetworkType + " cdmaSubscription=" + cdmaSubscription + ")"); } mCdmaSubscription = cdmaSubscription; - mNetworkMode = networkMode; + mPreferredNetworkType = preferredNetworkType; mPhoneType = RILConstants.NO_PHONE; PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); @@ -1813,6 +1813,8 @@ public final class RIL extends BaseCommands implements CommandsInterface { rr.mp.writeInt(1); rr.mp.writeInt(networkType); + mPreferredNetworkType = networkType; + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " : " + networkType); @@ -2222,7 +2224,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { case RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM: ret = responseInts(p); break; case RIL_REQUEST_EXPLICIT_CALL_TRANSFER: ret = responseVoid(p); break; case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE: ret = responseVoid(p); break; - case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: ret = responseInts(p); break; + case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: ret = responseGetPreferredNetworkType(p); break; case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS: ret = responseCellList(p); break; case RIL_REQUEST_SET_LOCATION_UPDATES: ret = responseVoid(p); break; case RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE: ret = responseVoid(p); break; @@ -2737,7 +2739,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { // Initial conditions setRadioPower(false, null); - setPreferredNetworkType(mNetworkMode, null); + setPreferredNetworkType(mPreferredNetworkType, null); setCdmaSubscriptionSource(mCdmaSubscription, null); notifyRegistrantsRilConnectionChanged(((int[])ret)[0]); break; @@ -3161,6 +3163,18 @@ public final class RIL extends BaseCommands implements CommandsInterface { return response; } + private Object responseGetPreferredNetworkType(Parcel p) { + int [] response = (int[]) responseInts(p); + + if (response.length >= 1) { + // Since this is the response for getPreferredNetworkType + // we'll assume that it should be the value we want the + // vendor ril to take if we reestablish a connection to it. + mPreferredNetworkType = response[0]; + } + return response; + } + private Object responseGmsBroadcastConfig(Parcel p) { int num; ArrayList<SmsBroadcastConfigInfo> response; diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index 922cd4c..8e3ed93 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -425,7 +425,7 @@ public final class Canvas_Delegate { AffineTransform matrixTx = matrixDelegate.getAffineTransform(); // combine them so that the given matrix is applied after. - currentTx.preConcatenate(matrixTx); + currentTx.concatenate(matrixTx); // give it to the graphics2D as a new matrix replacing all previous transform snapshot.setTransform(currentTx); @@ -717,7 +717,7 @@ public final class Canvas_Delegate { /*package*/ static void native_drawCircle(int nativeCanvas, float cx, float cy, float radius, int paint) { native_drawOval(nativeCanvas, - new RectF(cx - radius, cy - radius, radius*2, radius*2), + new RectF(cx - radius, cy - radius, radius, radius), paint); } diff --git a/tools/layoutlib/bridge/src/android/os/HandlerThread_Delegate.java b/tools/layoutlib/bridge/src/android/os/HandlerThread_Delegate.java new file mode 100644 index 0000000..afbe97c --- /dev/null +++ b/tools/layoutlib/bridge/src/android/os/HandlerThread_Delegate.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import com.android.layoutlib.bridge.android.BridgeContext; +import com.android.layoutlib.bridge.impl.RenderAction; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Delegate overriding selected methods of android.os.HandlerThread + * + * Through the layoutlib_create tool, selected methods of Handler have been replaced + * by calls to methods of the same name in this delegate class. + * + * + */ +public class HandlerThread_Delegate { + + private static Map<BridgeContext, List<HandlerThread>> sThreads = + new HashMap<BridgeContext, List<HandlerThread>>(); + + public static void cleanUp(BridgeContext context) { + List<HandlerThread> list = sThreads.get(context); + if (list != null) { + for (HandlerThread thread : list) { + thread.quit(); + } + + list.clear(); + sThreads.remove(context); + } + } + + // -------- Delegate methods + + @LayoutlibDelegate + /*package*/ static void run(HandlerThread theThread) { + // record the thread so that it can be quit() on clean up. + BridgeContext context = RenderAction.getCurrentContext(); + List<HandlerThread> list = sThreads.get(context); + if (list == null) { + list = new ArrayList<HandlerThread>(); + sThreads.put(context, list); + } + + list.add(theThread); + + // ---- START DEFAULT IMPLEMENTATION. + + theThread.mTid = Process.myTid(); + Looper.prepare(); + synchronized (theThread) { + theThread.mLooper = Looper.myLooper(); + theThread.notifyAll(); + } + Process.setThreadPriority(theThread.mPriority); + theThread.onLooperPrepared(); + Looper.loop(); + theThread.mTid = -1; + } +} diff --git a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java index 0f3cf57..3ef3288 100644 --- a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java +++ b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java @@ -22,7 +22,10 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; import android.util.AttributeSet; +import android.util.Xml; import java.io.IOException; @@ -35,6 +38,8 @@ import java.io.IOException; */ public class LayoutInflater_Delegate { + public static boolean sIsInInclude = false; + /** * Recursive method used to descend down the xml hierarchy and instantiate * views, instantiate their children, and then call onFinishInflate(). @@ -94,4 +99,128 @@ public class LayoutInflater_Delegate { } } } + + @LayoutlibDelegate + public static void parseInclude( + LayoutInflater thisInflater, + XmlPullParser parser, View parent, AttributeSet attrs) + throws XmlPullParserException, IOException { + + int type; + + if (parent instanceof ViewGroup) { + final int layout = attrs.getAttributeResourceValue(null, "layout", 0); + if (layout == 0) { + final String value = attrs.getAttributeValue(null, "layout"); + if (value == null) { + throw new InflateException("You must specifiy a layout in the" + + " include tag: <include layout=\"@layout/layoutID\" />"); + } else { + throw new InflateException("You must specifiy a valid layout " + + "reference. The layout ID " + value + " is not valid."); + } + } else { + final XmlResourceParser childParser = + thisInflater.getContext().getResources().getLayout(layout); + + try { + final AttributeSet childAttrs = Xml.asAttributeSet(childParser); + + while ((type = childParser.next()) != XmlPullParser.START_TAG && + type != XmlPullParser.END_DOCUMENT) { + // Empty. + } + + if (type != XmlPullParser.START_TAG) { + throw new InflateException(childParser.getPositionDescription() + + ": No start tag found!"); + } + + final String childName = childParser.getName(); + + if (LayoutInflater.TAG_MERGE.equals(childName)) { + // Inflate all children. + thisInflater.rInflate(childParser, parent, childAttrs, false); + } else { + final View view = thisInflater.createViewFromTag(parent, childName, childAttrs); + final ViewGroup group = (ViewGroup) parent; + + // We try to load the layout params set in the <include /> tag. If + // they don't exist, we will rely on the layout params set in the + // included XML file. + // During a layoutparams generation, a runtime exception is thrown + // if either layout_width or layout_height is missing. We catch + // this exception and set localParams accordingly: true means we + // successfully loaded layout params from the <include /> tag, + // false means we need to rely on the included layout params. + ViewGroup.LayoutParams params = null; + try { + // ---- START CHANGES + sIsInInclude = true; + // ---- END CHANGES + + params = group.generateLayoutParams(attrs); + + } catch (RuntimeException e) { + // ---- START CHANGES + sIsInInclude = false; + // ---- END CHANGES + + params = group.generateLayoutParams(childAttrs); + } finally { + // ---- START CHANGES + sIsInInclude = false; + // ---- END CHANGES + + if (params != null) { + view.setLayoutParams(params); + } + } + + // Inflate all children. + thisInflater.rInflate(childParser, view, childAttrs, true); + + // Attempt to override the included layout's android:id with the + // one set on the <include /> tag itself. + TypedArray a = thisInflater.mContext.obtainStyledAttributes(attrs, + com.android.internal.R.styleable.View, 0, 0); + int id = a.getResourceId(com.android.internal.R.styleable.View_id, View.NO_ID); + // While we're at it, let's try to override android:visibility. + int visibility = a.getInt(com.android.internal.R.styleable.View_visibility, -1); + a.recycle(); + + if (id != View.NO_ID) { + view.setId(id); + } + + switch (visibility) { + case 0: + view.setVisibility(View.VISIBLE); + break; + case 1: + view.setVisibility(View.INVISIBLE); + break; + case 2: + view.setVisibility(View.GONE); + break; + } + + group.addView(view); + } + } finally { + childParser.close(); + } + } + } else { + throw new InflateException("<include /> can only be used inside of a ViewGroup"); + } + + final int currentDepth = parser.getDepth(); + while (((type = parser.next()) != XmlPullParser.END_TAG || + parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) { + // Empty + } + } + + } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index 144ec42..3ba3257 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -196,7 +196,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { Capability.UNBOUND_RENDERING, Capability.CUSTOM_BACKGROUND_COLOR, Capability.RENDER, - //Capability.LAYOUT_ONLY, // disable to run on ADT 10.0 which doesn't include this. + Capability.LAYOUT_ONLY, Capability.EMBEDDED_LAYOUT, Capability.VIEW_MANIPULATION, Capability.PLAY_ANIMATION, diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index e536028..4fa924d 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -71,7 +71,6 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; -import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; @@ -342,7 +341,7 @@ public final class BridgeContext extends Activity { try { KXmlParser parser = new KXmlParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new FileReader(xml)); + parser.setInput(new FileInputStream(xml), "UTF-8"); //$NON-NLS-1$); // set the resource ref to have correct view cookies mBridgeInflater.setResourceReference(resource); @@ -514,14 +513,13 @@ public final class BridgeContext extends Activity { BridgeTypedArray ta = ((BridgeResources) mSystemResources).newTypeArray(attrs.length, isPlatformFile); - // resolve the defStyleAttr value into a IStyleResourceValue - StyleResourceValue defStyleValues = null; - // look for a custom style. String customStyle = null; if (set != null) { customStyle = set.getAttributeValue(null /* namespace*/, "style"); } + + StyleResourceValue customStyleValues = null; if (customStyle != null) { ResourceValue item = mRenderResources.findResValue(customStyle, false /*forceFrameworkOnly*/); @@ -530,75 +528,76 @@ public final class BridgeContext extends Activity { item = mRenderResources.resolveResValue(item); if (item instanceof StyleResourceValue) { - defStyleValues = (StyleResourceValue)item; + customStyleValues = (StyleResourceValue)item; } } - if (defStyleValues == null) { - if (defStyleAttr != 0) { - // get the name from the int. - String defStyleName = searchAttr(defStyleAttr); + // resolve the defStyleAttr value into a IStyleResourceValue + StyleResourceValue defStyleValues = null; - if (defaultPropMap != null) { - defaultPropMap.put("style", defStyleName); - } + if (defStyleAttr != 0) { + // get the name from the int. + String defStyleName = searchAttr(defStyleAttr); - // look for the style in the current theme, and its parent: - ResourceValue item = mRenderResources.findItemInTheme(defStyleName); + if (defaultPropMap != null) { + defaultPropMap.put("style", defStyleName); + } - if (item != null) { - // item is a reference to a style entry. Search for it. - item = mRenderResources.findResValue(item.getValue(), - false /*forceFrameworkOnly*/); + // look for the style in the current theme, and its parent: + ResourceValue item = mRenderResources.findItemInTheme(defStyleName); - if (item instanceof StyleResourceValue) { - defStyleValues = (StyleResourceValue)item; - } - } else { - Bridge.getLog().error(null, - String.format( - "Failed to find style '%s' in current theme", defStyleName), - null /*data*/); - } - } else if (defStyleRes != 0) { - Pair<ResourceType, String> value = Bridge.resolveResourceId(defStyleRes); - if (value == null) { - value = mProjectCallback.resolveResourceId(defStyleRes); + if (item != null) { + // item is a reference to a style entry. Search for it. + item = mRenderResources.findResValue(item.getValue(), + false /*forceFrameworkOnly*/); + + if (item instanceof StyleResourceValue) { + defStyleValues = (StyleResourceValue)item; } + } else { + Bridge.getLog().error(null, + String.format( + "Failed to find style '%s' in current theme", defStyleName), + null /*data*/); + } + } else if (defStyleRes != 0) { + Pair<ResourceType, String> value = Bridge.resolveResourceId(defStyleRes); + if (value == null) { + value = mProjectCallback.resolveResourceId(defStyleRes); + } - if (value != null) { - if (value.getFirst() == ResourceType.STYLE) { - // look for the style in the current theme, and its parent: - ResourceValue item = mRenderResources.findItemInTheme(value.getSecond()); - if (item != null) { - if (item instanceof StyleResourceValue) { - if (defaultPropMap != null) { - defaultPropMap.put("style", item.getName()); - } - - defStyleValues = (StyleResourceValue)item; + if (value != null) { + if (value.getFirst() == ResourceType.STYLE) { + // look for the style in the current theme, and its parent: + ResourceValue item = mRenderResources.findItemInTheme(value.getSecond()); + if (item != null) { + if (item instanceof StyleResourceValue) { + if (defaultPropMap != null) { + defaultPropMap.put("style", item.getName()); } - } else { - Bridge.getLog().error(null, - String.format( - "Style with id 0x%x (resolved to '%s') does not exist.", - defStyleRes, value.getSecond()), - null /*data*/); + + defStyleValues = (StyleResourceValue)item; } } else { Bridge.getLog().error(null, String.format( - "Resouce id 0x%x is not of type STYLE (instead %s)", - defStyleRes, value.getFirst().toString()), + "Style with id 0x%x (resolved to '%s') does not exist.", + defStyleRes, value.getSecond()), null /*data*/); } } else { Bridge.getLog().error(null, String.format( - "Failed to find style with id 0x%x in current theme", - defStyleRes), + "Resouce id 0x%x is not of type STYLE (instead %s)", + defStyleRes, value.getFirst().toString()), null /*data*/); } + } else { + Bridge.getLog().error(null, + String.format( + "Failed to find style with id 0x%x in current theme", + defStyleRes), + null /*data*/); } } @@ -623,8 +622,13 @@ public final class BridgeContext extends Activity { if (value == null) { ResourceValue resValue = null; - // look for the value in the defStyle first (and its parent if needed) - if (defStyleValues != null) { + // look for the value in the custom style first (and its parent if needed) + if (customStyleValues != null) { + resValue = mRenderResources.findItemInStyle(customStyleValues, name); + } + + // then look for the value in the default Style (and its parent if needed) + if (resValue == null && defStyleValues != null) { resValue = mRenderResources.findItemInStyle(defStyleValues, name); } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java index d6bbebd..7c90a31 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java @@ -36,7 +36,7 @@ import android.view.View; import android.view.ViewGroup; import java.io.File; -import java.io.FileReader; +import java.io.FileInputStream; /** * Custom implementation of {@link LayoutInflater} to handle custom views. @@ -177,7 +177,7 @@ public final class BridgeInflater extends LayoutInflater { try { KXmlParser parser = new KXmlParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new FileReader(f)); + parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$ BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser( parser, bridgeContext, false); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java index 273e493..345f053 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java @@ -46,7 +46,6 @@ import android.view.ViewGroup.LayoutParams; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.FileReader; import java.io.InputStream; /** @@ -244,7 +243,7 @@ public final class BridgeResources extends Resources { // give that to our XmlBlockParser parser = new KXmlParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new FileReader(xml)); + parser.setInput(new FileInputStream(xml), "UTF-8"); //$NON-NLS-1$); } } @@ -282,7 +281,7 @@ public final class BridgeResources extends Resources { // give that to our XmlBlockParser parser = new KXmlParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new FileReader(xml)); + parser.setInput(new FileInputStream(xml), "UTF-8"); //$NON-NLS-1$); return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]); } @@ -501,7 +500,7 @@ public final class BridgeResources extends Resources { try { KXmlParser parser = new KXmlParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new FileReader(f)); + parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$); return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]); } catch (XmlPullParserException e) { @@ -536,7 +535,7 @@ public final class BridgeResources extends Resources { try { KXmlParser parser = new KXmlParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new FileReader(f)); + parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$); return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]); } catch (XmlPullParserException e) { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java index b9f769f..d4600a1 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java @@ -36,10 +36,11 @@ import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.util.DisplayMetrics; import android.util.TypedValue; +import android.view.LayoutInflater_Delegate; import android.view.ViewGroup.LayoutParams; import java.io.File; -import java.io.FileReader; +import java.io.FileInputStream; import java.util.Arrays; import java.util.Map; @@ -315,7 +316,7 @@ public final class BridgeTypedArray extends TypedArray { try { KXmlParser parser = new KXmlParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new FileReader(f)); + parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$); BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser( parser, mContext, resValue.isFramework()); @@ -471,40 +472,23 @@ public final class BridgeTypedArray extends TypedArray { */ @Override public int getDimensionPixelSize(int index, int defValue) { - if (mResourceData[index] == null) { - return defValue; - } + try { + return getDimension(index); + } catch (RuntimeException e) { + if (mResourceData[index] != null) { + String s = mResourceData[index].getValue(); - String s = mResourceData[index].getValue(); + if (s != null) { + // looks like we were unable to resolve the dimension value + Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT, + String.format( + "\"%1$s\" in attribute \"%2$s\" is not a valid format.", + s, mNames[index]), null /*data*/); + } + } - if (s == null) { - return defValue; - } else if (s.equals(BridgeConstants.MATCH_PARENT) || - s.equals(BridgeConstants.FILL_PARENT)) { - return LayoutParams.MATCH_PARENT; - } else if (s.equals(BridgeConstants.WRAP_CONTENT)) { - return LayoutParams.WRAP_CONTENT; - } else if (RenderResources.REFERENCE_NULL.equals(s)) { return defValue; } - - if (ResourceHelper.stringToFloat(s, mValue)) { - float f = mValue.getDimension(mBridgeResources.mMetrics); - - final int res = (int)(f+0.5f); - if (res != 0) return res; - if (f == 0) return 0; - if (f > 0) return 1; - return defValue; // this is basically unreachable. - } - - // looks like we were unable to resolve the dimension value - Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT, - String.format( - "\"%1$s\" in attribute \"%2$s\" is not a valid format.", - s, mNames[index]), null /*data*/); - - return defValue; } /** @@ -521,7 +505,20 @@ public final class BridgeTypedArray extends TypedArray { */ @Override public int getLayoutDimension(int index, String name) { - return getDimensionPixelSize(index, 0); + try { + // this will throw an exception + return getDimension(index); + } catch (RuntimeException e) { + + if (LayoutInflater_Delegate.sIsInInclude) { + throw new RuntimeException(); + } + + Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT, + "You must supply a " + name + " attribute.", null); + + return 0; + } } @Override @@ -529,6 +526,36 @@ public final class BridgeTypedArray extends TypedArray { return getDimensionPixelSize(index, defValue); } + private int getDimension(int index) { + if (mResourceData[index] == null) { + throw new RuntimeException(); + } + + String s = mResourceData[index].getValue(); + + if (s == null) { + throw new RuntimeException(); + } else if (s.equals(BridgeConstants.MATCH_PARENT) || + s.equals(BridgeConstants.FILL_PARENT)) { + return LayoutParams.MATCH_PARENT; + } else if (s.equals(BridgeConstants.WRAP_CONTENT)) { + return LayoutParams.WRAP_CONTENT; + } else if (RenderResources.REFERENCE_NULL.equals(s)) { + throw new RuntimeException(); + } + + if (ResourceHelper.stringToFloat(s, mValue)) { + float f = mValue.getDimension(mBridgeResources.mMetrics); + + final int res = (int)(f+0.5f); + if (res != 0) return res; + if (f == 0) return 0; + if (f > 0) return 1; + } + + throw new RuntimeException(); + } + /** * Retrieve a fractional unit attribute at <var>index</var>. * diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java index 0c4b0d3..060e6ee 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java @@ -73,7 +73,7 @@ abstract class CustomBar extends LinearLayout { parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); parser.setInput( getClass().getResourceAsStream(layoutPath), - "UTF8"); + "UTF8"); //$NON-NLS-1$ BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser( parser, (BridgeContext) context, false /*platformFile*/); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java index 8e80c21..6194f5d 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java @@ -29,6 +29,7 @@ import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.resources.ResourceType; +import android.os.HandlerThread_Delegate; import android.util.DisplayMetrics; import java.util.concurrent.TimeUnit; @@ -228,6 +229,10 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso private void tearDown() { // Make sure to remove static references, otherwise we could not unload the lib mContext.disposeResources(); + + // quit HandlerThread created during this session. + HandlerThread_Delegate.cleanUp(sCurrentContext); + sCurrentContext = null; Bridge.setLog(null); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java index 96ab30f..e5efa4e 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java @@ -44,7 +44,6 @@ import android.util.TypedValue; import java.io.File; import java.io.FileInputStream; -import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; @@ -124,7 +123,7 @@ public final class ResourceHelper { // providing an XmlPullParser KXmlParser parser = new KXmlParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new FileReader(f)); + parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$); BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser( parser, context, resValue.isFramework()); @@ -206,7 +205,7 @@ public final class ResourceHelper { // let the framework inflate the Drawable from the XML file. KXmlParser parser = new KXmlParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(new FileReader(f)); + parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$); BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser( parser, context, value.isFramework()); 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 3252fb4..70d5446 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 @@ -44,7 +44,7 @@ public class BridgeXmlBlockParserTest extends TestCase { InputStream input = this.getClass().getClassLoader().getResourceAsStream( "com/android/layoutlib/testdata/layout1.xml"); - parser.setInput(input, null /*encoding*/); + parser.setInput(input, "UTF-8"); //$NON-NLS-1$ assertEquals(XmlPullParser.START_DOCUMENT, parser.next()); diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index eff6bbc..5c60318 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -99,8 +99,10 @@ public final class CreateInfo implements ICreateInfo { "android.content.res.Resources$Theme#resolveAttribute", "android.graphics.BitmapFactory#finishDecode", "android.os.Handler#sendMessageAtTime", + "android.os.HandlerThread#run", "android.os.Build#getString", "android.view.LayoutInflater#rInflate", + "android.view.LayoutInflater#parseInclude", "android.view.View#isInEditMode", "com.android.internal.util.XmlUtils#convertValueToInt", // TODO: comment out once DelegateClass is working |