diff options
48 files changed, 926 insertions, 849 deletions
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 @@ package android.telecom { 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 @@ interface IAccessibilityServiceConnection { 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/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 1b621d1..7cf8fb0 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -391,10 +391,12 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * {@link CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION android.control.aeExposureCompensation}, in counts of {@link CameraCharacteristics#CONTROL_AE_COMPENSATION_STEP android.control.aeCompensationStep}, * that are supported by this camera device.</p> * <p><b>Range of valid values:</b><br></p> + * <p>Range [0,0] indicates that exposure compensation is not supported.</p> + * <p>For LIMITED and FULL devices, range must follow below requirements if exposure + * compensation is supported (<code>range != [0, 0]</code>):</p> * <p><code>Min.exposure compensation * {@link CameraCharacteristics#CONTROL_AE_COMPENSATION_STEP android.control.aeCompensationStep} <= -2 EV</code></p> * <p><code>Max.exposure compensation * {@link CameraCharacteristics#CONTROL_AE_COMPENSATION_STEP android.control.aeCompensationStep} >= 2 EV</code></p> - * <p>LEGACY devices may support a smaller range than this, including the range [0,0], which - * indicates that changing the exposure compensation is not supported.</p> + * <p>LEGACY devices may support a smaller range than this.</p> * <p>This key is available on all devices.</p> * * @see CameraCharacteristics#CONTROL_AE_COMPENSATION_STEP diff --git a/core/java/android/hardware/camera2/DngCreator.java b/core/java/android/hardware/camera2/DngCreator.java index 6fc99ac..33d539c 100644 --- a/core/java/android/hardware/camera2/DngCreator.java +++ b/core/java/android/hardware/camera2/DngCreator.java @@ -530,9 +530,9 @@ public final class DngCreator implements AutoCloseable { 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/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java index 6535a4e..f1f2f0c 100644 --- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java +++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java @@ -44,6 +44,7 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import static com.android.internal.util.Preconditions.*; @@ -68,7 +69,7 @@ public class RequestThreadManager { // For slightly more spammy messages that will get repeated every frame private static final boolean VERBOSE = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.VERBOSE); - private final Camera mCamera; + private Camera mCamera; private final CameraCharacteristics mCharacteristics; private final CameraDeviceState mDeviceState; @@ -83,8 +84,8 @@ public class RequestThreadManager { private static final int MAX_IN_FLIGHT_REQUESTS = 2; private static final int PREVIEW_FRAME_TIMEOUT = 1000; // ms - private static final int JPEG_FRAME_TIMEOUT = 3000; // ms (same as CTS for API2) - private static final int REQUEST_COMPLETE_TIMEOUT = 3000; // ms (same as JPEG timeout) + private static final int JPEG_FRAME_TIMEOUT = 4000; // ms (same as CTS for API2) + private static final int REQUEST_COMPLETE_TIMEOUT = JPEG_FRAME_TIMEOUT; // ms (same as JPEG timeout) private static final float ASPECT_RATIO_TOLERANCE = 0.01f; private boolean mPreviewRunning = false; @@ -108,6 +109,8 @@ public class RequestThreadManager { private final FpsCounter mPrevCounter = new FpsCounter("Incoming Preview"); private final FpsCounter mRequestCounter = new FpsCounter("Incoming Requests"); + private final AtomicBoolean mQuit = new AtomicBoolean(false); + // Stuff JPEGs into HAL_PIXEL_FORMAT_RGBA_8888 gralloc buffers to get around SW write // limitations for (b/17379185). private static final boolean USE_BLOB_FORMAT_OVERRIDE = true; @@ -325,7 +328,15 @@ public class RequestThreadManager { Log.d(TAG, "configureOutputs with " + outputsStr); } - stopPreview(); + try { + stopPreview(); + } catch (RuntimeException e) { + Log.e(TAG, "Received device exception in configure call: ", e); + mDeviceState.setError( + CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE); + return; + } + /* * Try to release the previous preview's surface texture earlier if we end up * using a different one; this also reduces the likelihood of getting into a deadlock @@ -335,6 +346,11 @@ public class RequestThreadManager { mCamera.setPreviewTexture(/*surfaceTexture*/null); } catch (IOException e) { Log.w(TAG, "Failed to clear prior SurfaceTexture, may cause GL deadlock: ", e); + } catch (RuntimeException e) { + Log.e(TAG, "Received device exception in configure call: ", e); + mDeviceState.setError( + CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE); + return; } if (mGLThreadManager != null) { @@ -470,7 +486,14 @@ public class RequestThreadManager { mPreviewTexture.setOnFrameAvailableListener(mPreviewCallback); } - mCamera.setParameters(mParams); + try { + mCamera.setParameters(mParams); + } catch (RuntimeException e) { + Log.e(TAG, "Received device exception while configuring: ", e); + mDeviceState.setError( + CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE); + + } } private void resetJpegSurfaceFormats(Collection<Surface> surfaces) { @@ -793,7 +816,7 @@ public class RequestThreadManager { } } catch (IOException e) { - Log.e(TAG, "Received device exception: ", e); + Log.e(TAG, "Received device exception during capture call: ", e); mDeviceState.setError( CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE); break; @@ -802,6 +825,11 @@ public class RequestThreadManager { mDeviceState.setError( CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE); break; + } catch (RuntimeException e) { + Log.e(TAG, "Received device exception during capture call: ", e); + mDeviceState.setError( + CameraDeviceImpl.CameraDeviceCallbacks.ERROR_CAMERA_DEVICE); + break; } if (paramsChanged) { @@ -878,9 +906,11 @@ public class RequestThreadManager { } if (mGLThreadManager != null) { mGLThreadManager.quit(); + mGLThreadManager = null; } if (mCamera != null) { mCamera.release(); + mCamera = null; } resetJpegSurfaceFormats(mCallbackOutputs); break; @@ -942,14 +972,16 @@ public class RequestThreadManager { * Quit the request thread, and clean up everything. */ public void quit() { - Handler handler = mRequestThread.waitAndGetHandler(); - handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP)); - mRequestThread.quitSafely(); - try { - mRequestThread.join(); - } catch (InterruptedException e) { - Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.", - mRequestThread.getName(), mRequestThread.getId())); + if (!mQuit.getAndSet(true)) { // Avoid sending messages on dead thread's handler. + Handler handler = mRequestThread.waitAndGetHandler(); + handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP)); + mRequestThread.quitSafely(); + try { + mRequestThread.join(); + } catch (InterruptedException e) { + Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.", + mRequestThread.getName(), mRequestThread.getId())); + } } } diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index bedc695..3d5215b 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -276,7 +276,7 @@ public final class Parcel { private static native byte[] nativeMarshall(long nativePtr); private static native void nativeUnmarshall( - long nativePtr, byte[] data, int offest, int length); + long nativePtr, byte[] data, int offset, int length); private static native void nativeAppendFrom( long thisNativePtr, long otherNativePtr, int offset, int length); private static native boolean nativeHasFileDescriptors(long nativePtr); @@ -438,8 +438,8 @@ public final class Parcel { /** * Set the bytes in data to be the raw bytes of this Parcel. */ - public final void unmarshall(byte[] data, int offest, int length) { - nativeUnmarshall(mNativePtr, data, offest, length); + public final void unmarshall(byte[] data, int offset, int length) { + nativeUnmarshall(mNativePtr, data, offset, length); } public final void appendFrom(Parcel parcel, int offset, int length) { diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java index 7c9861f..6ed3885 100644 --- a/core/java/android/util/ArrayMap.java +++ b/core/java/android/util/ArrayMap.java @@ -255,7 +255,10 @@ public final class ArrayMap<K, V> implements Map<K, V> { } private ArrayMap(boolean immutable) { - mHashes = EmptyArray.INT; + // If this is immutable, use the sentinal EMPTY_IMMUTABLE_INTS + // instance instead of the usual EmptyArray.INT. The reference + // is checked later to see if the array is allowed to grow. + mHashes = immutable ? EMPTY_IMMUTABLE_INTS : EmptyArray.INT; mArray = EmptyArray.OBJECT; mSize = 0; } 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 @@ final class AccessibilityInteractionController { } } - 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 @@ final class AccessibilityInteractionController { 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 @@ final class AccessibilityInteractionController { 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 @@ final class AccessibilityInteractionController { 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 final class MotionEvent extends InputEvent implements Parcelable { 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 @@ public final class MotionEvent extends InputEvent implements Parcelable { 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..e4a8978 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -5553,12 +5553,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, getDrawingRect(bounds); info.setBoundsInParent(bounds); - getBoundsOnScreen(bounds); + getBoundsOnScreen(bounds, true); info.setBoundsInScreen(bounds); ViewParent parent = getParentForAccessibility(); @@ -5805,142 +5823,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @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) { @@ -12979,7 +12872,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Interpolator.Result.FREEZE_END) { cache.state = ScrollabilityCache.OFF; } else { - cache.scrollBar.setAlpha(Math.round(values[0])); + cache.scrollBar.mutate().setAlpha(Math.round(values[0])); } // This will make the scroll bars inval themselves after @@ -12989,7 +12882,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } else { // We're just on -- but we may have been fading before so // reset alpha - cache.scrollBar.setAlpha(255); + cache.scrollBar.mutate().setAlpha(255); } 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 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager @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 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** - * 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 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager 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 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // 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 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager 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 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager 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 @@ public final class ViewRootImpl implements ViewParent, 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 @@ public final class ViewRootImpl implements ViewParent, } @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/Window.java b/core/java/android/view/Window.java index 3f2f3a5..8704356 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -102,10 +102,12 @@ public abstract class Window { */ public static final int FEATURE_SWIPE_TO_DISMISS = 11; /** - * Flag for requesting that window content changes should be represented - * with scenes and transitions. + * Flag for requesting that window content changes should be animated using a + * TransitionManager. * - * TODO Add docs + * <p>The TransitionManager is set using + * {@link #setTransitionManager(android.transition.TransitionManager)}. If none is set, + * a default TransitionManager will be used.</p> * * @see #setContentView */ @@ -1019,10 +1021,16 @@ public abstract class Window { * <p>Note that calling this function "locks in" various characteristics * of the window that can not, from this point forward, be changed: the * features that have been requested with {@link #requestFeature(int)}, - * and certain window flags as described in {@link #setFlags(int, int)}. + * and certain window flags as described in {@link #setFlags(int, int)}.</p> + * + * <p>If {@link #FEATURE_CONTENT_TRANSITIONS} is set, the window's + * TransitionManager will be used to animate content from the current + * content View to view.</p> * * @param view The desired content to display. * @param params Layout parameters for the view. + * @see #getTransitionManager() + * @see #setTransitionManager(android.transition.TransitionManager) */ public abstract void setContentView(View view, ViewGroup.LayoutParams params); @@ -1467,6 +1475,7 @@ public abstract class Window { * {@link #setContentView}) if {@link #FEATURE_CONTENT_TRANSITIONS} has been granted.</p> * * @return This window's content TransitionManager or null if none is set. + * @attr ref android.R.styleable#Window_windowContentTransitionManager */ public TransitionManager getTransitionManager() { return null; @@ -1477,6 +1486,7 @@ public abstract class Window { * Requires {@link #FEATURE_CONTENT_TRANSITIONS}. * * @param tm The TransitionManager to use for scene changes. + * @attr ref android.R.styleable#Window_windowContentTransitionManager */ public void setTransitionManager(TransitionManager tm) { throw new UnsupportedOperationException(); 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 @@ public final class AccessibilityInteractionClient private boolean mPerformAccessibilityActionResult; - private Point mComputeClickPointResult; - private Message mSameThreadMessage; private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache = @@ -522,43 +520,6 @@ public final class AccessibilityInteractionClient 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 @@ public final class AccessibilityInteractionClient } /** - * 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 @@ public final class AccessibilityInteractionClient 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 @@ oneway interface IAccessibilityInteractionConnection { 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 @@ oneway interface IAccessibilityInteractionConnectionCallback { * @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/core/java/android/widget/ScrollBarDrawable.java b/core/java/android/widget/ScrollBarDrawable.java index 10e9ff4..8eff1aa 100644 --- a/core/java/android/widget/ScrollBarDrawable.java +++ b/core/java/android/widget/ScrollBarDrawable.java @@ -44,6 +44,7 @@ public class ScrollBarDrawable extends Drawable { private final Rect mTempBounds = new Rect(); private boolean mAlwaysDrawHorizontalTrack; private boolean mAlwaysDrawVerticalTrack; + private boolean mMutated; public ScrollBarDrawable() { } @@ -191,6 +192,9 @@ public class ScrollBarDrawable extends Drawable { public void setVerticalThumbDrawable(Drawable thumb) { if (thumb != null) { + if (mMutated) { + thumb.mutate(); + } thumb.setState(STATE_ENABLED); mVerticalThumb = thumb; } @@ -198,6 +202,9 @@ public class ScrollBarDrawable extends Drawable { public void setVerticalTrackDrawable(Drawable track) { if (track != null) { + if (mMutated) { + track.mutate(); + } track.setState(STATE_ENABLED); } mVerticalTrack = track; @@ -205,6 +212,9 @@ public class ScrollBarDrawable extends Drawable { public void setHorizontalThumbDrawable(Drawable thumb) { if (thumb != null) { + if (mMutated) { + thumb.mutate(); + } thumb.setState(STATE_ENABLED); mHorizontalThumb = thumb; } @@ -212,6 +222,9 @@ public class ScrollBarDrawable extends Drawable { public void setHorizontalTrackDrawable(Drawable track) { if (track != null) { + if (mMutated) { + track.mutate(); + } track.setState(STATE_ENABLED); } mHorizontalTrack = track; @@ -228,6 +241,26 @@ public class ScrollBarDrawable extends Drawable { } @Override + public ScrollBarDrawable mutate() { + if (!mMutated && super.mutate() == this) { + if (mVerticalTrack != null) { + mVerticalTrack.mutate(); + } + if (mVerticalThumb != null) { + mVerticalThumb.mutate(); + } + if (mHorizontalTrack != null) { + mHorizontalTrack.mutate(); + } + if (mHorizontalThumb != null) { + mHorizontalThumb.mutate(); + } + mMutated = true; + } + return this; + } + + @Override public void setAlpha(int alpha) { if (mVerticalTrack != null) { mVerticalTrack.setAlpha(alpha); diff --git a/core/java/com/android/internal/widget/SubtitleView.java b/core/java/com/android/internal/widget/SubtitleView.java index 2f987e9..a206e7f 100644 --- a/core/java/com/android/internal/widget/SubtitleView.java +++ b/core/java/com/android/internal/widget/SubtitleView.java @@ -148,6 +148,7 @@ public class SubtitleView extends View { mHasMeasurements = false; requestLayout(); + invalidate(); } public void setForegroundColor(int color) { diff --git a/core/res/res/drawable-mdpi/ic_clear_mtrl_alpha.png b/core/res/res/drawable-mdpi/ic_clear_mtrl_alpha.png Binary files differdeleted file mode 100644 index d43e4d1..0000000 --- a/core/res/res/drawable-mdpi/ic_clear_mtrl_alpha.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/ic_clear_mtrl_alpha.png b/core/res/res/drawable-xhdpi/ic_clear_mtrl_alpha.png Binary files differdeleted file mode 100644 index ddacb59..0000000 --- a/core/res/res/drawable-xhdpi/ic_clear_mtrl_alpha.png +++ /dev/null diff --git a/core/res/res/drawable-xxhdpi/ic_clear_mtrl_alpha.png b/core/res/res/drawable-xxhdpi/ic_clear_mtrl_alpha.png Binary files differdeleted file mode 100644 index 21ed914..0000000 --- a/core/res/res/drawable-xxhdpi/ic_clear_mtrl_alpha.png +++ /dev/null diff --git a/core/res/res/drawable/ic_clear_material.xml b/core/res/res/drawable/ic_clear_material.xml index 076c0a2..a21f47a 100644 --- a/core/res/res/drawable/ic_clear_material.xml +++ b/core/res/res/drawable/ic_clear_material.xml @@ -14,6 +14,13 @@ limitations under the License. --> -<bitmap xmlns:android="http://schemas.android.com/apk/res/android" - android:src="@drawable/ic_clear_mtrl_alpha" - android:tint="?attr/colorControlNormal" /> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?attr/colorControlNormal"> +<path + android:pathData="M19,6.41L17.59,5,12,10.59,6.41,5,5,6.41,10.59,12,5,17.59,6.41,19,12,13.41,17.59,19,19,17.59,13.41,12z" + android:fillColor="@color/white"/> +</vector> diff --git a/core/res/res/drawable/scrubber_progress_horizontal_material.xml b/core/res/res/drawable/scrubber_progress_horizontal_material.xml index f2ea30f..89a1787 100644 --- a/core/res/res/drawable/scrubber_progress_horizontal_material.xml +++ b/core/res/res/drawable/scrubber_progress_horizontal_material.xml @@ -14,29 +14,35 @@ limitations under the License. --> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_enabled="false"> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@id/background"> <nine-patch android:src="@drawable/scrubber_track_mtrl_alpha" - android:tint="?attr/colorControlNormal" /> - </item> - <item> - <layer-list> - <item android:id="@id/background"> - <nine-patch android:src="@drawable/scrubber_track_mtrl_alpha" android:tint="?attr/colorControlNormal" /> - </item> - <item android:id="@id/secondaryProgress"> - <scale android:scaleWidth="100%"> + </item> + <item android:id="@id/secondaryProgress"> + <scale android:scaleWidth="100%"> + <selector> + <item android:state_enabled="false"> + <color android:color="@color/transparent" /> + </item> + <item> <nine-patch android:src="@drawable/scrubber_primary_mtrl_alpha" - android:tint="?attr/colorControlNormal" /> - </scale> - </item> - <item android:id="@id/progress"> - <scale android:scaleWidth="100%"> + android:tint="?attr/colorControlNormal" /> + </item> + </selector> + </scale> + </item> + <item android:id="@id/progress"> + <scale android:scaleWidth="100%"> + <selector> + <item android:state_enabled="false"> + <color android:color="@color/transparent" /> + </item> + <item> <nine-patch android:src="@drawable/scrubber_primary_mtrl_alpha" - android:tint="?attr/colorControlActivated" /> - </scale> - </item> - </layer-list> + android:tint="?attr/colorControlActivated" /> + </item> + </selector> + </scale> </item> -</selector> +</layer-list>
\ No newline at end of file diff --git a/core/res/res/layout/preference_dialog_edittext_material.xml b/core/res/res/layout/preference_dialog_edittext_material.xml new file mode 100644 index 0000000..df76f7f --- /dev/null +++ b/core/res/res/layout/preference_dialog_edittext_material.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<!-- Layout used as the dialog's content View for EditTextPreference. --> +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginTop="48dp" + android:layout_marginBottom="48dp" + android:overScrollMode="ifContentScrolls"> + + <LinearLayout + android:id="@+id/edittext_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="20dp" + android:orientation="vertical"> + + <TextView + android:id="@+id/message" + android:layout_marginBottom="48dp" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingStart="4dp" + android:paddingEnd="4dp" + android:textColor="?attr/textColorSecondary" + style="?attr/textAppearanceSmall" /> + + </LinearLayout> + +</ScrollView> diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml index 6220a1b..419beba 100644 --- a/core/res/res/values/styles_material.xml +++ b/core/res/res/values/styles_material.xml @@ -88,7 +88,7 @@ please see styles_device_defaults.xml. </style> <style name="Preference.Material.DialogPreference.EditTextPreference"> - <item name="dialogLayout">@layout/preference_dialog_edittext</item> + <item name="dialogLayout">@layout/preference_dialog_edittext_material</item> </style> <style name="Preference.Material.RingtonePreference"> 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 @@ Platform Versions</a>.</p> </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 @@ Screens</a>.</p> </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 @@ support for any lower version (for example, support for version 2.0 also implies <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 @@ uses.</p> </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 @@ uses.</p> 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 @@ var VERSION_DATA = { "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 @@ var SCREEN_DATA = "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 @@ var VERSION_NAMES = }, { "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 @@ java.util.List} is not empty, you can safely use the intent. For example:</p> <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/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 67c0552..240faef 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -46,9 +46,9 @@ import android.provider.Settings; import android.util.Log; import android.view.KeyEvent; -import java.util.HashMap; import java.util.ArrayList; - +import java.util.HashMap; +import java.util.Iterator; /** * AudioManager provides access to volume and ringer mode control. @@ -3650,11 +3650,13 @@ public class AudioManager { newPorts.clear(); status = AudioSystem.listAudioPorts(newPorts, portGeneration); if (status != SUCCESS) { + Log.w(TAG, "updateAudioPortCache: listAudioPorts failed"); return status; } newPatches.clear(); status = AudioSystem.listAudioPatches(newPatches, patchGeneration); if (status != SUCCESS) { + Log.w(TAG, "updateAudioPortCache: listAudioPatches failed"); return status; } } while (patchGeneration[0] != portGeneration[0]); @@ -3663,18 +3665,33 @@ public class AudioManager { for (int j = 0; j < newPatches.get(i).sources().length; j++) { AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sources()[j], newPorts); - if (portCfg == null) { - return ERROR; - } newPatches.get(i).sources()[j] = portCfg; } for (int j = 0; j < newPatches.get(i).sinks().length; j++) { AudioPortConfig portCfg = updatePortConfig(newPatches.get(i).sinks()[j], newPorts); + newPatches.get(i).sinks()[j] = portCfg; + } + } + for (Iterator<AudioPatch> i = newPatches.iterator(); i.hasNext(); ) { + AudioPatch newPatch = i.next(); + boolean hasInvalidPort = false; + for (AudioPortConfig portCfg : newPatch.sources()) { if (portCfg == null) { - return ERROR; + hasInvalidPort = true; + break; } - newPatches.get(i).sinks()[j] = portCfg; + } + for (AudioPortConfig portCfg : newPatch.sinks()) { + if (portCfg == null) { + hasInvalidPort = true; + break; + } + } + if (hasInvalidPort) { + // Temporarily remove patches with invalid ports. One who created the patch + // is responsible for dealing with the port change. + i.remove(); } } 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 class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe } } + 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.util.Slog; 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 @@ public abstract class KeyguardViewBase extends FrameLayout implements SecurityCa 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/res/layout/qs_detail_item.xml b/packages/SystemUI/res/layout/qs_detail_item.xml index ea2e1e1..0ba3ba3 100644 --- a/packages/SystemUI/res/layout/qs_detail_item.xml +++ b/packages/SystemUI/res/layout/qs_detail_item.xml @@ -39,6 +39,7 @@ android:id="@android:id/title" android:layout_width="match_parent" android:layout_height="wrap_content" + android:ellipsize="end" android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary" /> <TextView @@ -58,4 +59,4 @@ android:scaleType="center" android:src="@drawable/ic_qs_cancel" /> -</LinearLayout>
\ No newline at end of file +</LinearLayout> diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java index 9155102..95ac558 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java @@ -190,6 +190,7 @@ public class QSDetailItems extends FrameLayout { title.setText(item.line1); final TextView summary = (TextView) view.findViewById(android.R.id.summary); final boolean twoLines = !TextUtils.isEmpty(item.line2); + title.setMaxLines(twoLines ? 1 : 2); summary.setVisibility(twoLines ? VISIBLE : GONE); summary.setText(twoLines ? item.line2 : null); view.setMinimumHeight(mContext.getResources() .getDimensionPixelSize( 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.KeyEvent; 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 @@ public class KeyguardBouncer { 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { 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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } @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 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } 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 @@ class TouchExplorer implements EventStreamTransformation { 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 @@ class TouchExplorer implements EventStreamTransformation { * * @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 @@ class TouchExplorer implements EventStreamTransformation { 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 @@ class TouchExplorer implements EventStreamTransformation { 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 @@ class TouchExplorer implements EventStreamTransformation { 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 @@ class TouchExplorer implements EventStreamTransformation { 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 @@ class TouchExplorer implements EventStreamTransformation { 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/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index a9a756e..65a5c23 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -1230,7 +1230,8 @@ class AlarmManagerService extends SystemService { if (mAlarmBatches.size() > 0) { final Batch firstWakeup = findFirstWakeupBatchLocked(); final Batch firstBatch = mAlarmBatches.get(0); - if (firstWakeup != null && mNextWakeup != firstWakeup.start) { + // always update the kernel alarms, as a backstop against missed wakeups + if (firstWakeup != null) { mNextWakeup = firstWakeup.start; setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start); } @@ -1243,7 +1244,8 @@ class AlarmManagerService extends SystemService { nextNonWakeup = mNextNonWakeupDeliveryTime; } } - if (nextNonWakeup != 0 && mNextNonWakeup != nextNonWakeup) { + // always update the kernel alarm, as a backstop against missed wakeups + if (nextNonWakeup != 0) { mNextNonWakeup = nextNonWakeup; setLocked(ELAPSED_REALTIME, nextNonWakeup); } 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.Network; 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 @@ public class NetworkMonitor extends StateMachine { 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 @@ public class NetworkMonitor extends StateMachine { 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/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index fd4c016..40e2056 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -3049,7 +3049,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mHandler.post(new Runnable() { public void run() { try { - ActivityManagerNative.getDefault().switchUser(UserHandle.USER_OWNER); + IActivityManager am = ActivityManagerNative.getDefault(); + if (am.getCurrentUser().id == userHandle) { + am.switchUser(UserHandle.USER_OWNER); + } if (!mUserManager.removeUser(userHandle)) { Slog.w(LOG_TAG, "Couldn't remove user " + userHandle); } 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 @@ public abstract class Connection implements IConferenceable { /** @hide */ public void onConferenceParticipantsChanged(Connection c, List<ConferenceParticipant> participants) {} + public void onConferenceStarted() {} } /** @hide */ @@ -1422,4 +1423,13 @@ public abstract class Connection implements IConferenceable { 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/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index f3b7129..c043659 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -1815,7 +1815,7 @@ public class PhoneNumberUtils // to the list. number = extractNetworkPortionAlt(number); - Rlog.d(LOG_TAG, "subId:" + subId + ", number: " + number + ", defaultCountryIso:" + + Rlog.d(LOG_TAG, "subId:" + subId + ", defaultCountryIso:" + ((defaultCountryIso == null) ? "NULL" : defaultCountryIso)); String emergencyNumbers = ""; diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java index a2bd6d7..1803692 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 @@ public final class BridgeTypedArray extends TypedArray { int pos = value.indexOf('/'); String idName = value.substring(pos + 1); - - // if this is a framework id - if (mPlatformFile || value.startsWith("@android") || value.startsWith("@+android")) { - // look for idName in the android R classes + boolean create = value.startsWith("@+"); + boolean isFrameworkId = + mPlatformFile || value.startsWith("@android") || value.startsWith("@+android"); + + // 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; + } + // 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); } - - // look for idName in the project R class. - 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 @@ public final class BridgeContext extends Context { 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 @@ public final class BridgeContext extends Context { 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 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.Context; 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 android.widget.RelativeLayout; 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; - - 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); + View decorContent = getDecorContent(); - 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 @@ public class ActionBarLayout { 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 @@ public class ActionBarLayout { 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 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 @@ package com.android.layoutlib.bridge.bars; 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 @@ import static com.android.resources.ResourceType.MENU; /** * 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 @@ public abstract class CustomActionBarWrapper { * ?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 @@ public abstract class CustomActionBarWrapper { * 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 @@ public abstract class CustomActionBarWrapper { * 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 @@ public abstract class CustomActionBarWrapper { @Override protected void setupActionBar() { - super.setupActionBar(); // Set the navigation mode. int navMode = mCallback.getNavigationMode(); @@ -278,16 +270,6 @@ public abstract class CustomActionBarWrapper { 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 @@ public abstract class CustomActionBarWrapper { } @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 @@ public abstract class CustomActionBarWrapper { @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.BridgeContext; 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 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { // 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 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { // 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 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { /** * 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() { |