Merge "audio: allow audio port cache update even when audio patches contain invalidated sources/sinks" into lmp-mr1-dev
diff --git a/api/system-current.txt b/api/system-current.txt
index 56edc72..d84b1dc 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -30037,6 +30037,7 @@
method public final int getState();
method public final android.telecom.StatusHints getStatusHints();
method public final boolean isRingbackRequested();
+ method protected void notifyConferenceStarted();
method public void onAbort();
method public void onAnswer();
method public void onAudioStateChanged(android.telecom.AudioState);
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 27a03b6..5f7a17d 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -54,10 +54,6 @@
int action, in Bundle arguments, int interactionId,
IAccessibilityInteractionConnectionCallback callback, long threadId);
- boolean computeClickPointInScreen(int accessibilityWindowId, long accessibilityNodeId,
- int interactionId, IAccessibilityInteractionConnectionCallback callback,
- long threadId);
-
AccessibilityWindowInfo getWindow(int windowId);
List<AccessibilityWindowInfo> getWindows();
diff --git a/core/java/android/hardware/camera2/DngCreator.java b/core/java/android/hardware/camera2/DngCreator.java
index 6fc99ac..33d539c2 100644
--- a/core/java/android/hardware/camera2/DngCreator.java
+++ b/core/java/android/hardware/camera2/DngCreator.java
@@ -530,9 +530,9 @@
int uPixStride = uPlane.getPixelStride();
byte[] yuvPixel = { 0, 0, 0 };
- byte[] yFullRow = new byte[yPixStride * width];
- byte[] uFullRow = new byte[uPixStride * width / 2];
- byte[] vFullRow = new byte[vPixStride * width / 2];
+ byte[] yFullRow = new byte[yPixStride * (width - 1) + 1];
+ byte[] uFullRow = new byte[uPixStride * (width / 2 - 1) + 1];
+ byte[] vFullRow = new byte[vPixStride * (width / 2 - 1) + 1];
byte[] finalRow = new byte[BYTES_PER_RGB_PIX * width];
for (int i = 0; i < height; i++) {
int halfH = i / 2;
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 5e05683..68ad782 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -636,95 +636,6 @@
}
}
- public void computeClickPointInScreenClientThread(long accessibilityNodeId,
- Region interactiveRegion, int interactionId,
- IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
- long interrogatingTid, MagnificationSpec spec) {
- Message message = mHandler.obtainMessage();
- message.what = PrivateHandler.MSG_COMPUTE_CLICK_POINT_IN_SCREEN;
-
- SomeArgs args = SomeArgs.obtain();
- args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
- args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
- args.argi3 = interactionId;
- args.arg1 = callback;
- args.arg2 = spec;
- args.arg3 = interactiveRegion;
-
- message.obj = args;
-
- // If the interrogation is performed by the same thread as the main UI
- // thread in this process, set the message as a static reference so
- // after this call completes the same thread but in the interrogating
- // client can handle the message to generate the result.
- if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId) {
- AccessibilityInteractionClient.getInstanceForThread(
- interrogatingTid).setSameThreadMessage(message);
- } else {
- mHandler.sendMessage(message);
- }
- }
-
- private void computeClickPointInScreenUiThread(Message message) {
- SomeArgs args = (SomeArgs) message.obj;
- final int accessibilityViewId = args.argi1;
- final int virtualDescendantId = args.argi2;
- final int interactionId = args.argi3;
- final IAccessibilityInteractionConnectionCallback callback =
- (IAccessibilityInteractionConnectionCallback) args.arg1;
- final MagnificationSpec spec = (MagnificationSpec) args.arg2;
- final Region interactiveRegion = (Region) args.arg3;
- args.recycle();
-
- boolean succeeded = false;
- Point point = mTempPoint;
- try {
- if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
- return;
- }
- View target = null;
- if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
- target = findViewByAccessibilityId(accessibilityViewId);
- } else {
- target = mViewRootImpl.mView;
- }
- if (target != null && isShown(target)) {
- AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
- if (provider != null) {
- // For virtual views just use the center of the bounds in screen.
- AccessibilityNodeInfo node = null;
- if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
- node = provider.createAccessibilityNodeInfo(virtualDescendantId);
- } else {
- node = provider.createAccessibilityNodeInfo(
- AccessibilityNodeProvider.HOST_VIEW_ID);
- }
- if (node != null) {
- succeeded = true;
- Rect boundsInScreen = mTempRect;
- node.getBoundsInScreen(boundsInScreen);
- point.set(boundsInScreen.centerX(), boundsInScreen.centerY());
- }
- } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
- // For a real view, ask the view to compute the click point.
- succeeded = target.computeClickPointInScreenForAccessibility(
- interactiveRegion, point);
- }
- }
- } finally {
- try {
- Point result = null;
- if (succeeded) {
- applyAppScaleAndMagnificationSpecIfNeeded(point, spec);
- result = point;
- }
- callback.setComputeClickPointInScreenActionResult(result, interactionId);
- } catch (RemoteException re) {
- /* ignore - the other side will time out */
- }
- }
- }
-
private View findViewByAccessibilityId(int accessibilityId) {
View root = mViewRootImpl.mView;
if (root == null) {
@@ -1201,7 +1112,6 @@
private final static int MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_TEXT = 4;
private final static int MSG_FIND_FOCUS = 5;
private final static int MSG_FOCUS_SEARCH = 6;
- private final static int MSG_COMPUTE_CLICK_POINT_IN_SCREEN = 7;
public PrivateHandler(Looper looper) {
super(looper);
@@ -1223,8 +1133,6 @@
return "MSG_FIND_FOCUS";
case MSG_FOCUS_SEARCH:
return "MSG_FOCUS_SEARCH";
- case MSG_COMPUTE_CLICK_POINT_IN_SCREEN:
- return "MSG_COMPUTE_CLICK_POINT_IN_SCREEN";
default:
throw new IllegalArgumentException("Unknown message type: " + type);
}
@@ -1252,9 +1160,6 @@
case MSG_FOCUS_SEARCH: {
focusSearchUiThread(message);
} break;
- case MSG_COMPUTE_CLICK_POINT_IN_SCREEN: {
- computeClickPointInScreenUiThread(message);
- } break;
default:
throw new IllegalArgumentException("Unknown message type: " + type);
}
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 1c5c41c..5e45c8f 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -402,6 +402,23 @@
public static final int FLAG_TAINTED = 0x80000000;
/**
+ * Private flag indicating that this event was synthesized by the system and
+ * should be delivered to the accessibility focused view first. When being
+ * dispatched such an event is not handled by predecessors of the accessibility
+ * focused view and after the event reaches that view the flag is cleared and
+ * normal event dispatch is performed. This ensures that the platform can click
+ * on any view that has accessibility focus which is semantically equivalent to
+ * asking the view to perform a click accessibility action but more generic as
+ * views not implementing click action correctly can still be activated.
+ *
+ * @hide
+ * @see #isTargetAccessibilityFocus()
+ * @see #setTargetAccessibilityFocus(boolean)
+ */
+ public static final int FLAG_TARGET_ACCESSIBILITY_FOCUS = 0x40000000;
+
+
+ /**
* Flag indicating the motion event intersected the top edge of the screen.
*/
public static final int EDGE_TOP = 0x00000001;
@@ -1766,6 +1783,20 @@
nativeSetFlags(mNativePtr, tainted ? flags | FLAG_TAINTED : flags & ~FLAG_TAINTED);
}
+ /** @hide */
+ public final boolean isTargetAccessibilityFocus() {
+ final int flags = getFlags();
+ return (flags & FLAG_TARGET_ACCESSIBILITY_FOCUS) != 0;
+ }
+
+ /** @hide */
+ public final void setTargetAccessibilityFocus(boolean targetsFocus) {
+ final int flags = getFlags();
+ nativeSetFlags(mNativePtr, targetsFocus
+ ? flags | FLAG_TARGET_ACCESSIBILITY_FOCUS
+ : flags & ~FLAG_TARGET_ACCESSIBILITY_FOCUS);
+ }
+
/**
* Returns the time (in ms) when the user originally pressed down to start
* a stream of position events.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ed75de3..6928b2c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5553,12 +5553,23 @@
}
/**
- * Gets the location of this view in screen coordintates.
+ * Gets the location of this view in screen coordinates.
*
* @param outRect The output location
* @hide
*/
public void getBoundsOnScreen(Rect outRect) {
+ getBoundsOnScreen(outRect, false);
+ }
+
+ /**
+ * Gets the location of this view in screen coordinates.
+ *
+ * @param outRect The output location
+ * @param clipToParent Whether to clip child bounds to the parent ones.
+ * @hide
+ */
+ public void getBoundsOnScreen(Rect outRect, boolean clipToParent) {
if (mAttachInfo == null) {
return;
}
@@ -5578,6 +5589,13 @@
position.offset(-parentView.mScrollX, -parentView.mScrollY);
+ if (clipToParent) {
+ position.left = Math.max(position.left, 0);
+ position.top = Math.max(position.top, 0);
+ position.right = Math.min(position.right, parentView.getWidth());
+ position.bottom = Math.min(position.bottom, parentView.getHeight());
+ }
+
if (!parentView.hasIdentityMatrix()) {
parentView.getMatrix().mapRect(position);
}
@@ -5609,7 +5627,7 @@
getDrawingRect(bounds);
info.setBoundsInParent(bounds);
- getBoundsOnScreen(bounds);
+ getBoundsOnScreen(bounds, true);
info.setBoundsInScreen(bounds);
ViewParent parent = getParentForAccessibility();
@@ -5805,142 +5823,6 @@
}
/**
- * Computes a point on which a sequence of a down/up event can be sent to
- * trigger clicking this view. This method is for the exclusive use by the
- * accessibility layer to determine where to send a click event in explore
- * by touch mode.
- *
- * @param interactiveRegion The interactive portion of this window.
- * @param outPoint The point to populate.
- * @return True of such a point exists.
- */
- boolean computeClickPointInScreenForAccessibility(Region interactiveRegion,
- Point outPoint) {
- // Since the interactive portion of the view is a region but as a view
- // may have a transformation matrix which cannot be applied to a
- // region we compute the view bounds rectangle and all interactive
- // predecessor's and sibling's (siblings of predecessors included)
- // rectangles that intersect the view bounds. At the
- // end if the view was partially covered by another interactive
- // view we compute the view's interactive region and pick a point
- // on its boundary path as regions do not offer APIs to get inner
- // points. Note that the the code is optimized to fail early and
- // avoid unnecessary allocations plus computations.
-
- // The current approach has edge cases that may produce false
- // positives or false negatives. For example, a portion of the
- // view may be covered by an interactive descendant of a
- // predecessor, which we do not compute. Also a view may be handling
- // raw touch events instead registering click listeners, which
- // we cannot compute. Despite these limitations this approach will
- // work most of the time and it is a huge improvement over just
- // blindly sending the down and up events in the center of the
- // view.
-
- // Cannot click on an unattached view.
- if (mAttachInfo == null) {
- return false;
- }
-
- // Attached to an invisible window means this view is not visible.
- if (mAttachInfo.mWindowVisibility != View.VISIBLE) {
- return false;
- }
-
- RectF bounds = mAttachInfo.mTmpTransformRect;
- bounds.set(0, 0, getWidth(), getHeight());
- List<RectF> intersections = mAttachInfo.mTmpRectList;
- intersections.clear();
-
- if (mParent instanceof ViewGroup) {
- ViewGroup parentGroup = (ViewGroup) mParent;
- if (!parentGroup.translateBoundsAndIntersectionsInWindowCoordinates(
- this, bounds, intersections)) {
- intersections.clear();
- return false;
- }
- }
-
- // Take into account the window location.
- final int dx = mAttachInfo.mWindowLeft;
- final int dy = mAttachInfo.mWindowTop;
- bounds.offset(dx, dy);
- offsetRects(intersections, dx, dy);
-
- if (intersections.isEmpty() && interactiveRegion == null) {
- outPoint.set((int) bounds.centerX(), (int) bounds.centerY());
- } else {
- // This view is partially covered by other views, then compute
- // the not covered region and pick a point on its boundary.
- Region region = new Region();
- region.set((int) bounds.left, (int) bounds.top,
- (int) bounds.right, (int) bounds.bottom);
-
- final int intersectionCount = intersections.size();
- for (int i = intersectionCount - 1; i >= 0; i--) {
- RectF intersection = intersections.remove(i);
- region.op((int) intersection.left, (int) intersection.top,
- (int) intersection.right, (int) intersection.bottom,
- Region.Op.DIFFERENCE);
- }
-
- // If the view is completely covered, done.
- if (region.isEmpty()) {
- return false;
- }
-
- // Take into account the interactive portion of the window
- // as the rest is covered by other windows. If no such a region
- // then the whole window is interactive.
- if (interactiveRegion != null) {
- region.op(interactiveRegion, Region.Op.INTERSECT);
- }
-
- // Take into account the window bounds.
- final View root = getRootView();
- if (root != null) {
- region.op(dx, dy, root.getWidth() + dx, root.getHeight() + dy, Region.Op.INTERSECT);
- }
-
- // If the view is completely covered, done.
- if (region.isEmpty()) {
- return false;
- }
-
- // Try a shortcut here.
- if (region.isRect()) {
- Rect regionBounds = mAttachInfo.mTmpInvalRect;
- region.getBounds(regionBounds);
- outPoint.set(regionBounds.centerX(), regionBounds.centerY());
- return true;
- }
-
- // Get the a point on the region boundary path.
- Path path = region.getBoundaryPath();
- PathMeasure pathMeasure = new PathMeasure(path, false);
- final float[] coordinates = mAttachInfo.mTmpTransformLocation;
-
- // Without loss of generality pick a point.
- final float point = pathMeasure.getLength() * 0.01f;
- if (!pathMeasure.getPosTan(point, coordinates, null)) {
- return false;
- }
-
- outPoint.set(Math.round(coordinates[0]), Math.round(coordinates[1]));
- }
-
- return true;
- }
-
- static void offsetRects(List<RectF> rects, float offsetX, float offsetY) {
- final int rectCount = rects.size();
- for (int i = 0; i < rectCount; i++) {
- RectF intersection = rects.get(i);
- intersection.offset(offsetX, offsetY);
- }
- }
-
- /**
* Returns the delegate for implementing accessibility support via
* composition. For more details see {@link AccessibilityDelegate}.
*
@@ -8555,6 +8437,17 @@
* @return True if the event was handled by the view, false otherwise.
*/
public boolean dispatchTouchEvent(MotionEvent event) {
+ // If the event should be handled by accessibility focus first.
+ if (event.isTargetAccessibilityFocus()) {
+ // We don't have focus or no virtual descendant has it, do not handle the event.
+ if (!isAccessibilityFocused() && !(getViewRootImpl() != null && getViewRootImpl()
+ .getAccessibilityFocusedHost() == this)) {
+ return false;
+ }
+ // We have focus and got the event, then use normal event dispatch.
+ event.setTargetAccessibilityFocus(false);
+ }
+
boolean result = false;
if (mInputEventConsistencyVerifier != null) {
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 0b1a2d4..fd50c4d 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -474,9 +474,6 @@
@ViewDebug.ExportedProperty(category = "layout")
private int mChildCountWithTransientState = 0;
- // Iterator over the children in decreasing Z order (top children first).
- private OrderedChildIterator mOrderedChildIterator;
-
/**
* Currently registered axes for nested scrolling. Flag set consisting of
* {@link #SCROLL_AXIS_HORIZONTAL} {@link #SCROLL_AXIS_VERTICAL} or {@link #SCROLL_AXIS_NONE}
@@ -780,144 +777,6 @@
}
/**
- * Translates the given bounds and intersections from child coordinates to
- * local coordinates. In case any interactive sibling of the calling child
- * covers the latter, a new intersections is added to the intersection list.
- * This method is for the exclusive use by the accessibility layer to compute
- * a point where a sequence of down and up events would click on a view.
- *
- * @param child The child making the call.
- * @param bounds The bounds to translate in child coordinates.
- * @param intersections The intersections of interactive views covering the child.
- * @return True if the bounds and intersections were computed, false otherwise.
- */
- boolean translateBoundsAndIntersectionsInWindowCoordinates(View child,
- RectF bounds, List<RectF> intersections) {
- // Not attached, done.
- if (mAttachInfo == null) {
- return false;
- }
-
- if (getAlpha() <= 0 || getTransitionAlpha() <= 0 ||
- getVisibility() != VISIBLE) {
- // Cannot click on a view with an invisible predecessor.
- return false;
- }
-
- // Compensate for the child transformation.
- if (!child.hasIdentityMatrix()) {
- Matrix matrix = child.getMatrix();
- matrix.mapRect(bounds);
- final int intersectionCount = intersections.size();
- for (int i = 0; i < intersectionCount; i++) {
- RectF intersection = intersections.get(i);
- matrix.mapRect(intersection);
- }
- }
-
- // Translate the bounds from child to parent coordinates.
- final int dx = child.mLeft - mScrollX;
- final int dy = child.mTop - mScrollY;
- bounds.offset(dx, dy);
- offsetRects(intersections, dx, dy);
-
- // If the bounds do not intersect our bounds, done.
- if (!bounds.intersects(0, 0, getWidth(), getHeight())) {
- return false;
- }
-
- // Clip the bounds by our bounds.
- bounds.left = Math.max(bounds.left, 0);
- bounds.top = Math.max(bounds.top, 0);
- bounds.right = Math.min(bounds.right, getWidth());
- bounds.bottom = Math.min(bounds.bottom, getHeight());
-
- Iterator<View> iterator = obtainOrderedChildIterator();
- while (iterator.hasNext()) {
- View sibling = iterator.next();
-
- // We care only about siblings over the child.
- if (sibling == child) {
- break;
- }
-
- // Ignore invisible views as they are not interactive.
- if (!isVisible(sibling)) {
- continue;
- }
-
- // Compute the sibling bounds in its coordinates.
- RectF siblingBounds = mAttachInfo.mTmpTransformRect1;
- siblingBounds.set(0, 0, sibling.getWidth(), sibling.getHeight());
-
- // Translate the sibling bounds to our coordinates.
- offsetChildRectToMyCoords(siblingBounds, sibling);
-
- // Compute the intersection between the child and the sibling.
- if (siblingBounds.intersect(bounds)) {
- // Conservatively we consider an overlapping sibling to be
- // interactive and ignore it. This is not ideal as if the
- // sibling completely covers the view despite handling no
- // touch events we will not be able to click on the view.
- intersections.add(siblingBounds);
- }
- }
-
- releaseOrderedChildIterator();
-
- if (mParent instanceof ViewGroup) {
- ViewGroup parentGroup = (ViewGroup) mParent;
- return parentGroup.translateBoundsAndIntersectionsInWindowCoordinates(
- this, bounds, intersections);
- }
-
- return true;
- }
-
- private void offsetChildRectToMyCoords(RectF rect, View child) {
- if (!child.hasIdentityMatrix()) {
- child.getMatrix().mapRect(rect);
- }
- final int childDx = child.mLeft - mScrollX;
- final int childDy = child.mTop - mScrollY;
- rect.offset(childDx, childDy);
- }
-
- private static boolean isVisible(View view) {
- return (view.getAlpha() > 0 && view.getTransitionAlpha() > 0 &&
- view.getVisibility() == VISIBLE);
- }
-
- /**
- * Obtains the iterator to traverse the children in a descending Z order.
- * Only one party can use the iterator at any given time and you cannot
- * modify the children while using this iterator. Acquisition if already
- * obtained is an error.
- *
- * @return The child iterator.
- */
- OrderedChildIterator obtainOrderedChildIterator() {
- if (mOrderedChildIterator == null) {
- mOrderedChildIterator = new OrderedChildIterator();
- } else if (mOrderedChildIterator.isInitialized()) {
- throw new IllegalStateException("Already obtained");
- }
- mOrderedChildIterator.initialize();
- return mOrderedChildIterator;
- }
-
- /**
- * Releases the iterator to traverse the children in a descending Z order.
- * Release if not obtained is an error.
- */
- void releaseOrderedChildIterator() {
- if (mOrderedChildIterator == null || !mOrderedChildIterator.isInitialized()) {
- throw new IllegalStateException("Not obtained");
- }
- mOrderedChildIterator.release();
- }
-
- /**
* Called when a child view has changed whether or not it is tracking transient state.
*/
public void childHasTransientStateChanged(View child, boolean childHasTransientState) {
@@ -2072,6 +1931,9 @@
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
+ // Whether this event should be handled by the accessibility focus first.
+ final boolean targetAccessibilityFocus = ev.isTargetAccessibilityFocus();
+
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
@@ -2088,19 +1950,24 @@
// Check for interception.
final boolean intercepted;
- if (actionMasked == MotionEvent.ACTION_DOWN
- || mFirstTouchTarget != null) {
- final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
- if (!disallowIntercept) {
- intercepted = onInterceptTouchEvent(ev);
- ev.setAction(action); // restore action in case it was changed
+ if (!targetAccessibilityFocus) {
+ if (actionMasked == MotionEvent.ACTION_DOWN
+ || mFirstTouchTarget != null) {
+ final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
+ if (!disallowIntercept) {
+ intercepted = onInterceptTouchEvent(ev);
+ ev.setAction(action); // restore action in case it was changed
+ } else {
+ intercepted = false;
+ }
} else {
- intercepted = false;
+ // There are no touch targets and this action is not an initial down
+ // so this view group continues to intercept touches.
+ intercepted = true;
}
} else {
- // There are no touch targets and this action is not an initial down
- // so this view group continues to intercept touches.
- intercepted = true;
+ // If event should reach the accessibility focus first, do not intercept it.
+ intercepted = false;
}
// Check for cancelation.
@@ -2114,7 +1981,8 @@
if (!canceled && !intercepted) {
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
- || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
+ || actionMasked == MotionEvent.ACTION_HOVER_MOVE
+ || targetAccessibilityFocus) {
final int actionIndex = ev.getActionIndex(); // always 0 for down
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
@@ -7403,57 +7271,4 @@
canvas.drawLines(sDebugLines, paint);
}
-
- private final class OrderedChildIterator implements Iterator<View> {
- private List<View> mOrderedChildList;
- private boolean mUseCustomOrder;
- private int mCurrentIndex;
- private boolean mInitialized;
-
- public void initialize() {
- mOrderedChildList = buildOrderedChildList();
- mUseCustomOrder = (mOrderedChildList == null)
- && isChildrenDrawingOrderEnabled();
- mCurrentIndex = mChildrenCount - 1;
- mInitialized = true;
- }
-
- public void release() {
- if (mOrderedChildList != null) {
- mOrderedChildList.clear();
- }
- mUseCustomOrder = false;
- mCurrentIndex = 0;
- mInitialized = false;
- }
-
- public boolean isInitialized() {
- return mInitialized;
- }
-
- @Override
- public boolean hasNext() {
- return (mCurrentIndex >= 0);
- }
-
- @Override
- public View next() {
- if (!hasNext()) {
- throw new NoSuchElementException("No such element");
- }
- return getChild(mCurrentIndex--);
- }
-
- private View getChild(int index) {
- final int childIndex = mUseCustomOrder
- ? getChildDrawingOrder(mChildrenCount, index) : index;
- return (mOrderedChildList == null)
- ? mChildren[childIndex] : mOrderedChildList.get(childIndex);
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
- }
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 87d9a58..e4d82b1 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2703,7 +2703,7 @@
final AccessibilityNodeProvider provider = host.getAccessibilityNodeProvider();
if (provider == null) {
- host.getBoundsOnScreen(bounds);
+ host.getBoundsOnScreen(bounds, true);
} else if (mAccessibilityFocusedVirtualView != null) {
mAccessibilityFocusedVirtualView.getBoundsInScreen(bounds);
} else {
@@ -6835,26 +6835,6 @@
}
@Override
- public void computeClickPointInScreen(long accessibilityNodeId, Region interactiveRegion,
- int interactionId, IAccessibilityInteractionConnectionCallback callback,
- int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
- ViewRootImpl viewRootImpl = mViewRootImpl.get();
- if (viewRootImpl != null && viewRootImpl.mView != null) {
- viewRootImpl.getAccessibilityInteractionController()
- .computeClickPointInScreenClientThread(accessibilityNodeId,
- interactiveRegion, interactionId, callback, interrogatingPid,
- interrogatingTid, spec);
- } else {
- // We cannot make the call and notify the caller so it does not wait.
- try {
- callback.setComputeClickPointInScreenActionResult(null, interactionId);
- } catch (RemoteException re) {
- /* best effort - ignore */
- }
- }
- }
-
- @Override
public void findAccessibilityNodeInfosByViewId(long accessibilityNodeId,
String viewId, Region interactiveRegion, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags,
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 374f7e0..cefd34d 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -99,8 +99,6 @@
private boolean mPerformAccessibilityActionResult;
- private Point mComputeClickPointResult;
-
private Message mSameThreadMessage;
private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache =
@@ -522,43 +520,6 @@
return false;
}
- /**
- * Computes a point in screen coordinates where sending a down/up events would
- * perform a click on an {@link AccessibilityNodeInfo}.
- *
- * @param connectionId The id of a connection for interacting with the system.
- * @param accessibilityWindowId A unique window id. Use
- * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
- * to query the currently active window.
- * @param accessibilityNodeId A unique view id or virtual descendant id from
- * where to start the search. Use
- * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
- * to start from the root.
- * @return Point the click point of null if no such point.
- */
- public Point computeClickPointInScreen(int connectionId, int accessibilityWindowId,
- long accessibilityNodeId) {
- try {
- IAccessibilityServiceConnection connection = getConnection(connectionId);
- if (connection != null) {
- final int interactionId = mInteractionIdCounter.getAndIncrement();
- final boolean success = connection.computeClickPointInScreen(
- accessibilityWindowId, accessibilityNodeId,
- interactionId, this, Thread.currentThread().getId());
- if (success) {
- return getComputeClickPointInScreenResultAndClear(interactionId);
- }
- } else {
- if (DEBUG) {
- Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
- }
- }
- } catch (RemoteException re) {
- Log.w(LOG_TAG, "Error while calling remote computeClickPointInScreen", re);
- }
- return null;
- }
-
public void clearCache() {
sAccessibilityCache.clear();
}
@@ -674,34 +635,6 @@
}
/**
- * Gets the result of a request to compute a point in screen for clicking on a node.
- *
- * @param interactionId The interaction id to match the result with the request.
- * @return The point or null if no such point.
- */
- private Point getComputeClickPointInScreenResultAndClear(int interactionId) {
- synchronized (mInstanceLock) {
- final boolean success = waitForResultTimedLocked(interactionId);
- Point result = success ? mComputeClickPointResult : null;
- clearResultLocked();
- return result;
- }
- }
-
- /**
- * {@inheritDoc}
- */
- public void setComputeClickPointInScreenActionResult(Point point, int interactionId) {
- synchronized (mInstanceLock) {
- if (interactionId > mInteractionId) {
- mComputeClickPointResult = point;
- mInteractionId = interactionId;
- }
- mInstanceLock.notifyAll();
- }
- }
-
- /**
* Clears the result state.
*/
private void clearResultLocked() {
@@ -709,7 +642,6 @@
mFindAccessibilityNodeInfoResult = null;
mFindAccessibilityNodeInfosResult = null;
mPerformAccessibilityActionResult = false;
- mComputeClickPointResult = null;
}
/**
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
index 66a3f46..cecc4af 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
@@ -54,8 +54,4 @@
void performAccessibilityAction(long accessibilityNodeId, int action, in Bundle arguments,
int interactionId, IAccessibilityInteractionConnectionCallback callback, int flags,
int interrogatingPid, long interrogatingTid);
-
- void computeClickPointInScreen(long accessibilityNodeId, in Region bounds, int interactionId,
- IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
- long interrogatingTid, in MagnificationSpec spec);
}
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
index f480216..42ae1b3 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
@@ -52,12 +52,4 @@
* @param interactionId The interaction id to match the result with the request.
*/
void setPerformAccessibilityActionResult(boolean succeeded, int interactionId);
-
- /**
- * Sets the result of a request to compute a point for clicking in a view.
- *
- * @param point The point of null if no such point.
- * @param interactionId The interaction id to match the result with the request.
- */
- void setComputeClickPointInScreenActionResult(in Point point, int interactionId);
}
diff --git a/docs/html/about/dashboards/index.jd b/docs/html/about/dashboards/index.jd
index 5265f20..4e03108 100644
--- a/docs/html/about/dashboards/index.jd
+++ b/docs/html/about/dashboards/index.jd
@@ -57,7 +57,7 @@
</div>
-<p style="clear:both"><em>Data collected during a 7-day period ending on January 5, 2015.
+<p style="clear:both"><em>Data collected during a 7-day period ending on February 2, 2015.
<br/>Any versions with less than 0.1% distribution are not shown.</em>
</p>
@@ -88,7 +88,7 @@
</div>
-<p style="clear:both"><em>Data collected during a 7-day period ending on January 5, 2015.
+<p style="clear:both"><em>Data collected during a 7-day period ending on February 2, 2015.
<br/>Any screen configurations with less than 0.1% distribution are not shown.</em></p>
@@ -108,7 +108,7 @@
<img alt="" style="float:right"
-src="//chart.googleapis.com/chart?chl=GL%202.0%7CGL%203.0&chf=bg%2Cs%2C00000000&chd=t%3A69.9%2C30.1&chco=c4df9b%2C6fad0c&cht=p&chs=400x250" />
+src="//chart.googleapis.com/chart?chl=GL%202.0%7CGL%203.0&chf=bg%2Cs%2C00000000&chd=t%3A68.9%2C31.1&chco=c4df9b%2C6fad0c&cht=p&chs=400x250" />
<p>To declare which version of OpenGL ES your application requires, you should use the {@code
@@ -127,17 +127,17 @@
</tr>
<tr>
<td>2.0</td>
-<td>69.9%</td>
+<td>68.9%</td>
</tr>
<tr>
<td>3.0</td>
-<td>30.1%</td>
+<td>31.1%</td>
</tr>
</table>
-<p style="clear:both"><em>Data collected during a 7-day period ending on January 5, 2015</em></p>
+<p style="clear:both"><em>Data collected during a 7-day period ending on February 2, 2015</em></p>
@@ -155,7 +155,7 @@
var VERSION_DATA =
[
{
- "chart": "//chart.googleapis.com/chart?cht=p&chs=500x250&chco=c4df9b%2C6fad0c&chd=t%3A0.4%2C7.8%2C6.7%2C46.0%2C39.1&chf=bg%2Cs%2C00000000&chl=Froyo%7CGingerbread%7CIce%20Cream%20Sandwich%7CJelly%20Bean%7CKitKat",
+ "chart": "//chart.googleapis.com/chart?chf=bg%2Cs%2C00000000&chd=t%3A0.4%2C7.4%2C6.4%2C44.5%2C39.7%2C1.6&chco=c4df9b%2C6fad0c&chl=Froyo%7CGingerbread%7CIce%20Cream%20Sandwich%7CJelly%20Bean%7CKitKat%7CLollipop&chs=500x250&cht=p",
"data": [
{
"api": 8,
@@ -165,32 +165,37 @@
{
"api": 10,
"name": "Gingerbread",
- "perc": "7.8"
+ "perc": "7.4"
},
{
"api": 15,
"name": "Ice Cream Sandwich",
- "perc": "6.7"
+ "perc": "6.4"
},
{
"api": 16,
"name": "Jelly Bean",
- "perc": "19.2"
+ "perc": "18.4"
},
{
"api": 17,
"name": "Jelly Bean",
- "perc": "20.3"
+ "perc": "19.8"
},
{
"api": 18,
"name": "Jelly Bean",
- "perc": "6.5"
+ "perc": "6.3"
},
{
"api": 19,
"name": "KitKat",
- "perc": "39.1"
+ "perc": "39.7"
+ },
+ {
+ "api": 21,
+ "name": "Lollipop",
+ "perc": "1.6"
}
]
}
@@ -203,29 +208,29 @@
"data": {
"Large": {
"hdpi": "0.6",
- "ldpi": "0.6",
- "mdpi": "5.4",
- "tvdpi": "2.3",
+ "ldpi": "0.5",
+ "mdpi": "5.1",
+ "tvdpi": "2.2",
"xhdpi": "0.6"
},
"Normal": {
- "hdpi": "37.5",
- "mdpi": "8.8",
+ "hdpi": "38.3",
+ "mdpi": "8.7",
"tvdpi": "0.1",
- "xhdpi": "18.4",
- "xxhdpi": "16.3"
+ "xhdpi": "18.8",
+ "xxhdpi": "15.9"
},
"Small": {
"ldpi": "4.8"
},
"Xlarge": {
"hdpi": "0.3",
- "mdpi": "3.7",
+ "mdpi": "3.5",
"xhdpi": "0.6"
}
},
- "densitychart": "//chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b%2C6fad0c&chd=t%3A5.4%2C17.9%2C2.4%2C38.4%2C19.6%2C16.3&chf=bg%2Cs%2C00000000&chl=ldpi%7Cmdpi%7Ctvdpi%7Chdpi%7Cxhdpi%7Cxxhdpi",
- "layoutchart": "//chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b%2C6fad0c&chd=t%3A4.6%2C9.5%2C81.1%2C4.8&chf=bg%2Cs%2C00000000&chl=Xlarge%7CLarge%7CNormal%7CSmall"
+ "densitychart": "//chart.googleapis.com/chart?chf=bg%2Cs%2C00000000&chd=t%3A5.3%2C17.3%2C2.3%2C39.2%2C20.0%2C15.9&chco=c4df9b%2C6fad0c&chl=ldpi%7Cmdpi%7Ctvdpi%7Chdpi%7Cxhdpi%7Cxxhdpi&chs=400x250&cht=p",
+ "layoutchart": "//chart.googleapis.com/chart?chf=bg%2Cs%2C00000000&chd=t%3A4.4%2C9.0%2C81.8%2C4.8&chco=c4df9b%2C6fad0c&chl=Xlarge%7CLarge%7CNormal%7CSmall&chs=400x250&cht=p"
}
];
@@ -305,7 +310,7 @@
},
{
"api":21,
- "link":"<a href='/about/versions/android-5.0.html'>4.4</a>",
+ "link":"<a href='/about/versions/android-5.0.html'>5.0</a>",
"codename":"Lollipop"
}
];
diff --git a/docs/html/training/basics/intents/sending.jd b/docs/html/training/basics/intents/sending.jd
index 4698ba1..b9463e4 100644
--- a/docs/html/training/basics/intents/sending.jd
+++ b/docs/html/training/basics/intents/sending.jd
@@ -153,7 +153,8 @@
<pre>
PackageManager packageManager = {@link android.content.Context#getPackageManager()};
-List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
+List<ResolveInfo> activities = packageManager.queryIntentActivities(intent,
+ PackageManager.MATCH_DEFAULT_ONLY);
boolean isIntentSafe = activities.size() > 0;
</pre>
diff --git a/packages/Keyguard/res/layout/keyguard_simple_host_view.xml b/packages/Keyguard/res/layout/keyguard_simple_host_view.xml
index 28ce265..63e55d5 100644
--- a/packages/Keyguard/res/layout/keyguard_simple_host_view.xml
+++ b/packages/Keyguard/res/layout/keyguard_simple_host_view.xml
@@ -26,7 +26,9 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
- android:clipToPadding="false">
+ android:clipToPadding="false"
+ android:importantForAccessibility="yes"> <!-- Needed because TYPE_WINDOW_STATE_CHANGED is sent
+ from this view when bouncer is shown -->
<com.android.keyguard.KeyguardSecurityContainer
android:id="@+id/keyguard_security_container"
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
index faecaed..559b112 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -121,6 +121,14 @@
}
}
+ public CharSequence getCurrentSecurityModeContentDescription() {
+ View v = (View) getSecurityView(mCurrentSecuritySelection);
+ if (v != null) {
+ return v.getContentDescription();
+ }
+ return "";
+ }
+
private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
KeyguardSecurityView view = null;
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
index e1e1cfc..fc17909 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
@@ -38,6 +38,7 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
import com.android.internal.widget.LockPatternUtils;
@@ -151,6 +152,16 @@
mSecurityContainer.announceCurrentSecurityMethod();
}
+ @Override
+ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
+ event.getText().add(mSecurityContainer.getCurrentSecurityModeContentDescription());
+ return true;
+ } else {
+ return super.dispatchPopulateAccessibilityEvent(event);
+ }
+ }
+
protected KeyguardSecurityContainer getSecurityContainer() {
return mSecurityContainer;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 5bc1321..d0fe32e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -22,6 +22,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardViewBase;
@@ -86,6 +87,7 @@
mKeyguardView.onResume();
mKeyguardView.startAppearAnimation();
mShowingSoon = false;
+ mKeyguardView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
};
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index bbf3644..93c65f3 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -170,6 +170,8 @@
private final Rect mTempRect = new Rect();
+ private final Rect mTempRect1 = new Rect();
+
private final Point mTempPoint = new Point();
private final PackageManager mPackageManager;
@@ -2533,57 +2535,6 @@
}
@Override
- public boolean computeClickPointInScreen(int accessibilityWindowId,
- long accessibilityNodeId, int interactionId,
- IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
- throws RemoteException {
- final int resolvedWindowId;
- IAccessibilityInteractionConnection connection = null;
- Region partialInteractiveRegion = mTempRegion;
- synchronized (mLock) {
- // We treat calls from a profile as if made by its parent as profiles
- // share the accessibility state of the parent. The call below
- // performs the current profile parent resolution.
- final int resolvedUserId = mSecurityPolicy
- .resolveCallingUserIdEnforcingPermissionsLocked(
- UserHandle.USER_CURRENT);
- if (resolvedUserId != mCurrentUserId) {
- return false;
- }
- resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
- final boolean permissionGranted =
- mSecurityPolicy.canRetrieveWindowContentLocked(this);
- if (!permissionGranted) {
- return false;
- } else {
- connection = getConnectionLocked(resolvedWindowId);
- if (connection == null) {
- return false;
- }
- }
- if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
- resolvedWindowId, partialInteractiveRegion)) {
- partialInteractiveRegion = null;
- }
- }
- final int interrogatingPid = Binder.getCallingPid();
- final long identityToken = Binder.clearCallingIdentity();
- MagnificationSpec spec = getCompatibleMagnificationSpecLocked(resolvedWindowId);
- try {
- connection.computeClickPointInScreen(accessibilityNodeId, partialInteractiveRegion,
- interactionId, callback, interrogatingPid, interrogatingTid, spec);
- return true;
- } catch (RemoteException re) {
- if (DEBUG) {
- Slog.e(LOG_TAG, "Error computeClickPointInScreen().");
- }
- } finally {
- Binder.restoreCallingIdentity(identityToken);
- }
- return false;
- }
-
- @Override
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
mSecurityPolicy.enforceCallingPermission(Manifest.permission.DUMP, FUNCTION_DUMP);
synchronized (mLock) {
@@ -3236,38 +3187,36 @@
}
synchronized (mLock) {
- Point point = mClient.computeClickPointInScreen(mConnectionId,
- focus.getWindowId(), focus.getSourceNodeId());
+ Rect boundsInScreen = mTempRect;
+ focus.getBoundsInScreen(boundsInScreen);
- if (point == null) {
+ // Clip to the window bounds.
+ Rect windowBounds = mTempRect1;
+ getWindowBounds(focus.getWindowId(), windowBounds);
+ boundsInScreen.intersect(windowBounds);
+ if (boundsInScreen.isEmpty()) {
return false;
}
+ // Apply magnification if needed.
MagnificationSpec spec = getCompatibleMagnificationSpecLocked(focus.getWindowId());
if (spec != null && !spec.isNop()) {
- point.offset((int) -spec.offsetX, (int) -spec.offsetY);
- point.x = (int) (point.x * (1 / spec.scale));
- point.y = (int) (point.y * (1 / spec.scale));
+ boundsInScreen.offset((int) -spec.offsetX, (int) -spec.offsetY);
+ boundsInScreen.scale(1 / spec.scale);
}
- // Make sure the point is within the window.
- Rect windowBounds = mTempRect;
- getWindowBounds(focus.getWindowId(), windowBounds);
- if (!windowBounds.contains(point.x, point.y)) {
- return false;
- }
-
- // Make sure the point is within the screen.
+ // Clip to the screen bounds.
Point screenSize = mTempPoint;
mDefaultDisplay.getRealSize(screenSize);
- if (point.x < 0 || point.x > screenSize.x
- || point.y < 0 || point.y > screenSize.y) {
+ boundsInScreen.intersect(0, 0, screenSize.x, screenSize.y);
+ if (boundsInScreen.isEmpty()) {
return false;
}
- outPoint.set(point.x, point.y);
- return true;
+ outPoint.set(boundsInScreen.centerX(), boundsInScreen.centerY());
}
+
+ return true;
}
private AccessibilityNodeInfo getAccessibilityFocusNotLocked() {
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index b9ed89b..f18b5ef 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -77,6 +77,10 @@
private static final int STATE_DELEGATING = 0x00000004;
private static final int STATE_GESTURE_DETECTING = 0x00000005;
+ private static final int CLICK_LOCATION_NONE = 0;
+ private static final int CLICK_LOCATION_ACCESSIBILITY_FOCUS = 1;
+ private static final int CLICK_LOCATION_LAST_TOUCH_EXPLORED = 2;
+
// The maximum of the cosine between the vectors of two moving
// pointers so they can be considered moving in the same direction.
private static final float MAX_DRAGGING_ANGLE_COS = 0.525321989f; // cos(pi/4)
@@ -942,12 +946,16 @@
*
* @param prototype The prototype from which to create the injected events.
* @param policyFlags The policy flags associated with the event.
+ * @param targetAccessibilityFocus Whether the event targets the accessibility focus.
*/
- private void sendActionDownAndUp(MotionEvent prototype, int policyFlags) {
+ private void sendActionDownAndUp(MotionEvent prototype, int policyFlags,
+ boolean targetAccessibilityFocus) {
// Tap with the pointer that last explored.
final int pointerId = prototype.getPointerId(prototype.getActionIndex());
final int pointerIdBits = (1 << pointerId);
+ prototype.setTargetAccessibilityFocus(targetAccessibilityFocus);
sendMotionEvent(prototype, MotionEvent.ACTION_DOWN, pointerIdBits, policyFlags);
+ prototype.setTargetAccessibilityFocus(targetAccessibilityFocus);
sendMotionEvent(prototype, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
}
@@ -1155,7 +1163,8 @@
final int pointerIndex = secondTapUp.findPointerIndex(pointerId);
Point clickLocation = mTempPoint;
- if (!computeClickLocation(clickLocation)) {
+ final int result = computeClickLocation(clickLocation);
+ if (result == CLICK_LOCATION_NONE) {
return;
}
@@ -1171,7 +1180,8 @@
secondTapUp.getEventTime(), MotionEvent.ACTION_DOWN, 1, properties,
coords, 0, 0, 1.0f, 1.0f, secondTapUp.getDeviceId(), 0,
secondTapUp.getSource(), secondTapUp.getFlags());
- sendActionDownAndUp(event, policyFlags);
+ final boolean targetAccessibilityFocus = (result == CLICK_LOCATION_ACCESSIBILITY_FOCUS);
+ sendActionDownAndUp(event, policyFlags, targetAccessibilityFocus);
event.recycle();
}
@@ -1216,7 +1226,7 @@
MAX_DRAGGING_ANGLE_COS);
}
- private boolean computeClickLocation(Point outLocation) {
+ private int computeClickLocation(Point outLocation) {
MotionEvent lastExploreEvent = mInjectedPointerTracker.getLastInjectedHoverEventForClick();
if (lastExploreEvent != null) {
final int lastExplorePointerIndex = lastExploreEvent.getActionIndex();
@@ -1224,14 +1234,17 @@
outLocation.y = (int) lastExploreEvent.getY(lastExplorePointerIndex);
if (!mAms.accessibilityFocusOnlyInActiveWindow()
|| mLastTouchedWindowId == mAms.getActiveWindowId()) {
- mAms.getAccessibilityFocusClickPointInScreen(outLocation);
+ if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) {
+ return CLICK_LOCATION_ACCESSIBILITY_FOCUS;
+ } else {
+ return CLICK_LOCATION_LAST_TOUCH_EXPLORED;
+ }
}
- return true;
}
if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) {
- return true;
+ return CLICK_LOCATION_ACCESSIBILITY_FOCUS;
}
- return false;
+ return CLICK_LOCATION_NONE;
}
/**
@@ -1310,14 +1323,13 @@
return;
}
- int clickLocationX;
- int clickLocationY;
-
final int pointerId = mEvent.getPointerId(mEvent.getActionIndex());
final int pointerIndex = mEvent.findPointerIndex(pointerId);
Point clickLocation = mTempPoint;
- if (!computeClickLocation(clickLocation)) {
+ final int result = computeClickLocation(clickLocation);
+
+ if (result == CLICK_LOCATION_NONE) {
return;
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index f900d0d..87f78c1 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -28,6 +28,7 @@
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkRequest;
+import android.net.ProxyInfo;
import android.net.TrafficStats;
import android.net.Uri;
import android.net.wifi.WifiInfo;
@@ -656,12 +657,36 @@
int httpResponseCode = 599;
try {
URL url = new URL("http", mServer, "/generate_204");
+ // On networks with a PAC instead of fetching a URL that should result in a 204
+ // reponse, we instead simply fetch the PAC script. This is done for a few reasons:
+ // 1. At present our PAC code does not yet handle multiple PACs on multiple networks
+ // until something like https://android-review.googlesource.com/#/c/115180/ lands.
+ // Network.openConnection() will ignore network-specific PACs and instead fetch
+ // using NO_PROXY. If a PAC is in place, the only fetch we know will succeed with
+ // NO_PROXY is the fetch of the PAC itself.
+ // 2. To proxy the generate_204 fetch through a PAC would require a number of things
+ // happen before the fetch can commence, namely:
+ // a) the PAC script be fetched
+ // b) a PAC script resolver service be fired up and resolve mServer
+ // Network validation could be delayed until these prerequisities are satisifed or
+ // could simply be left to race them. Neither is an optimal solution.
+ // 3. PAC scripts are sometimes used to block or restrict Internet access and may in
+ // fact block fetching of the generate_204 URL which would lead to false negative
+ // results for network validation.
+ boolean fetchPac = false;
+ {
+ final ProxyInfo proxyInfo = mNetworkAgentInfo.linkProperties.getHttpProxy();
+ if (proxyInfo != null && !Uri.EMPTY.equals(proxyInfo.getPacFileUrl())) {
+ url = new URL(proxyInfo.getPacFileUrl().toString());
+ fetchPac = true;
+ }
+ }
if (DBG) {
log("Checking " + url.toString() + " on " +
mNetworkAgentInfo.networkInfo.getExtraInfo());
}
urlConnection = (HttpURLConnection) mNetworkAgentInfo.network.openConnection(url);
- urlConnection.setInstanceFollowRedirects(false);
+ urlConnection.setInstanceFollowRedirects(fetchPac);
urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setUseCaches(false);
@@ -695,6 +720,11 @@
httpResponseCode = 204;
}
+ if (httpResponseCode == 200 && fetchPac) {
+ if (DBG) log("PAC fetch 200 response interpreted as 204 response.");
+ httpResponseCode = 204;
+ }
+
sendNetworkConditionsBroadcast(true /* response received */,
httpResponseCode != 204 /* isCaptivePortal */,
requestTimestamp, responseTimestamp);
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index a180f44..03fec01 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -260,6 +260,7 @@
/** @hide */
public void onConferenceParticipantsChanged(Connection c,
List<ConferenceParticipant> participants) {}
+ public void onConferenceStarted() {}
}
/** @hide */
@@ -1422,4 +1423,13 @@
l.onConferenceParticipantsChanged(this, conferenceParticipants);
}
}
+
+ /**
+ * Notifies listeners that a conference call has been started.
+ */
+ protected void notifyConferenceStarted() {
+ for (Listener l : mListeners) {
+ l.onConferenceStarted();
+ }
+ }
}
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
index a2bd6d7..18036927 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
@@ -614,15 +614,27 @@
int pos = value.indexOf('/');
String idName = value.substring(pos + 1);
+ boolean create = value.startsWith("@+");
+ boolean isFrameworkId =
+ mPlatformFile || value.startsWith("@android") || value.startsWith("@+android");
- // if this is a framework id
- if (mPlatformFile || value.startsWith("@android") || value.startsWith("@+android")) {
- // look for idName in the android R classes
- return mContext.getFrameworkResourceValue(ResourceType.ID, idName, defValue);
+ // Look for the idName in project or android R class depending on isPlatform.
+ if (create) {
+ Integer idValue;
+ if (isFrameworkId) {
+ idValue = Bridge.getResourceId(ResourceType.ID, idName);
+ } else {
+ idValue = mContext.getProjectCallback().getResourceId(ResourceType.ID, idName);
+ }
+ return idValue == null ? defValue : idValue;
}
-
- // look for idName in the project R class.
- return mContext.getProjectResourceValue(ResourceType.ID, idName, defValue);
+ // This calls the same method as in if(create), but doesn't create a dynamic id, if
+ // one is not found.
+ if (isFrameworkId) {
+ return mContext.getFrameworkResourceValue(ResourceType.ID, idName, defValue);
+ } else {
+ return mContext.getProjectResourceValue(ResourceType.ID, idName, defValue);
+ }
}
// not a direct id valid reference? resolve it
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 3953624..3441878 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
@@ -289,6 +289,11 @@
value = mRenderResources.resolveResValue(value);
}
+ if (value == null) {
+ // unable to find the attribute.
+ return false;
+ }
+
// check if this is a style resource
if (value instanceof StyleResourceValue) {
// get the id that will represent this style.
@@ -296,7 +301,6 @@
return true;
}
-
int a;
// if this is a framework value.
if (value.isFramework()) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
new file mode 100644
index 0000000..e5023b8
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2015 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.layoutlib.bridge.bars;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.SessionParams;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.impl.ResourceHelper;
+import com.android.resources.ResourceType;
+
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+
+/**
+ * Assumes that the AppCompat library is present in the project's classpath and creates an
+ * actionbar around it.
+ */
+public class AppCompatActionBar extends BridgeActionBar {
+
+ private Object mWindowDecorActionBar;
+ private static final String WINDOW_ACTION_BAR_CLASS = "android.support.v7.internal.app.WindowDecorActionBar";
+ private Class<?> mWindowActionBarClass;
+
+ /**
+ * Inflate the action bar and attach it to {@code parentView}
+ */
+ public AppCompatActionBar(@NonNull BridgeContext context, @NonNull SessionParams params,
+ @NonNull ViewGroup parentView) {
+ super(context, params, parentView);
+ int contentRootId = context.getProjectResourceValue(ResourceType.ID,
+ "action_bar_activity_content", 0);
+ View contentView = getDecorContent().findViewById(contentRootId);
+ if (contentView != null) {
+ assert contentView instanceof FrameLayout;
+ setContentRoot(((FrameLayout) contentView));
+ } else {
+ // Something went wrong. Create a new FrameLayout in the enclosing layout.
+ FrameLayout contentRoot = new FrameLayout(context);
+ setMatchParent(contentRoot);
+ mEnclosingLayout.addView(contentRoot);
+ setContentRoot(contentRoot);
+ }
+ try {
+ Class[] constructorParams = {View.class};
+ Object[] constructorArgs = {getDecorContent()};
+ mWindowDecorActionBar = params.getProjectCallback().loadView(WINDOW_ACTION_BAR_CLASS,
+ constructorParams, constructorArgs);
+
+ mWindowActionBarClass = mWindowDecorActionBar == null ? null :
+ mWindowDecorActionBar.getClass();
+ setupActionBar();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ protected ResourceValue getLayoutResource(BridgeContext context) {
+ // We always assume that the app has requested the action bar.
+ return context.getRenderResources().getProjectResource(ResourceType.LAYOUT,
+ "abc_screen_toolbar");
+ }
+
+ @Override
+ protected void setTitle(CharSequence title) {
+ if (title != null && mWindowDecorActionBar != null) {
+ Method setTitle = getMethod(mWindowActionBarClass, "setTitle", CharSequence.class);
+ invoke(setTitle, mWindowDecorActionBar, title);
+ }
+ }
+
+ @Override
+ protected void setSubtitle(CharSequence subtitle) {
+ if (subtitle != null && mWindowDecorActionBar != null) {
+ Method setSubtitle = getMethod(mWindowActionBarClass, "setSubtitle", CharSequence.class);
+ invoke(setSubtitle, mWindowDecorActionBar, subtitle);
+ }
+ }
+
+ @Override
+ protected void setIcon(String icon) {
+ // Do this only if the action bar doesn't already have an icon.
+ if (icon != null && !icon.isEmpty() && mWindowDecorActionBar != null) {
+ if (((Boolean) invoke(getMethod(mWindowActionBarClass, "hasIcon"), mWindowDecorActionBar)
+ )) {
+ Drawable iconDrawable = getDrawable(icon, false);
+ if (iconDrawable != null) {
+ Method setIcon = getMethod(mWindowActionBarClass, "setIcon", Drawable.class);
+ invoke(setIcon, mWindowDecorActionBar, iconDrawable);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void setHomeAsUp(boolean homeAsUp) {
+ if (mWindowDecorActionBar != null) {
+ Method setHomeAsUp = getMethod(mWindowActionBarClass,
+ "setDefaultDisplayHomeAsUpEnabled", boolean.class);
+ invoke(setHomeAsUp, mWindowDecorActionBar, homeAsUp);
+ }
+ }
+
+ @Override
+ public void createMenuPopup() {
+ // it's hard to addd menus to appcompat's actionbar, since it'll use a lot of reflection.
+ // so we skip it for now.
+ }
+
+ @Nullable
+ private static Method getMethod(Class<?> owner, String name, Class<?>... parameterTypes) {
+ try {
+ return owner == null ? null : owner.getMethod(name, parameterTypes);
+ } catch (NoSuchMethodException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ @Nullable
+ private static Object invoke(Method method, Object owner, Object... args) {
+ try {
+ return method == null ? null : method.invoke(owner, args);
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ // TODO: this is duplicated from FrameworkActionBarWrapper$WindowActionBarWrapper
+ @Nullable
+ private Drawable getDrawable(@NonNull String name, boolean isFramework) {
+ RenderResources res = mBridgeContext.getRenderResources();
+ ResourceValue value = res.findResValue(name, isFramework);
+ value = res.resolveResValue(value);
+ if (value != null) {
+ return ResourceHelper.getDrawable(value, mBridgeContext);
+ }
+ return null;
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/BridgeActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/BridgeActionBar.java
new file mode 100644
index 0000000..b29d25f
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/BridgeActionBar.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2015 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.layoutlib.bridge.bars;
+
+import com.android.annotations.NonNull;
+import com.android.ide.common.rendering.api.ActionBarCallback;
+import com.android.ide.common.rendering.api.ActionBarCallback.HomeButtonStyle;
+import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.SessionParams;
+import com.android.layoutlib.bridge.android.BridgeContext;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.FrameLayout;
+import android.widget.RelativeLayout;
+
+/**
+ * An abstraction over two implementations of the ActionBar - framework and appcompat.
+ */
+public abstract class BridgeActionBar {
+ // Store a reference to the context so that we don't have to cast it repeatedly.
+ @NonNull protected final BridgeContext mBridgeContext;
+ @NonNull protected final SessionParams mParams;
+ // A Layout that contains the inflated action bar. The menu popup is added to this layout.
+ @NonNull protected final ViewGroup mEnclosingLayout;
+
+ private final View mDecorContent;
+ private final ActionBarCallback mCallback;
+
+ @NonNull private FrameLayout mContentRoot;
+
+ public BridgeActionBar(@NonNull BridgeContext context, @NonNull SessionParams params,
+ @NonNull ViewGroup parentView) {
+ mBridgeContext = context;
+ mParams = params;
+ mCallback = params.getProjectCallback().getActionBarCallback();
+ ResourceValue layoutName = getLayoutResource(context);
+ if (layoutName == null) {
+ throw new RuntimeException("Unable to find the layout for Action Bar.");
+ }
+ int layoutId;
+ if (layoutName.isFramework()) {
+ layoutId = context.getFrameworkResourceValue(layoutName.getResourceType(),
+ layoutName.getName(), 0);
+ } else {
+ layoutId = context.getProjectResourceValue(layoutName.getResourceType(),
+ layoutName.getName(), 0);
+
+ }
+ if (layoutId == 0) {
+ throw new RuntimeException(
+ String.format("Unable to resolve attribute \"%1$s\" of type \"%2$s\"",
+ layoutName.getName(), layoutName.getResourceType()));
+ }
+ if (mCallback.isOverflowPopupNeeded()) {
+ // Create a RelativeLayout around the action bar, to which the overflow popup may be
+ // added.
+ mEnclosingLayout = new RelativeLayout(mBridgeContext);
+ setMatchParent(mEnclosingLayout);
+ parentView.addView(mEnclosingLayout);
+ } else {
+ mEnclosingLayout = parentView;
+ }
+
+ // Inflate action bar layout.
+ mDecorContent = LayoutInflater.from(context).inflate(layoutId, mEnclosingLayout, true);
+
+ }
+
+ /**
+ * Returns the Layout Resource that should be used to inflate the action bar. This layout
+ * should cover the complete screen, and have a FrameLayout included, where the content will
+ * be inflated.
+ */
+ protected abstract ResourceValue getLayoutResource(BridgeContext context);
+
+ protected void setContentRoot(FrameLayout contentRoot) {
+ mContentRoot = contentRoot;
+ }
+
+ @NonNull
+ public FrameLayout getContentRoot() {
+ return mContentRoot;
+ }
+
+ /**
+ * Returns the view inflated. This should contain both the ActionBar and the app content in it.
+ */
+ protected View getDecorContent() {
+ return mDecorContent;
+ }
+
+ /** Setup things like the title, subtitle, icon etc. */
+ protected void setupActionBar() {
+ setTitle();
+ setSutTitle();
+ setIcon();
+ setHomeAsUp(mCallback.getHomeButtonStyle() == HomeButtonStyle.SHOW_HOME_AS_UP);
+ }
+
+ protected abstract void setTitle(CharSequence title);
+ protected abstract void setSubtitle(CharSequence subtitle);
+ protected abstract void setIcon(String icon);
+ protected abstract void setHomeAsUp(boolean homeAsUp);
+
+ private void setTitle() {
+ RenderResources res = mBridgeContext.getRenderResources();
+
+ String title = mParams.getAppLabel();
+ ResourceValue titleValue = res.findResValue(title, false);
+ if (titleValue != null && titleValue.getValue() != null) {
+ setTitle(titleValue.getValue());
+ } else {
+ setTitle(title);
+ }
+ }
+
+ private void setSutTitle() {
+ String subTitle = mCallback.getSubTitle();
+ if (subTitle != null) {
+ setSubtitle(subTitle);
+ }
+ }
+
+ private void setIcon() {
+ String appIcon = mParams.getAppIcon();
+ if (appIcon != null) {
+ setIcon(appIcon);
+ }
+ }
+
+ public abstract void createMenuPopup();
+
+ public ActionBarCallback getCallBack() {
+ return mCallback;
+ }
+
+ protected static void setMatchParent(View view) {
+ view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT));
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBar.java
similarity index 76%
rename from tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
rename to tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBar.java
index 2ff8d37..a1c9065 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBar.java
@@ -31,7 +31,7 @@
import android.content.res.TypedArray;
import android.util.DisplayMetrics;
import android.util.TypedValue;
-import android.view.LayoutInflater;
+import android.view.InflateException;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
@@ -44,70 +44,30 @@
import java.util.ArrayList;
-public class ActionBarLayout {
+/**
+ * Creates the ActionBar as done by the framework.
+ */
+public class FrameworkActionBar extends BridgeActionBar {
private static final String LAYOUT_ATTR_NAME = "windowActionBarFullscreenDecorLayout";
// The Action Bar
- @NonNull
- private CustomActionBarWrapper mActionBar;
-
- // Store another reference to the context so that we don't have to cast it repeatedly.
- @NonNull
- private final BridgeContext mBridgeContext;
-
- @NonNull
- private FrameLayout mContentRoot;
+ @NonNull private FrameworkActionBarWrapper mActionBar;
// A fake parent for measuring views.
- @Nullable
- private ViewGroup mMeasureParent;
-
- // A Layout that contains the inflated action bar. The menu popup is added to this layout.
- @NonNull
- private final RelativeLayout mEnclosingLayout;
+ @Nullable private ViewGroup mMeasureParent;
/**
* Inflate the action bar and attach it to {@code parentView}
*/
- public ActionBarLayout(@NonNull BridgeContext context, @NonNull SessionParams params,
+ public FrameworkActionBar(@NonNull BridgeContext context, @NonNull SessionParams params,
@NonNull ViewGroup parentView) {
+ super(context, params, parentView);
- mBridgeContext = context;
+ View decorContent = getDecorContent();
- ResourceValue layoutName = context.getRenderResources()
- .findItemInTheme(LAYOUT_ATTR_NAME, true);
- if (layoutName != null) {
- // We may need to resolve the reference obtained.
- layoutName = context.getRenderResources().findResValue(layoutName.getValue(),
- layoutName.isFramework());
- }
- int layoutId = 0;
- String error = null;
- if (layoutName == null) {
- error = "Unable to find action bar layout (" + LAYOUT_ATTR_NAME
- + ") in the current theme.";
- } else {
- layoutId = context.getFrameworkResourceValue(layoutName.getResourceType(),
- layoutName.getName(), 0);
- if (layoutId == 0) {
- error = String.format("Unable to resolve attribute \"%s\" of type \"%s\"",
- layoutName.getName(), layoutName.getResourceType());
- }
- }
- if (layoutId == 0) {
- throw new RuntimeException(error);
- }
- // Create a RelativeLayout to hold the action bar. The layout is needed so that we may
- // add the menu popup to it.
- mEnclosingLayout = new RelativeLayout(mBridgeContext);
- setMatchParent(mEnclosingLayout);
- parentView.addView(mEnclosingLayout);
-
- // Inflate action bar layout.
- View decorContent = LayoutInflater.from(context).inflate(layoutId, mEnclosingLayout, true);
-
- mActionBar = CustomActionBarWrapper.getActionBarWrapper(context, params, decorContent);
+ mActionBar = FrameworkActionBarWrapper.getActionBarWrapper(context, getCallBack(),
+ decorContent);
FrameLayout contentRoot = (FrameLayout) mEnclosingLayout.findViewById(android.R.id.content);
@@ -117,27 +77,62 @@
contentRoot = new FrameLayout(context);
setMatchParent(contentRoot);
mEnclosingLayout.addView(contentRoot);
- mContentRoot = contentRoot;
+ setContentRoot(contentRoot);
} else {
- mContentRoot = contentRoot;
- mActionBar.setupActionBar();
+ setContentRoot(contentRoot);
+ setupActionBar();
mActionBar.inflateMenus();
}
}
- private void setMatchParent(View view) {
- view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT));
+ @Override
+ protected ResourceValue getLayoutResource(BridgeContext context) {
+ ResourceValue layoutName =
+ context.getRenderResources().findItemInTheme(LAYOUT_ATTR_NAME, true);
+ if (layoutName != null) {
+ // We may need to resolve the reference obtained.
+ layoutName = context.getRenderResources().findResValue(layoutName.getValue(),
+ layoutName.isFramework());
+ }
+ if (layoutName == null) {
+ throw new InflateException("Unable to find action bar layout (" + LAYOUT_ATTR_NAME
+ + ") in the current theme.");
+ }
+ return layoutName;
+ }
+
+ @Override
+ protected void setupActionBar() {
+ super.setupActionBar();
+ mActionBar.setupActionBar();
+ }
+
+ @Override
+ protected void setHomeAsUp(boolean homeAsUp) {
+ mActionBar.setHomeAsUp(homeAsUp);
+ }
+
+ @Override
+ protected void setTitle(CharSequence title) {
+ mActionBar.setTitle(title);
+ }
+
+ @Override
+ protected void setSubtitle(CharSequence subtitle) {
+ mActionBar.setSubTitle(subtitle);
+ }
+
+ @Override
+ protected void setIcon(String icon) {
+ mActionBar.setIcon(icon);
}
/**
* Creates a Popup and adds it to the content frame. It also adds another {@link FrameLayout} to
* the content frame which shall serve as the new content root.
*/
+ @Override
public void createMenuPopup() {
- assert mEnclosingLayout.getChildCount() == 1
- : "Action Bar Menus have already been created.";
-
if (!isOverflowPopupNeeded()) {
return;
}
@@ -193,11 +188,6 @@
return needed;
}
- @NonNull
- public FrameLayout getContentRoot() {
- return mContentRoot;
- }
-
// Copied from com.android.internal.view.menu.MenuPopHelper.measureContentWidth()
private int measureContentWidth(@NonNull ListAdapter adapter) {
// Menus don't tend to be long, so this is more sane than it looks.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomActionBarWrapper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBarWrapper.java
similarity index 81%
rename from tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomActionBarWrapper.java
rename to tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBarWrapper.java
index 6db722e..44c2cd8 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomActionBarWrapper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBarWrapper.java
@@ -19,10 +19,8 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.ide.common.rendering.api.ActionBarCallback;
-import com.android.ide.common.rendering.api.ActionBarCallback.HomeButtonStyle;
import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.ResourceValue;
-import com.android.ide.common.rendering.api.SessionParams;
import com.android.internal.R;
import com.android.internal.app.ToolbarActionBar;
import com.android.internal.app.WindowDecorActionBar;
@@ -54,10 +52,9 @@
/**
* A common API to access {@link ToolbarActionBar} and {@link WindowDecorActionBar}.
*/
-public abstract class CustomActionBarWrapper {
+public abstract class FrameworkActionBarWrapper {
@NonNull protected ActionBar mActionBar;
- @NonNull protected SessionParams mParams;
@NonNull protected ActionBarCallback mCallback;
@NonNull protected BridgeContext mContext;
@@ -68,49 +65,48 @@
* ?attr/windowActionBarFullscreenDecorLayout
*/
@NonNull
- public static CustomActionBarWrapper getActionBarWrapper(@NonNull BridgeContext context,
- @NonNull SessionParams params, @NonNull View decorContent) {
+ public static FrameworkActionBarWrapper getActionBarWrapper(@NonNull BridgeContext context,
+ @NonNull ActionBarCallback callback, @NonNull View decorContent) {
View view = decorContent.findViewById(R.id.action_bar);
if (view instanceof Toolbar) {
- return new ToolbarWrapper(context, params, ((Toolbar) view));
+ return new ToolbarWrapper(context, callback, (Toolbar) view);
} else if (view instanceof ActionBarView) {
- return new WindowActionBarWrapper(context, params, decorContent,
- ((ActionBarView) view));
+ return new WindowActionBarWrapper(context, callback, decorContent,
+ (ActionBarView) view);
} else {
throw new IllegalStateException("Can't make an action bar out of " +
view.getClass().getSimpleName());
}
}
- CustomActionBarWrapper(@NonNull BridgeContext context, @NonNull SessionParams params,
+ FrameworkActionBarWrapper(@NonNull BridgeContext context, ActionBarCallback callback,
@NonNull ActionBar actionBar) {
mActionBar = actionBar;
- mParams = params;
- mCallback = params.getProjectCallback().getActionBarCallback();
+ mCallback = callback;
mContext = context;
}
+ /** A call to setup any custom properties. */
protected void setupActionBar() {
- // Do the things that are common to all implementations.
- RenderResources res = mContext.getRenderResources();
+ // Nothing to do here.
+ }
- String title = mParams.getAppLabel();
- ResourceValue titleValue = res.findResValue(title, false);
- if (titleValue != null && titleValue.getValue() != null) {
- mActionBar.setTitle(titleValue.getValue());
- } else {
- mActionBar.setTitle(title);
- }
+ public void setTitle(CharSequence title) {
+ mActionBar.setTitle(title);
+ }
- String subTitle = mCallback.getSubTitle();
+ public void setSubTitle(CharSequence subTitle) {
if (subTitle != null) {
mActionBar.setSubtitle(subTitle);
}
+ }
- // Add show home as up icon.
- if (mCallback.getHomeButtonStyle() == HomeButtonStyle.SHOW_HOME_AS_UP) {
- mActionBar.setDisplayOptions(0xFF, ActionBar.DISPLAY_HOME_AS_UP);
- }
+ public void setHomeAsUp(boolean homeAsUp) {
+ mActionBar.setDisplayHomeAsUpEnabled(homeAsUp);
+ }
+
+ public void setIcon(String icon) {
+ // Nothing to do.
}
protected boolean isSplit() {
@@ -186,15 +182,14 @@
* Material theme uses {@link Toolbar} as the action bar. This wrapper provides access to
* Toolbar using a common API.
*/
- private static class ToolbarWrapper extends CustomActionBarWrapper {
+ private static class ToolbarWrapper extends FrameworkActionBarWrapper {
@NonNull
private final Toolbar mToolbar; // This is the view.
- ToolbarWrapper(@NonNull BridgeContext context, @NonNull SessionParams params,
+ ToolbarWrapper(@NonNull BridgeContext context, @NonNull ActionBarCallback callback,
@NonNull Toolbar toolbar) {
- super(context, params, new ToolbarActionBar(toolbar, "", new WindowCallback())
- );
+ super(context, callback, new ToolbarActionBar(toolbar, "", new WindowCallback()));
mToolbar = toolbar;
}
@@ -248,19 +243,17 @@
* Holo theme uses {@link WindowDecorActionBar} as the action bar. This wrapper provides
* access to it using a common API.
*/
- private static class WindowActionBarWrapper extends CustomActionBarWrapper {
+ private static class WindowActionBarWrapper extends FrameworkActionBarWrapper {
- @NonNull
- private final WindowDecorActionBar mActionBar;
- @NonNull
- private final ActionBarView mActionBarView;
- @NonNull
- private final View mDecorContentRoot;
+ @NonNull private final WindowDecorActionBar mActionBar;
+ @NonNull private final ActionBarView mActionBarView;
+ @NonNull private final View mDecorContentRoot;
private MenuBuilder mMenuBuilder;
- public WindowActionBarWrapper(@NonNull BridgeContext context, @NonNull SessionParams params,
- @NonNull View decorContentRoot, @NonNull ActionBarView actionBarView) {
- super(context, params, new WindowDecorActionBar(decorContentRoot));
+ public WindowActionBarWrapper(@NonNull BridgeContext context,
+ @NonNull ActionBarCallback callback, @NonNull View decorContentRoot,
+ @NonNull ActionBarView actionBarView) {
+ super(context, callback, new WindowDecorActionBar(decorContentRoot));
mActionBarView = actionBarView;
mActionBar = ((WindowDecorActionBar) super.mActionBar);
mDecorContentRoot = decorContentRoot;
@@ -268,7 +261,6 @@
@Override
protected void setupActionBar() {
- super.setupActionBar();
// Set the navigation mode.
int navMode = mCallback.getNavigationMode();
@@ -278,16 +270,6 @@
setupTabs(3);
}
- String icon = mParams.getAppIcon();
- // If the action bar style doesn't specify an icon, set the icon obtained from the
- // session params.
- if (!mActionBar.hasIcon() && icon != null) {
- Drawable iconDrawable = getDrawable(icon, false);
- if (iconDrawable != null) {
- mActionBar.setIcon(iconDrawable);
- }
- }
-
// Set action bar to be split, if needed.
ViewGroup splitView = (ViewGroup) mDecorContentRoot.findViewById(R.id.split_action_bar);
if (splitView != null) {
@@ -300,6 +282,17 @@
}
@Override
+ public void setIcon(String icon) {
+ // Set the icon only if the action bar doesn't specify an icon.
+ if (!mActionBar.hasIcon() && icon != null) {
+ Drawable iconDrawable = getDrawable(icon, false);
+ if (iconDrawable != null) {
+ mActionBar.setIcon(iconDrawable);
+ }
+ }
+ }
+
+ @Override
protected void inflateMenus() {
super.inflateMenus();
// The super implementation doesn't set the menu on the view. Set it here.
@@ -340,7 +333,7 @@
@Override
int getMenuPopupMargin() {
- return -ActionBarLayout.getPixelValue("10dp", mContext.getMetrics());
+ return -FrameworkActionBar.getPixelValue("10dp", mContext.getMetrics());
}
// TODO: Use an adapter, like List View to set up tabs.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 4637bfd..58acab9 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -51,11 +51,13 @@
import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
import com.android.layoutlib.bridge.android.SessionParamsFlags;
+import com.android.layoutlib.bridge.bars.BridgeActionBar;
+import com.android.layoutlib.bridge.bars.AppCompatActionBar;
import com.android.layoutlib.bridge.bars.Config;
import com.android.layoutlib.bridge.bars.NavigationBar;
import com.android.layoutlib.bridge.bars.StatusBar;
import com.android.layoutlib.bridge.bars.TitleBar;
-import com.android.layoutlib.bridge.bars.ActionBarLayout;
+import com.android.layoutlib.bridge.bars.FrameworkActionBar;
import com.android.layoutlib.bridge.impl.binding.FakeAdapter;
import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter;
import com.android.resources.Density;
@@ -354,7 +356,7 @@
// if the theme says no title/action bar, then the size will be 0
if (mActionBarSize > 0) {
- ActionBarLayout actionBar = createActionBar(context, params, backgroundLayout);
+ BridgeActionBar actionBar = createActionBar(context, params, backgroundLayout);
actionBar.createMenuPopup();
mContentRoot = actionBar.getContentRoot();
} else if (mTitleBarSize > 0) {
@@ -1190,8 +1192,22 @@
// android.support.v7.app.ActionBarActivity, and not care about the theme name at all.
if (mIsThemeAppCompat == null) {
StyleResourceValue defaultTheme = resources.getDefaultTheme();
- StyleResourceValue val = resources.getStyle("Theme.AppCompat", false);
- mIsThemeAppCompat = defaultTheme == val || resources.themeIsParentOf(val, defaultTheme);
+ // We can't simply check for parent using resources.themeIsParentOf() since the
+ // inheritance structure isn't really what one would expect. The first common parent
+ // between Theme.AppCompat.Light and Theme.AppCompat is Theme.Material (for v21).
+ boolean isThemeAppCompat = false;
+ for (int i = 0; i < 50; i++) {
+ // for loop ensures that we don't run into cyclic theme inheritance.
+ if (defaultTheme.getName().startsWith("Theme.AppCompat")) {
+ isThemeAppCompat = true;
+ break;
+ }
+ defaultTheme = resources.getParent(defaultTheme);
+ if (defaultTheme == null) {
+ break;
+ }
+ }
+ mIsThemeAppCompat = isThemeAppCompat;
}
return mIsThemeAppCompat;
}
@@ -1647,9 +1663,13 @@
/**
* Creates the action bar. Also queries the project callback for missing information.
*/
- private ActionBarLayout createActionBar(BridgeContext context, SessionParams params,
+ private BridgeActionBar createActionBar(BridgeContext context, SessionParams params,
ViewGroup parentView) {
- return new ActionBarLayout(context, params, parentView);
+ if (mIsThemeAppCompat == Boolean.TRUE) {
+ return new AppCompatActionBar(context, params, parentView);
+ } else {
+ return new FrameworkActionBar(context, params, parentView);
+ }
}
public BufferedImage getImage() {