diff options
51 files changed, 1383 insertions, 619 deletions
diff --git a/api/current.txt b/api/current.txt index 5cafd6c233df..07974ed8e356 100644 --- a/api/current.txt +++ b/api/current.txt @@ -37498,6 +37498,7 @@ package android.widget { method public void clearChoices(); method public void clearTextFilter(); method public void deferNotifyDataSetChanged(); + method public void fling(int); method public int getCacheColorHint(); method public int getCheckedItemCount(); method public long[] getCheckedItemIds(); diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index c4615119dc3b..6a9d56544184 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -130,15 +130,6 @@ public abstract class CameraDevice implements AutoCloseable { public abstract String getId(); /** - * <p>Set up a new output set of Surfaces for the camera device.</p> - * - * @deprecated Use {@link #createCaptureSession} instead - * @hide - */ - @Deprecated - public abstract void configureOutputs(List<Surface> outputs) throws CameraAccessException; - - /** * <p>Create a new camera capture session by providing the target output set of Surfaces to the * camera device.</p> * @@ -276,68 +267,6 @@ public abstract class CameraDevice implements AutoCloseable { throws CameraAccessException; /** - * <p>Submit a request for an image to be captured by this CameraDevice.</p> - * - * @deprecated Use {@link CameraCaptureSession#capture} instead - * @hide - */ - @Deprecated - public abstract int capture(CaptureRequest request, CaptureListener listener, Handler handler) - throws CameraAccessException; - - /** - * Submit a list of requests to be captured in sequence as a burst. - * - * @deprecated Use {@link CameraCaptureSession#captureBurst} instead - * @hide - */ - @Deprecated - public abstract int captureBurst(List<CaptureRequest> requests, CaptureListener listener, - Handler handler) throws CameraAccessException; - - /** - * Request endlessly repeating capture of images by this CameraDevice. - * - * @deprecated Use {@link CameraCaptureSession#setRepeatingRequest} instead - * @hide - */ - @Deprecated - public abstract int setRepeatingRequest(CaptureRequest request, CaptureListener listener, - Handler handler) throws CameraAccessException; - - /** - * <p>Request endlessly repeating capture of a sequence of images by this - * CameraDevice.</p> - * - * @deprecated Use {@link CameraCaptureSession#setRepeatingBurst} instead - * @hide - */ - @Deprecated - public abstract int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener, - Handler handler) throws CameraAccessException; - - /** - * <p>Cancel any ongoing repeating capture set by either - * {@link #setRepeatingRequest setRepeatingRequest} or - * {@link #setRepeatingBurst}. - * - * @deprecated Use {@link CameraCaptureSession#stopRepeating} instead - * @hide - */ - @Deprecated - public abstract void stopRepeating() throws CameraAccessException; - - /** - * Flush all captures currently pending and in-progress as fast as - * possible. - * - * @deprecated Use {@link CameraCaptureSession#abortCaptures} instead - * @hide - */ - @Deprecated - public abstract void flush() throws CameraAccessException; - - /** * Close the connection to this camera device as quickly as possible. * * <p>Immediately after this call, all calls to the camera device or active session interface @@ -356,96 +285,6 @@ public abstract class CameraDevice implements AutoCloseable { public abstract void close(); /** - * <p>A listener for tracking the progress of a {@link CaptureRequest} - * submitted to the camera device.</p> - * - * @deprecated Use {@link CameraCaptureSession.CaptureListener} instead - * @hide - */ - @Deprecated - public static abstract class CaptureListener { - - /** - * This constant is used to indicate that no images were captured for - * the request. - * - * @hide - */ - public static final int NO_FRAMES_CAPTURED = -1; - - /** - * This method is called when the camera device has started capturing - * the output image for the request, at the beginning of image exposure. - * - * @see android.media.MediaActionSound - */ - public void onCaptureStarted(CameraDevice camera, - CaptureRequest request, long timestamp) { - // default empty implementation - } - - /** - * This method is called when some results from an image capture are - * available. - * - * @hide - */ - public void onCapturePartial(CameraDevice camera, - CaptureRequest request, CaptureResult result) { - // default empty implementation - } - - /** - * This method is called when an image capture makes partial forward progress; some - * (but not all) results from an image capture are available. - * - */ - public void onCaptureProgressed(CameraDevice camera, - CaptureRequest request, CaptureResult partialResult) { - // default empty implementation - } - - /** - * This method is called when an image capture has fully completed and all the - * result metadata is available. - */ - public void onCaptureCompleted(CameraDevice camera, - CaptureRequest request, TotalCaptureResult result) { - // default empty implementation - } - - /** - * This method is called instead of {@link #onCaptureCompleted} when the - * camera device failed to produce a {@link CaptureResult} for the - * request. - */ - public void onCaptureFailed(CameraDevice camera, - CaptureRequest request, CaptureFailure failure) { - // default empty implementation - } - - /** - * This method is called independently of the others in CaptureListener, - * when a capture sequence finishes and all {@link CaptureResult} - * or {@link CaptureFailure} for it have been returned via this listener. - */ - public void onCaptureSequenceCompleted(CameraDevice camera, - int sequenceId, long frameNumber) { - // default empty implementation - } - - /** - * This method is called independently of the others in CaptureListener, - * when a capture sequence aborts before any {@link CaptureResult} - * or {@link CaptureFailure} for it have been returned via this listener. - */ - public void onCaptureSequenceAborted(CameraDevice camera, - int sequenceId) { - // default empty implementation - } - } - - /** * A listener for notifications about the state of a camera * device. * @@ -542,40 +381,6 @@ public abstract class CameraDevice implements AutoCloseable { public abstract void onOpened(CameraDevice camera); // Must implement /** - * The method called when a camera device has no outputs configured. - * - * @deprecated Use {@link #onOpened} instead. - * @hide - */ - @Deprecated - public void onUnconfigured(CameraDevice camera) { - // Default empty implementation - } - - /** - * The method called when a camera device begins processing - * {@link CaptureRequest capture requests}. - * - * @deprecated Use {@link CameraCaptureSession.StateListener#onActive} instead. - * @hide - */ - @Deprecated - public void onActive(CameraDevice camera) { - // Default empty implementation - } - - /** - * The method called when a camera device is busy. - * - * @deprecated Use {@link CameraCaptureSession.StateListener#onConfigured} instead. - * @hide - */ - @Deprecated - public void onBusy(CameraDevice camera) { - // Default empty implementation - } - - /** * The method called when a camera device has been closed with * {@link CameraDevice#close}. * @@ -591,18 +396,6 @@ public abstract class CameraDevice implements AutoCloseable { } /** - * The method called when a camera device has finished processing all - * submitted capture requests and has reached an idle state. - * - * @deprecated Use {@link CameraCaptureSession.StateListener#onReady} instead. - * @hide - */ - @Deprecated - public void onIdle(CameraDevice camera) { - // Default empty implementation - } - - /** * The method called when a camera device is no longer available for * use. * diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index f829f5eb5d07..a15028c3b141 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java @@ -103,7 +103,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { * Use the same handler as the device's StateListener for all the internal coming events * * This ensures total ordering between CameraDevice.StateListener and - * CameraDevice.CaptureListener events. + * CameraDeviceImpl.CaptureListener events. */ mSequenceDrainer = new TaskDrainer<>(mDeviceHandler, new SequenceDrainListener(), /*name*/"seq"); @@ -141,7 +141,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { checkNotClosed(); checkLegalToCapture(); - handler = checkHandler(handler); + handler = checkHandler(handler, listener); if (VERBOSE) { Log.v(TAG, "capture - request " + request + ", listener " + listener + " handler" + @@ -164,7 +164,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { checkNotClosed(); checkLegalToCapture(); - handler = checkHandler(handler); + handler = checkHandler(handler, listener); if (VERBOSE) { CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]); @@ -186,7 +186,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { checkNotClosed(); checkLegalToCapture(); - handler = checkHandler(handler); + handler = checkHandler(handler, listener); if (VERBOSE) { Log.v(TAG, "setRepeatingRequest - request " + request + ", listener " + listener + @@ -209,7 +209,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { checkNotClosed(); checkLegalToCapture(); - handler = checkHandler(handler); + handler = checkHandler(handler, listener); if (VERBOSE) { CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]); @@ -261,9 +261,12 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { * <p>The semantics are identical to {@link #close}, except that unconfiguring will be skipped. * <p> * + * <p>After this call completes, the session will not call any further methods on the camera + * device.</p> + * * @see CameraCaptureSession#close */ - synchronized void replaceSessionClose(CameraCaptureSession other) { + synchronized void replaceSessionClose() { /* * In order for creating new sessions to be fast, the new session should be created * before the old session is closed. @@ -278,13 +281,17 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { if (VERBOSE) Log.v(TAG, "replaceSessionClose"); - // #close was already called explicitly, keep going the slow route - if (mClosed) { - if (VERBOSE) Log.v(TAG, "replaceSessionClose - close was already called"); - return; - } - + // Set up fast shutdown. Possible alternative paths: + // - This session is active, so close() below starts the shutdown drain + // - This session is mid-shutdown drain, and hasn't yet reached the idle drain listener. + // - This session is already closed and has executed the idle drain listener, and + // configureOutputs(null) has already been called. + // + // Do not call configureOutputs(null) going forward, since it would race with the + // configuration for the new session. If it was already called, then we don't care, since it + // won't get called again. mSkipUnconfigure = true; + close(); } @@ -347,7 +354,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { /** * Forward callbacks from - * CameraDevice.CaptureListener to the CameraCaptureSession.CaptureListener. + * CameraDeviceImpl.CaptureListener to the CameraCaptureSession.CaptureListener. * * <p>In particular, all calls are automatically split to go both to our own * internal listener, and to the user-specified listener (by transparently posting @@ -356,9 +363,9 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { * <p>When a capture sequence finishes, update the pending checked sequences set.</p> */ @SuppressWarnings("deprecation") - private CameraDevice.CaptureListener createCaptureListenerProxy( + private CameraDeviceImpl.CaptureListener createCaptureListenerProxy( Handler handler, CaptureListener listener) { - CameraDevice.CaptureListener localListener = new CameraDevice.CaptureListener() { + CameraDeviceImpl.CaptureListener localListener = new CameraDeviceImpl.CaptureListener() { @Override public void onCaptureSequenceCompleted(CameraDevice camera, int sequenceId, long frameNumber) { @@ -379,27 +386,30 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { * - then forward the call to a handler * - then finally invoke the destination method on the session listener object */ - Dispatchable<CaptureListener> userListenerSink; - if (listener == null) { // OK: API allows the user to not specify a listener - userListenerSink = new NullDispatcher<>(); - } else { - userListenerSink = new InvokeDispatcher<>(listener); + if (listener == null) { + // OK: API allows the user to not specify a listener, and the handler may + // also be null in that case. Collapse whole dispatch chain to only call the local + // listener + return localListener; } - InvokeDispatcher<CameraDevice.CaptureListener> localSink = + InvokeDispatcher<CameraDeviceImpl.CaptureListener> localSink = new InvokeDispatcher<>(localListener); + + InvokeDispatcher<CaptureListener> userListenerSink = + new InvokeDispatcher<>(listener); HandlerDispatcher<CaptureListener> handlerPassthrough = new HandlerDispatcher<>(userListenerSink, handler); - DuckTypingDispatcher<CameraDevice.CaptureListener, CaptureListener> duckToSession + DuckTypingDispatcher<CameraDeviceImpl.CaptureListener, CaptureListener> duckToSession = new DuckTypingDispatcher<>(handlerPassthrough, CaptureListener.class); - ArgumentReplacingDispatcher<CameraDevice.CaptureListener, CameraCaptureSessionImpl> - replaceDeviceWithSession = new ArgumentReplacingDispatcher<>(duckToSession, - /*argumentIndex*/0, this); + ArgumentReplacingDispatcher<CameraDeviceImpl.CaptureListener, CameraCaptureSessionImpl> + replaceDeviceWithSession = new ArgumentReplacingDispatcher<>(duckToSession, + /*argumentIndex*/0, this); - BroadcastDispatcher<CameraDevice.CaptureListener> broadcaster = - new BroadcastDispatcher<CameraDevice.CaptureListener>( - replaceDeviceWithSession, - localSink); + BroadcastDispatcher<CameraDeviceImpl.CaptureListener> broadcaster = + new BroadcastDispatcher<CameraDeviceImpl.CaptureListener>( + replaceDeviceWithSession, + localSink); return new ListenerProxies.DeviceCaptureListenerProxy(broadcaster); } @@ -415,10 +425,10 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { * </ul> * </p> * */ - CameraDevice.StateListener getDeviceStateListener() { + CameraDeviceImpl.StateListenerKK getDeviceStateListener() { final CameraCaptureSession session = this; - return new CameraDevice.StateListener() { + return new CameraDeviceImpl.StateListenerKK() { private boolean mBusy = false; private boolean mActive = false; @@ -596,6 +606,8 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { * * This operation is idempotent; a session will not be closed twice. */ + if (VERBOSE) Log.v(TAG, "Session drain complete, skip unconfigure: " + + mSkipUnconfigure); // Fast path: A new capture session has replaced this one; don't unconfigure. if (mSkipUnconfigure) { diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 18b12022ab35..71eb0e9f2883 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -21,8 +21,10 @@ import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.CaptureFailure; import android.hardware.camera2.ICameraDeviceCallbacks; import android.hardware.camera2.ICameraDeviceUser; import android.hardware.camera2.TotalCaptureResult; @@ -47,7 +49,7 @@ import java.util.TreeSet; /** * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate */ -public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { +public class CameraDeviceImpl extends CameraDevice { private final String TAG; private final boolean DEBUG; @@ -62,7 +64,7 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks(); private final StateListener mDeviceListener; - private volatile StateListener mSessionStateListener; + private volatile StateListenerKK mSessionStateListener; private final Handler mDeviceHandler; private volatile boolean mClosing = false; @@ -103,7 +105,7 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { private final Runnable mCallOnOpened = new Runnable() { @Override public void run() { - StateListener sessionListener = null; + StateListenerKK sessionListener = null; synchronized(mInterfaceLock) { if (mRemoteDevice == null) return; // Camera already closed @@ -119,7 +121,7 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { private final Runnable mCallOnUnconfigured = new Runnable() { @Override public void run() { - StateListener sessionListener = null; + StateListenerKK sessionListener = null; synchronized(mInterfaceLock) { if (mRemoteDevice == null) return; // Camera already closed @@ -128,14 +130,13 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { if (sessionListener != null) { sessionListener.onUnconfigured(CameraDeviceImpl.this); } - mDeviceListener.onUnconfigured(CameraDeviceImpl.this); } }; private final Runnable mCallOnActive = new Runnable() { @Override public void run() { - StateListener sessionListener = null; + StateListenerKK sessionListener = null; synchronized(mInterfaceLock) { if (mRemoteDevice == null) return; // Camera already closed @@ -144,14 +145,13 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { if (sessionListener != null) { sessionListener.onActive(CameraDeviceImpl.this); } - mDeviceListener.onActive(CameraDeviceImpl.this); } }; private final Runnable mCallOnBusy = new Runnable() { @Override public void run() { - StateListener sessionListener = null; + StateListenerKK sessionListener = null; synchronized(mInterfaceLock) { if (mRemoteDevice == null) return; // Camera already closed @@ -160,7 +160,6 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { if (sessionListener != null) { sessionListener.onBusy(CameraDeviceImpl.this); } - mDeviceListener.onBusy(CameraDeviceImpl.this); } }; @@ -172,7 +171,7 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { if (mClosedOnce) { throw new AssertionError("Don't post #onClosed more than once"); } - StateListener sessionListener = null; + StateListenerKK sessionListener = null; synchronized(mInterfaceLock) { sessionListener = mSessionStateListener; } @@ -187,7 +186,7 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { private final Runnable mCallOnIdle = new Runnable() { @Override public void run() { - StateListener sessionListener = null; + StateListenerKK sessionListener = null; synchronized(mInterfaceLock) { if (mRemoteDevice == null) return; // Camera already closed @@ -196,14 +195,13 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { if (sessionListener != null) { sessionListener.onIdle(CameraDeviceImpl.this); } - mDeviceListener.onIdle(CameraDeviceImpl.this); } }; private final Runnable mCallOnDisconnected = new Runnable() { @Override public void run() { - StateListener sessionListener = null; + StateListenerKK sessionListener = null; synchronized(mInterfaceLock) { if (mRemoteDevice == null) return; // Camera already closed @@ -313,7 +311,6 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { return mCameraId; } - @Override public void configureOutputs(List<Surface> outputs) throws CameraAccessException { // Treat a null input the same an empty list if (outputs == null) { @@ -390,7 +387,11 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { checkIfCameraClosedOrInError(); - // TODO: we must be in UNCONFIGURED mode to begin with, or using another session + // Notify current session that it's going away, before starting camera operations + // After this call completes, the session is not allowed to call into CameraDeviceImpl + if (mCurrentSession != null) { + mCurrentSession.replaceSessionClose(); + } // TODO: dont block for this boolean configureSuccess = true; @@ -410,10 +411,6 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { new CameraCaptureSessionImpl(outputs, listener, handler, this, mDeviceHandler, configureSuccess); - if (mCurrentSession != null) { - mCurrentSession.replaceSessionClose(newSession); - } - // TODO: wait until current session closes, then create the new session mCurrentSession = newSession; @@ -425,6 +422,15 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { } } + /** + * For use by backwards-compatibility code only. + */ + public void setSessionListener(StateListenerKK sessionListener) { + synchronized(mInterfaceLock) { + mSessionStateListener = sessionListener; + } + } + @Override public CaptureRequest.Builder createCaptureRequest(int templateType) throws CameraAccessException { @@ -449,7 +455,6 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { } } - @Override public int capture(CaptureRequest request, CaptureListener listener, Handler handler) throws CameraAccessException { if (DEBUG) { @@ -460,7 +465,6 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { return submitCaptureRequest(requestList, listener, handler, /*streaming*/false); } - @Override public int captureBurst(List<CaptureRequest> requests, CaptureListener listener, Handler handler) throws CameraAccessException { if (requests == null || requests.isEmpty()) { @@ -543,9 +547,7 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { // Need a valid handler, or current thread needs to have a looper, if // listener is valid - if (listener != null) { - handler = checkHandler(handler); - } + handler = checkHandler(handler, listener); // Make sure that there all requests have at least 1 surface; all surfaces are non-null for (CaptureRequest request : requestList) { @@ -613,7 +615,6 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { } } - @Override public int setRepeatingRequest(CaptureRequest request, CaptureListener listener, Handler handler) throws CameraAccessException { List<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); @@ -621,7 +622,6 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { return submitCaptureRequest(requestList, listener, handler, /*streaming*/true); } - @Override public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener, Handler handler) throws CameraAccessException { if (requests == null || requests.isEmpty()) { @@ -630,7 +630,6 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { return submitCaptureRequest(requests, listener, handler, /*streaming*/true); } - @Override public void stopRepeating() throws CameraAccessException { synchronized(mInterfaceLock) { @@ -681,7 +680,6 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { } } - @Override public void flush() throws CameraAccessException { synchronized(mInterfaceLock) { checkIfCameraClosedOrInError(); @@ -739,6 +737,133 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { } } + /** + * <p>A listener for tracking the progress of a {@link CaptureRequest} + * submitted to the camera device.</p> + * + */ + public static abstract class CaptureListener { + + /** + * This constant is used to indicate that no images were captured for + * the request. + * + * @hide + */ + public static final int NO_FRAMES_CAPTURED = -1; + + /** + * This method is called when the camera device has started capturing + * the output image for the request, at the beginning of image exposure. + * + * @see android.media.MediaActionSound + */ + public void onCaptureStarted(CameraDevice camera, + CaptureRequest request, long timestamp) { + // default empty implementation + } + + /** + * This method is called when some results from an image capture are + * available. + * + * @hide + */ + public void onCapturePartial(CameraDevice camera, + CaptureRequest request, CaptureResult result) { + // default empty implementation + } + + /** + * This method is called when an image capture makes partial forward progress; some + * (but not all) results from an image capture are available. + * + */ + public void onCaptureProgressed(CameraDevice camera, + CaptureRequest request, CaptureResult partialResult) { + // default empty implementation + } + + /** + * This method is called when an image capture has fully completed and all the + * result metadata is available. + */ + public void onCaptureCompleted(CameraDevice camera, + CaptureRequest request, TotalCaptureResult result) { + // default empty implementation + } + + /** + * This method is called instead of {@link #onCaptureCompleted} when the + * camera device failed to produce a {@link CaptureResult} for the + * request. + */ + public void onCaptureFailed(CameraDevice camera, + CaptureRequest request, CaptureFailure failure) { + // default empty implementation + } + + /** + * This method is called independently of the others in CaptureListener, + * when a capture sequence finishes and all {@link CaptureResult} + * or {@link CaptureFailure} for it have been returned via this listener. + */ + public void onCaptureSequenceCompleted(CameraDevice camera, + int sequenceId, long frameNumber) { + // default empty implementation + } + + /** + * This method is called independently of the others in CaptureListener, + * when a capture sequence aborts before any {@link CaptureResult} + * or {@link CaptureFailure} for it have been returned via this listener. + */ + public void onCaptureSequenceAborted(CameraDevice camera, + int sequenceId) { + // default empty implementation + } + } + + /** + * A listener for notifications about the state of a camera device, adding in the callbacks that + * were part of the earlier KK API design, but now only used internally. + */ + public static abstract class StateListenerKK extends StateListener { + /** + * The method called when a camera device has no outputs configured. + * + */ + public void onUnconfigured(CameraDevice camera) { + // Default empty implementation + } + + /** + * The method called when a camera device begins processing + * {@link CaptureRequest capture requests}. + * + */ + public void onActive(CameraDevice camera) { + // Default empty implementation + } + + /** + * The method called when a camera device is busy. + * + */ + public void onBusy(CameraDevice camera) { + // Default empty implementation + } + + /** + * The method called when a camera device has finished processing all + * submitted capture requests and has reached an idle state. + * + */ + public void onIdle(CameraDevice camera) { + // Default empty implementation + } + } + static class CaptureListenerHolder { private final boolean mRepeating; @@ -1155,6 +1280,18 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { return handler; } + /** + * Default handler management, conditional on there being a listener. + * + * <p>If the listener isn't null, check the handler, otherwise pass it through.</p> + */ + static <T> Handler checkHandler(Handler handler, T listener) { + if (listener != null) { + return checkHandler(handler); + } + return handler; + } + private void checkIfCameraClosedOrInError() throws CameraAccessException { if (mInError) { throw new CameraAccessException(CameraAccessException.CAMERA_ERROR, diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index dc71a060f91e..febb015a7fd0 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -67,6 +67,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; /** * Implementation of camera metadata marshal/unmarshal across Binder to @@ -227,6 +228,7 @@ public class CameraMetadataNative implements Parcelable { private static final String CELLID_PROCESS = "CELLID"; private static final String GPS_PROCESS = "GPS"; + private static final int FACE_LANDMARK_SIZE = 6; private static String translateLocationProviderToProcess(final String provider) { if (provider == null) { @@ -347,7 +349,7 @@ public class CameraMetadataNative implements Parcelable { // Check if key has been overridden to use a wrapper class on the java side. GetCommand g = sGetCommandMap.get(key); if (g != null) { - return (T) g.getValue(this, key); + return g.getValue(this, key); } return getBase(key); } @@ -587,9 +589,71 @@ public class CameraMetadataNative implements Parcelable { return availableFormats; } - private Face[] getFaces() { - final int FACE_LANDMARK_SIZE = 6; + private boolean setFaces(Face[] faces) { + if (faces == null) { + return false; + } + + int numFaces = faces.length; + + // Detect if all faces are SIMPLE or not; count # of valid faces + boolean fullMode = true; + for (Face face : faces) { + if (face == null) { + numFaces--; + Log.w(TAG, "setFaces - null face detected, skipping"); + continue; + } + + if (face.getId() == Face.ID_UNSUPPORTED) { + fullMode = false; + } + } + + Rect[] faceRectangles = new Rect[numFaces]; + byte[] faceScores = new byte[numFaces]; + int[] faceIds = null; + int[] faceLandmarks = null; + + if (fullMode) { + faceIds = new int[numFaces]; + faceLandmarks = new int[numFaces * FACE_LANDMARK_SIZE]; + } + + int i = 0; + for (Face face : faces) { + if (face == null) { + continue; + } + + faceRectangles[i] = face.getBounds(); + faceScores[i] = (byte)face.getScore(); + + if (fullMode) { + faceIds[i] = face.getId(); + int j = 0; + + faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getLeftEyePosition().x; + faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getLeftEyePosition().y; + faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getRightEyePosition().x; + faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getRightEyePosition().y; + faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getMouthPosition().x; + faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getMouthPosition().y; + } + + i++; + } + + set(CaptureResult.STATISTICS_FACE_RECTANGLES, faceRectangles); + set(CaptureResult.STATISTICS_FACE_IDS, faceIds); + set(CaptureResult.STATISTICS_FACE_LANDMARKS, faceLandmarks); + set(CaptureResult.STATISTICS_FACE_SCORES, faceScores); + + return true; + } + + private Face[] getFaces() { Integer faceDetectMode = get(CaptureResult.STATISTICS_FACE_DETECT_MODE); if (faceDetectMode == null) { Log.w(TAG, "Face detect mode metadata is null, assuming the mode is SIMPLE"); @@ -653,9 +717,12 @@ public class CameraMetadataNative implements Parcelable { if (faceScores[i] <= Face.SCORE_MAX && faceScores[i] >= Face.SCORE_MIN && faceIds[i] >= 0) { - Point leftEye = new Point(faceLandmarks[i*6], faceLandmarks[i*6+1]); - Point rightEye = new Point(faceLandmarks[i*6+2], faceLandmarks[i*6+3]); - Point mouth = new Point(faceLandmarks[i*6+4], faceLandmarks[i*6+5]); + Point leftEye = new Point(faceLandmarks[i*FACE_LANDMARK_SIZE], + faceLandmarks[i*FACE_LANDMARK_SIZE+1]); + Point rightEye = new Point(faceLandmarks[i*FACE_LANDMARK_SIZE+2], + faceLandmarks[i*FACE_LANDMARK_SIZE+3]); + Point mouth = new Point(faceLandmarks[i*FACE_LANDMARK_SIZE+4], + faceLandmarks[i*FACE_LANDMARK_SIZE+5]); Face face = new Face(faceRectangles[i], faceScores[i], faceIds[i], leftEye, rightEye, mouth); faceList.add(face); @@ -865,6 +932,13 @@ public class CameraMetadataNative implements Parcelable { metadata.setFaceRectangles((Rect[]) value); } }); + sSetCommandMap.put(CaptureResult.STATISTICS_FACES.getNativeKey(), + new SetCommand() { + @Override + public <T> void setValue(CameraMetadataNative metadata, T value) { + metadata.setFaces((Face[])value); + } + }); sSetCommandMap.put(CaptureRequest.TONEMAP_CURVE.getNativeKey(), new SetCommand() { @Override public <T> void setValue(CameraMetadataNative metadata, T value) { diff --git a/core/java/android/hardware/camera2/impl/ListenerProxies.java b/core/java/android/hardware/camera2/impl/ListenerProxies.java index ab9a4d5c6292..f44f9ada5249 100644 --- a/core/java/android/hardware/camera2/impl/ListenerProxies.java +++ b/core/java/android/hardware/camera2/impl/ListenerProxies.java @@ -36,13 +36,13 @@ public class ListenerProxies { // TODO: replace with codegen - public static class DeviceStateListenerProxy extends CameraDevice.StateListener { - private final MethodNameInvoker<CameraDevice.StateListener> mProxy; + public static class DeviceStateListenerProxy extends CameraDeviceImpl.StateListenerKK { + private final MethodNameInvoker<CameraDeviceImpl.StateListenerKK> mProxy; public DeviceStateListenerProxy( - Dispatchable<CameraDevice.StateListener> dispatchTarget) { + Dispatchable<CameraDeviceImpl.StateListenerKK> dispatchTarget) { dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null"); - mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDevice.StateListener.class); + mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDeviceImpl.StateListenerKK.class); } @Override @@ -87,13 +87,13 @@ public class ListenerProxies { } @SuppressWarnings("deprecation") - public static class DeviceCaptureListenerProxy extends CameraDevice.CaptureListener { - private final MethodNameInvoker<CameraDevice.CaptureListener> mProxy; + public static class DeviceCaptureListenerProxy extends CameraDeviceImpl.CaptureListener { + private final MethodNameInvoker<CameraDeviceImpl.CaptureListener> mProxy; public DeviceCaptureListenerProxy( - Dispatchable<CameraDevice.CaptureListener> dispatchTarget) { + Dispatchable<CameraDeviceImpl.CaptureListener> dispatchTarget) { dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null"); - mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDevice.CaptureListener.class); + mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDeviceImpl.CaptureListener.class); } @Override diff --git a/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java b/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java new file mode 100644 index 000000000000..1470b70b4234 --- /dev/null +++ b/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.camera2.legacy; + +import android.graphics.Rect; +import android.hardware.Camera; +import android.hardware.Camera.FaceDetectionListener; +import android.hardware.camera2.impl.CameraMetadataNative; +import android.hardware.camera2.legacy.ParameterUtils.ZoomData; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.params.Face; +import android.hardware.camera2.utils.ListUtils; +import android.hardware.camera2.utils.ParamsUtils; +import android.util.Log; +import android.util.Size; + +import com.android.internal.util.ArrayUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static android.hardware.camera2.CaptureRequest.*; +import static com.android.internal.util.Preconditions.*; + +/** + * Map legacy face detect callbacks into face detection results. + */ +@SuppressWarnings("deprecation") +public class LegacyFaceDetectMapper { + private static String TAG = "LegacyFaceDetectMapper"; + private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); + + private final Camera mCamera; + private final boolean mFaceDetectSupported; + private boolean mFaceDetectEnabled = false; + + private final Object mLock = new Object(); + private Camera.Face[] mFaces; + private Camera.Face[] mFacesPrev; + /** + * Instantiate a new face detect mapper. + * + * @param camera a non-{@code null} camera1 device + * @param characteristics a non-{@code null} camera characteristics for that camera1 + * + * @throws NullPointerException if any of the args were {@code null} + */ + public LegacyFaceDetectMapper(Camera camera, CameraCharacteristics characteristics) { + mCamera = checkNotNull(camera, "camera must not be null"); + checkNotNull(characteristics, "characteristics must not be null"); + + mFaceDetectSupported = ArrayUtils.contains( + characteristics.get( + CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES), + STATISTICS_FACE_DETECT_MODE_SIMPLE); + + if (!mFaceDetectSupported) { + return; + } + + mCamera.setFaceDetectionListener(new FaceDetectionListener() { + + @Override + public void onFaceDetection(Camera.Face[] faces, Camera camera) { + int lengthFaces = faces == null ? 0 : faces.length; + synchronized (mLock) { + if (mFaceDetectEnabled) { + mFaces = faces; + } else if (lengthFaces > 0) { + // stopFaceDetectMode could race against the requests, print a debug log + Log.d(TAG, + "onFaceDetection - Ignored some incoming faces since" + + "face detection was disabled"); + } + } + + if (VERBOSE) { + Log.v(TAG, "onFaceDetection - read " + lengthFaces + " faces"); + } + } + }); + } + + /** + * Process the face detect mode from the capture request into an api1 face detect toggle. + * + * <p>This method should be called after the parameters are {@link LegacyRequestMapper mapped} + * with the request.</p> + * + * <p>Callbacks are processed in the background, and the next call to {@link #mapResultTriggers} + * will have the latest faces detected as reflected by the camera1 callbacks.</p> + * + * <p>None of the arguments will be mutated.</p> + * + * @param captureRequest a non-{@code null} request + * @param parameters a non-{@code null} parameters corresponding to this request (read-only) + */ + public void processFaceDetectMode(CaptureRequest captureRequest, + Camera.Parameters parameters) { + checkNotNull(captureRequest, "captureRequest must not be null"); + + /* + * statistics.faceDetectMode + */ + int fdMode = ParamsUtils.getOrDefault(captureRequest, STATISTICS_FACE_DETECT_MODE, + STATISTICS_FACE_DETECT_MODE_OFF); + + if (fdMode != STATISTICS_FACE_DETECT_MODE_OFF && !mFaceDetectSupported) { + Log.w(TAG, + "processFaceDetectMode - Ignoring statistics.faceDetectMode; " + + "face detection is not available"); + return; + } + + // Print some warnings out in case the values were wrong + switch (fdMode) { + case STATISTICS_FACE_DETECT_MODE_OFF: + case STATISTICS_FACE_DETECT_MODE_SIMPLE: + break; + case STATISTICS_FACE_DETECT_MODE_FULL: + Log.w(TAG, + "processFaceDetectMode - statistics.faceDetectMode == FULL unsupported, " + + "downgrading to SIMPLE"); + break; + default: + Log.w(TAG, "processFaceDetectMode - ignoring unknown statistics.faceDetectMode = " + + fdMode); + return; + } + + boolean enableFaceDetect = fdMode != STATISTICS_FACE_DETECT_MODE_OFF; + synchronized (mLock) { + // Enable/disable face detection if it's changed since last time + if (enableFaceDetect != mFaceDetectEnabled) { + if (enableFaceDetect) { + mCamera.startFaceDetection(); + + if (VERBOSE) { + Log.v(TAG, "processFaceDetectMode - start face detection"); + } + } else { + mCamera.stopFaceDetection(); + + if (VERBOSE) { + Log.v(TAG, "processFaceDetectMode - stop face detection"); + } + + mFaces = null; + } + + mFaceDetectEnabled = enableFaceDetect; + } + } + } + + /** + * Update the {@code result} camera metadata map with the new value for the + * {@code statistics.faces} and {@code statistics.faceDetectMode}. + * + * <p>Face detect callbacks are processed in the background, and each call to + * {@link #mapResultFaces} will have the latest faces as reflected by the camera1 callbacks.</p> + * + * @param result a non-{@code null} result + * @param legacyRequest a non-{@code null} request (read-only) + */ + public void mapResultFaces(CameraMetadataNative result, LegacyRequest legacyRequest) { + checkNotNull(result, "result must not be null"); + checkNotNull(legacyRequest, "legacyRequest must not be null"); + + Camera.Face[] faces, previousFaces; + int fdMode; + synchronized (mLock) { + fdMode = mFaceDetectEnabled ? + STATISTICS_FACE_DETECT_MODE_SIMPLE : STATISTICS_FACE_DETECT_MODE_OFF; + + if (mFaceDetectEnabled) { + faces = mFaces; + } else { + faces = null; + } + + previousFaces = mFacesPrev; + mFacesPrev = faces; + } + + CameraCharacteristics characteristics = legacyRequest.characteristics; + CaptureRequest request = legacyRequest.captureRequest; + Size previewSize = legacyRequest.previewSize; + Camera.Parameters params = legacyRequest.parameters; + + Rect activeArray = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); + ZoomData zoomData = ParameterUtils.convertScalerCropRegion(activeArray, + request.get(CaptureRequest.SCALER_CROP_REGION), previewSize, params); + + List<Face> convertedFaces = new ArrayList<>(); + if (faces != null) { + for (Camera.Face face : faces) { + if (face != null) { + convertedFaces.add( + ParameterUtils.convertFaceFromLegacy(face, activeArray, zoomData)); + } else { + Log.w(TAG, "mapResultFaces - read NULL face from camera1 device"); + } + } + } + + if (VERBOSE && previousFaces != faces) { // Log only in verbose and IF the faces changed + Log.v(TAG, "mapResultFaces - changed to " + ListUtils.listToString(convertedFaces)); + } + + result.set(CaptureResult.STATISTICS_FACES, convertedFaces.toArray(new Face[0])); + result.set(CaptureResult.STATISTICS_FACE_DETECT_MODE, fdMode); + } +} diff --git a/core/java/android/hardware/camera2/legacy/LegacyFocusStateMapper.java b/core/java/android/hardware/camera2/legacy/LegacyFocusStateMapper.java index e576b43da154..d0a3a3fd3e68 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyFocusStateMapper.java +++ b/core/java/android/hardware/camera2/legacy/LegacyFocusStateMapper.java @@ -247,7 +247,8 @@ public class LegacyFocusStateMapper { // No action necessary. The callbacks will handle transitions. break; default: - Log.w(TAG, "mapTriggers - ignoring unknown control.afTrigger = " + afTrigger); + Log.w(TAG, "processRequestTriggers - ignoring unknown control.afTrigger = " + + afTrigger); } } diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java index 711edf4762e7..b05508b25824 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java +++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java @@ -204,6 +204,11 @@ public class LegacyMetadataMapper { mapSensor(m, p); /* + * statistics.* + */ + mapStatistics(m, p); + + /* * sync.* */ mapSync(m, p); @@ -487,6 +492,18 @@ public class LegacyMetadataMapper { private static void mapControlOther(CameraMetadataNative m, Camera.Parameters p) { /* + * android.control.availableVideoStabilizationModes + */ + { + int stabModes[] = p.isVideoStabilizationSupported() ? + new int[] { CONTROL_VIDEO_STABILIZATION_MODE_OFF, + CONTROL_VIDEO_STABILIZATION_MODE_ON } : + new int[] { CONTROL_VIDEO_STABILIZATION_MODE_OFF }; + + m.set(CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES, stabModes); + } + + /* * android.control.maxRegions */ final int AE = 0, AWB = 1, AF = 2; @@ -742,6 +759,31 @@ public class LegacyMetadataMapper { m.set(SENSOR_INFO_PIXEL_ARRAY_SIZE, largestJpegSize); } + private static void mapStatistics(CameraMetadataNative m, Parameters p) { + /* + * statistics.info.availableFaceDetectModes + */ + int[] fdModes; + + if (p.getMaxNumDetectedFaces() > 0) { + fdModes = new int[] { + STATISTICS_FACE_DETECT_MODE_OFF, + STATISTICS_FACE_DETECT_MODE_SIMPLE + // FULL is never-listed, since we have no way to query it statically + }; + } else { + fdModes = new int[] { + STATISTICS_FACE_DETECT_MODE_OFF + }; + } + m.set(STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES, fdModes); + + /* + * statistics.info.maxFaceCount + */ + m.set(STATISTICS_INFO_MAX_FACE_COUNT, p.getMaxNumDetectedFaces()); + } + private static void mapSync(CameraMetadataNative m, Parameters p) { /* * sync.maxLatency diff --git a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java index a6fe035ca144..20f3fd2f7b5b 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java +++ b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java @@ -150,10 +150,8 @@ public class LegacyRequestMapper { if (supported) { params.setPreviewFpsRange(legacyFps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX], legacyFps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]); - params.setRecordingHint(false); } else { Log.w(TAG, "Unsupported FPS range set [" + legacyFps[0] + "," + legacyFps[1] + "]"); - params.setRecordingHint(true); } } @@ -248,6 +246,18 @@ public class LegacyRequestMapper { // TODO: Don't add control.awbLock to availableRequestKeys if it's not supported } + // control.videoStabilizationMode + { + Integer stabMode = getIfSupported(request, CONTROL_VIDEO_STABILIZATION_MODE, + /*defaultValue*/CONTROL_VIDEO_STABILIZATION_MODE_OFF, + params.isVideoStabilizationSupported(), + /*allowedValue*/CONTROL_VIDEO_STABILIZATION_MODE_OFF); + + if (stabMode != null) { + params.setVideoStabilization(stabMode == CONTROL_VIDEO_STABILIZATION_MODE_ON); + } + } + // lens.focusDistance { boolean infinityFocusSupported = diff --git a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java index 9eff943ac268..a2487f4bd9b8 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java +++ b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java @@ -35,6 +35,9 @@ import java.util.ArrayList; import java.util.List; import static com.android.internal.util.Preconditions.*; +import static android.hardware.camera2.CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_OFF; +import static android.hardware.camera2.CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_ON; +import static android.hardware.camera2.CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE; import static android.hardware.camera2.CaptureResult.*; /** @@ -142,7 +145,6 @@ public class LegacyResultMapper { */ mapAwb(result, /*out*/params); - /* * control.mode */ @@ -171,7 +173,6 @@ public class LegacyResultMapper { } } - /* * control.effectMode */ @@ -187,6 +188,15 @@ public class LegacyResultMapper { } } + // control.videoStabilizationMode + { + int stabMode = + (params.isVideoStabilizationSupported() && params.getVideoStabilization()) ? + CONTROL_VIDEO_STABILIZATION_MODE_ON : + CONTROL_VIDEO_STABILIZATION_MODE_OFF; + result.set(CONTROL_VIDEO_STABILIZATION_MODE, stabMode); + } + /* * flash */ diff --git a/core/java/android/hardware/camera2/legacy/ParameterUtils.java b/core/java/android/hardware/camera2/legacy/ParameterUtils.java index efd12f289626..385f8440afb2 100644 --- a/core/java/android/hardware/camera2/legacy/ParameterUtils.java +++ b/core/java/android/hardware/camera2/legacy/ParameterUtils.java @@ -43,6 +43,7 @@ import static com.android.internal.util.Preconditions.*; /** * Various utilities for dealing with camera API1 parameters. */ +@SuppressWarnings("deprecation") public class ParameterUtils { /** Upper/left minimal point of a normalized rectangle */ public static final int NORMALIZED_RECTANGLE_MIN = -1000; @@ -164,19 +165,23 @@ public class ParameterUtils { * <p>If the score is out of range of {@value Face#SCORE_MIN}, {@value Face#SCORE_MAX}, * the score is clipped first and a warning is printed to logcat.</p> * + * <p>If the id is negative, the id is changed to 0 and a warning is printed to + * logcat.</p> + * * <p>All other parameters are passed-through as-is.</p> * * @return a new face with the optional features set */ public Face toFace( int id, Point leftEyePosition, Point rightEyePosition, Point mouthPosition) { + int idSafe = clipLower(id, /*lo*/0, rect, "id"); int score = clip(weight, Face.SCORE_MIN, Face.SCORE_MAX, rect, "score"); - return new Face(rect, score, id, leftEyePosition, rightEyePosition, mouthPosition); + return new Face(rect, score, idSafe, leftEyePosition, rightEyePosition, mouthPosition); } /** @@ -861,6 +866,61 @@ public class ParameterUtils { /*usePreviewCrop*/true); } + /** + * Convert an api1 face into an active-array based api2 face. + * + * <p>Out-of-ranges scores and ids will be clipped to be within range (with a warning).</p> + * + * @param face a non-{@code null} api1 face + * @param activeArraySize active array size of the sensor (e.g. max jpeg size) + * @param zoomData the calculated zoom data corresponding to this request + * + * @return a non-{@code null} api2 face + * + * @throws NullPointerException if the {@code face} was {@code null} + */ + public static Face convertFaceFromLegacy(Camera.Face face, Rect activeArray, + ZoomData zoomData) { + checkNotNull(face, "face must not be null"); + + Face api2Face; + + Camera.Area fakeArea = new Camera.Area(face.rect, /*weight*/1); + + WeightedRectangle faceRect = + convertCameraAreaToActiveArrayRectangle(activeArray, zoomData, fakeArea); + + Point leftEye = face.leftEye, rightEye = face.rightEye, mouth = face.mouth; + if (leftEye != null && rightEye != null && mouth != null) { + leftEye = convertCameraPointToActiveArrayPoint(activeArray, zoomData, + leftEye, /*usePreviewCrop*/true); + rightEye = convertCameraPointToActiveArrayPoint(activeArray, zoomData, + leftEye, /*usePreviewCrop*/true); + mouth = convertCameraPointToActiveArrayPoint(activeArray, zoomData, + leftEye, /*usePreviewCrop*/true); + + api2Face = faceRect.toFace(face.id, leftEye, rightEye, mouth); + } else { + api2Face = faceRect.toFace(); + } + + return api2Face; + } + + private static Point convertCameraPointToActiveArrayPoint( + Rect activeArray, ZoomData zoomData, Point point, boolean usePreviewCrop) { + Rect pointedRect = new Rect(point.x, point.y, point.x, point.y); + Camera.Area pointedArea = new Area(pointedRect, /*weight*/1); + + WeightedRectangle adjustedRect = + convertCameraAreaToActiveArrayRectangle(activeArray, + zoomData, pointedArea, usePreviewCrop); + + Point transformedPoint = new Point(adjustedRect.rect.left, adjustedRect.rect.top); + + return transformedPoint; + } + private static WeightedRectangle convertCameraAreaToActiveArrayRectangle( Rect activeArray, ZoomData zoomData, Camera.Area area, boolean usePreviewCrop) { Rect previewCrop = zoomData.previewCrop; diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java index c556c3286ae8..ec233da79db2 100644 --- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java +++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java @@ -39,7 +39,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.TimeUnit; import static com.android.internal.util.Preconditions.*; @@ -55,18 +54,23 @@ import static com.android.internal.util.Preconditions.*; * - An {@link CameraDeviceState} state machine that manages the callbacks for various operations. * </p> */ +@SuppressWarnings("deprecation") public class RequestThreadManager { private final String TAG; private final int mCameraId; private final RequestHandlerThread mRequestThread; private static final boolean DEBUG = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG); + // 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 final CameraCharacteristics mCharacteristics; private final CameraDeviceState mDeviceState; private final CaptureCollector mCaptureCollector; private final LegacyFocusStateMapper mFocusStateMapper; + private final LegacyFaceDetectMapper mFaceDetectMapper; private static final int MSG_CONFIGURE_OUTPUTS = 1; private static final int MSG_SUBMIT_CAPTURE_REQUEST = 2; @@ -219,6 +223,9 @@ public class RequestThreadManager { }; private void stopPreview() { + if (VERBOSE) { + Log.v(TAG, "stopPreview - preview running? " + mPreviewRunning); + } if (mPreviewRunning) { mCamera.stopPreview(); mPreviewRunning = false; @@ -226,14 +233,18 @@ public class RequestThreadManager { } private void startPreview() { + if (VERBOSE) { + Log.v(TAG, "startPreview - preview running? " + mPreviewRunning); + } if (!mPreviewRunning) { + // XX: CameraClient:;startPreview is not getting called after a stop mCamera.startPreview(); mPreviewRunning = true; } } - private void doJpegCapture(RequestHolder request) throws IOException { - if (DEBUG) Log.d(TAG, "doJpegCapture"); + private void doJpegCapturePrepare(RequestHolder request) throws IOException { + if (DEBUG) Log.d(TAG, "doJpegCapturePrepare - preview running? " + mPreviewRunning); if (!mPreviewRunning) { if (DEBUG) Log.d(TAG, "doJpegCapture - create fake surface"); @@ -242,11 +253,20 @@ public class RequestThreadManager { mCamera.setPreviewTexture(mDummyTexture); startPreview(); } + } + + private void doJpegCapture(RequestHolder request) { + if (DEBUG) Log.d(TAG, "doJpegCapturePrepare"); + mCamera.takePicture(mJpegShutterCallback, /*raw*/null, mJpegCallback); mPreviewRunning = false; } private void doPreviewCapture(RequestHolder request) throws IOException { + if (VERBOSE) { + Log.v(TAG, "doPreviewCapture - preview running? " + mPreviewRunning); + } + if (mPreviewRunning) { return; // Already running } @@ -264,7 +284,20 @@ public class RequestThreadManager { } private void configureOutputs(Collection<Surface> outputs) throws IOException { + if (DEBUG) { + String outputsStr = outputs == null ? "null" : (outputs.size() + " surfaces"); + + Log.d(TAG, "configureOutputs with " + outputsStr); + } + stopPreview(); + /* + * 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 + * when disconnecting from the old previous texture at a later time. + */ + mCamera.setPreviewTexture(/*surfaceTexture*/null); + if (mGLThreadManager != null) { mGLThreadManager.waitUntilStarted(); mGLThreadManager.ignoreNewFrames(); @@ -305,7 +338,6 @@ public class RequestThreadManager { } mParams.setPreviewFpsRange(bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX], bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]); - mParams.setRecordingHint(true); if (mPreviewOutputs.size() > 0) { List<Size> outputSizes = new ArrayList<>(outputs.size()); @@ -575,7 +607,6 @@ public class RequestThreadManager { Log.e(TAG, "Interrupted while waiting for requests to complete."); } mDeviceState.setIdle(); - stopPreview(); break; } else { // Queue another capture if we did not get the last burst. @@ -613,10 +644,6 @@ public class RequestThreadManager { } } - // Unconditionally process AF triggers, since they're non-idempotent - // - must be done after setting the most-up-to-date AF mode - mFocusStateMapper.processRequestTriggers(request, mParams); - try { boolean success = mCaptureCollector.queueRequest(holder, mLastRequest, JPEG_FRAME_TIMEOUT, TimeUnit.MILLISECONDS); @@ -624,6 +651,8 @@ public class RequestThreadManager { if (!success) { Log.e(TAG, "Timed out while queueing capture request."); } + // Starting the preview needs to happen before enabling + // face detection or auto focus if (holder.hasPreviewTargets()) { doPreviewCapture(holder); } @@ -635,12 +664,33 @@ public class RequestThreadManager { Log.e(TAG, "Timed out waiting for prior requests to complete."); } mReceivedJpeg.close(); + doJpegCapturePrepare(holder); + if (!mReceivedJpeg.block(JPEG_FRAME_TIMEOUT)) { + // TODO: report error to CameraDevice + Log.e(TAG, "Hit timeout for jpeg callback!"); + } + } + + /* + * Do all the actions that require a preview to have been started + */ + + // Toggle face detection on/off + // - do this before AF to give AF a chance to use faces + mFaceDetectMapper.processFaceDetectMode(request, /*in*/mParams); + + // Unconditionally process AF triggers, since they're non-idempotent + // - must be done after setting the most-up-to-date AF mode + mFocusStateMapper.processRequestTriggers(request, mParams); + + if (holder.hasJpegTargets()) { doJpegCapture(holder); if (!mReceivedJpeg.block(JPEG_FRAME_TIMEOUT)) { // TODO: report error to CameraDevice Log.e(TAG, "Hit timeout for jpeg callback!"); } } + } catch (IOException e) { // TODO: report error to CameraDevice throw new IOError(e); @@ -677,6 +727,8 @@ public class RequestThreadManager { mLastRequest, timestampMutable.value); // Update AF state mFocusStateMapper.mapResultTriggers(result); + // Update detected faces list + mFaceDetectMapper.mapResultFaces(result, mLastRequest); mDeviceState.setCaptureResult(holder, result); } @@ -731,6 +783,7 @@ public class RequestThreadManager { TAG = name; mDeviceState = checkNotNull(deviceState, "deviceState must not be null"); mFocusStateMapper = new LegacyFocusStateMapper(mCamera); + mFaceDetectMapper = new LegacyFaceDetectMapper(mCamera, mCharacteristics); mCaptureCollector = new CaptureCollector(MAX_IN_FLIGHT_REQUESTS, mDeviceState); mRequestThread = new RequestHandlerThread(name, mRequestHandlerCb); } diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java index 1efabb1212bd..2e6b9aefc7d7 100644 --- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java +++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java @@ -813,6 +813,7 @@ public final class StreamConfigurationMap { switch (format) { case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: case HAL_PIXEL_FORMAT_BLOB: + case HAL_PIXEL_FORMAT_RAW_OPAQUE: return format; case ImageFormat.JPEG: throw new IllegalArgumentException( @@ -843,12 +844,6 @@ public final class StreamConfigurationMap { * @throws IllegalArgumentException if the format was not user-defined */ static int checkArgumentFormat(int format) { - // TODO: remove this hack , CTS shouldn't have been using internal constants - if (format == HAL_PIXEL_FORMAT_RAW_OPAQUE) { - Log.w(TAG, "RAW_OPAQUE is not yet a published format; allowing it anyway"); - return format; - } - if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) { throw new IllegalArgumentException(String.format( "format 0x%x was not defined in either ImageFormat or PixelFormat", format)); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 04e6227ca11e..30875060d35b 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -864,15 +864,6 @@ public class UserManager { } /** - * Kept during L development to simplify updating unbundled apps. - * TODO: Remove after 2014-08-04 - * @hide - */ - public String getBadgedLabelForUser(String label, UserHandle user) { - return (String) getBadgedLabelForUser((CharSequence) label, user); - } - - /** * If the target user is a managed profile of the calling user or the caller * is itself a managed profile, then this returns a drawable to use as a small * icon to include in a view to distinguish it from the original icon. diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 129476c91050..60ef6936783f 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -3996,6 +3996,22 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te return super.onGenericMotionEvent(event); } + /** + * Initiate a fling with the given velocity. + * + * <p>Applications can use this method to manually initiate a fling as if the user + * initiated it via touch interaction.</p> + * + * @param velocityY Vertical velocity in pixels per second + */ + public void fling(int velocityY) { + if (mFlingRunnable == null) { + mFlingRunnable = new FlingRunnable(); + } + reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING); + mFlingRunnable.start(-velocityY); + } + @Override public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { return ((nestedScrollAxes & SCROLL_AXIS_VERTICAL) != 0); diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java index 59ec6d3da37c..8102c4acf364 100644 --- a/core/java/android/widget/TimePickerClockDelegate.java +++ b/core/java/android/widget/TimePickerClockDelegate.java @@ -18,6 +18,7 @@ package android.widget; import android.content.Context; import android.content.res.Configuration; +import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Color; import android.os.Parcel; @@ -171,7 +172,10 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate { mMinuteSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT); /* Get the localized am/pm strings and use them in the spinner */ - mAmPmStrings = new DateFormatSymbols().getAmPmStrings(); + final Resources res = context.getResources(); + final String amText = res.getString(R.string.time_picker_am_label); + final String pmText = res.getString(R.string.time_picker_pm_label); + mAmPmStrings = new String[] {amText, pmText}; // am/pm View amPmView = mDelegator.findViewById(R.id.amPm); diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java index 4e9a39fea095..523965c96e9e 100644 --- a/core/java/android/widget/TimePickerSpinnerDelegate.java +++ b/core/java/android/widget/TimePickerSpinnerDelegate.java @@ -134,6 +134,8 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate im mSelectHours = res.getString(R.string.select_hours); mMinutePickerDescription = res.getString(R.string.minute_picker_description); mSelectMinutes = res.getString(R.string.select_minutes); + mAmText = res.getString(R.string.time_picker_am_label); + mPmText = res.getString(R.string.time_picker_pm_label); final int layoutResourceId = a.getResourceId(R.styleable.TimePicker_internalLayout, R.layout.time_picker_holo); @@ -182,10 +184,6 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate im R.id.radial_picker); mDoneButton = (Button) mainView.findViewById(R.id.done_button); - String[] amPmTexts = new DateFormatSymbols().getAmPmStrings(); - mAmText = amPmTexts[0]; - mPmText = amPmTexts[1]; - setupListeners(); mAllowAutoAdvance = true; diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 2a7bffd8ea73..bef98d57e133 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4134,6 +4134,10 @@ <string name="time_picker_increment_set_pm_button">Set PM</string> <!-- Description of the button to decrease the TimePicker's set AM value. [CHAR LIMIT=NONE] --> <string name="time_picker_decrement_set_am_button">Set AM</string> + <!-- Label for the TimePicker's PM button. [CHAR LIMIT=2] --> + <string name="time_picker_pm_label">PM</string> + <!-- Label for the TimePicker's AM button. [CHAR LIMIT=2] --> + <string name="time_picker_am_label">AM</string> <!-- DatePicker - accessibility support --> <!-- Description of the button to increase the DatePicker's month value. [CHAR LIMIT=NONE] --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 84bc62c9cd3c..603071511e4e 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1969,9 +1969,7 @@ <java-symbol type="array" name="config_cdma_home_system" /> <java-symbol type="attr" name="headerSelectedTextColor" /> <java-symbol type="attr" name="amPmSelectedBackgroundColor" /> - - <!--From SmsMessage--> - <!--Support decoding the user data payload as pack GSM 8-bit (a GSM alphabet - string that's stored in 8-bit unpacked format) characters.--> <java-symbol type="bool" name="config_sms_decode_gsm_8bit_data" /> + <java-symbol type="string" name="time_picker_am_label" /> + <java-symbol type="string" name="time_picker_pm_label" /> </resources> diff --git a/docs/html/tools/support-library/features.jd b/docs/html/tools/support-library/features.jd index 65148bf2d2ff..78946ee9de94 100644 --- a/docs/html/tools/support-library/features.jd +++ b/docs/html/tools/support-library/features.jd @@ -225,8 +225,7 @@ com.android.support:gridlayout-v7:18.0.+ <p>This library provides {@link android.support.v7.media.MediaRouter}, {@link android.support.v7.media.MediaRouteProvider}, and related media classes that -support the <a href="https://developers.google.com/cast/">Google Cast -developer preview</a>. </p> +support <a href="https://developers.google.com/cast/docs/android_sender">Google Cast</a>. </p> <p>In general, the APIs in the v7 mediarouter library provide a means of controlling the routing of media channels and streams from the current device to @@ -258,9 +257,8 @@ com.android.support:mediarouter-v7:18.0.+ <p class="caution">The v7 mediarouter library APIs introduced in Support Library r18 are subject to change in later revisions of the Support Library. At this -time, we recommend using the library only in connection with the <a -href="https://developers.google.com/cast/">Google Cast -developer preview</a>. </p> +time, we recommend using the library only in connection with <a +href="https://developers.google.com/cast/docs/android_sender">Google Cast</a>. </p> <h2 id="v8">v8 Support Library</h2> diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java index 6ebb8b558a7f..a6dbcb0df1b2 100644 --- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java +++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java @@ -626,13 +626,18 @@ public class NinePatchDrawable extends Drawable { mAutoMirrored = autoMirror; // Sanity check for valid padding when we have optical insets. - if (mPadding.left < mOpticalInsets.left) { - mPadding.left = mOpticalInsets.left; - mPadding.right = mOpticalInsets.right; - } - if (mPadding.top < mOpticalInsets.top) { - mPadding.top = mOpticalInsets.top; - mPadding.bottom = mOpticalInsets.bottom; + if (!opticalInsets.isEmpty()) { + if (mPadding == null) { + mPadding = new Rect(); + } + if (mPadding.left < opticalInsets.left) { + mPadding.left = opticalInsets.left; + mPadding.right = opticalInsets.right; + } + if (mPadding.top < opticalInsets.top) { + mPadding.top = opticalInsets.top; + mPadding.bottom = opticalInsets.bottom; + } } } diff --git a/graphics/java/android/graphics/drawable/Ripple.java b/graphics/java/android/graphics/drawable/Ripple.java index 6f21f2e00495..be2241bca205 100644 --- a/graphics/java/android/graphics/drawable/Ripple.java +++ b/graphics/java/android/graphics/drawable/Ripple.java @@ -276,7 +276,7 @@ class Ripple { public void getBounds(Rect bounds) { final int outerX = (int) mOuterX; final int outerY = (int) mOuterY; - final int r = (int) mOuterRadius; + final int r = (int) mOuterRadius + 1; bounds.set(outerX - r, outerY - r, outerX + r, outerY + r); } diff --git a/graphics/java/android/graphics/drawable/RippleBackground.java b/graphics/java/android/graphics/drawable/RippleBackground.java index d404ccd8b1f2..93df64874542 100644 --- a/graphics/java/android/graphics/drawable/RippleBackground.java +++ b/graphics/java/android/graphics/drawable/RippleBackground.java @@ -264,7 +264,7 @@ class RippleBackground { public void getBounds(Rect bounds) { final int outerX = (int) mOuterX; final int outerY = (int) mOuterY; - final int r = (int) mOuterRadius; + final int r = (int) mOuterRadius + 1; bounds.set(outerX - r, outerY - r, outerX + r, outerY + r); } diff --git a/media/java/android/media/tv/ITvInputService.aidl b/media/java/android/media/tv/ITvInputService.aidl index c98a48dafc32..7a853d1caa62 100644 --- a/media/java/android/media/tv/ITvInputService.aidl +++ b/media/java/android/media/tv/ITvInputService.aidl @@ -35,6 +35,6 @@ oneway interface ITvInputService { // For hardware TvInputService void notifyHardwareAdded(in TvInputHardwareInfo hardwareInfo); void notifyHardwareRemoved(in TvInputHardwareInfo hardwareInfo); - void notifyHdmiCecDeviceAdded(in HdmiDeviceInfo deviceInfo); - void notifyHdmiCecDeviceRemoved(in HdmiDeviceInfo deviceInfo); + void notifyHdmiDeviceAdded(in HdmiDeviceInfo deviceInfo); + void notifyHdmiDeviceRemoved(in HdmiDeviceInfo deviceInfo); } diff --git a/media/java/android/media/tv/ITvInputServiceCallback.aidl b/media/java/android/media/tv/ITvInputServiceCallback.aidl index df648e737d31..de5d56f85f40 100644 --- a/media/java/android/media/tv/ITvInputServiceCallback.aidl +++ b/media/java/android/media/tv/ITvInputServiceCallback.aidl @@ -24,7 +24,7 @@ import android.media.tv.TvInputInfo; * @hide */ oneway interface ITvInputServiceCallback { - void addHardwareTvInput(in int deviceID, in TvInputInfo inputInfo); - void addHdmiCecTvInput(in int logicalAddress, in TvInputInfo inputInfo); + void addHardwareTvInput(in int deviceId, in TvInputInfo inputInfo); + void addHdmiTvInput(in int logicalAddress, in TvInputInfo inputInfo); void removeTvInput(in String inputId); } diff --git a/media/java/android/media/tv/TvContentRating.java b/media/java/android/media/tv/TvContentRating.java index 2e4d0310ef0e..18136e9e39a5 100644 --- a/media/java/android/media/tv/TvContentRating.java +++ b/media/java/android/media/tv/TvContentRating.java @@ -19,7 +19,6 @@ package android.media.tv; import android.annotation.SystemApi; import android.net.Uri; import android.text.TextUtils; -import android.util.Log; import java.util.Arrays; import java.util.Collections; @@ -963,8 +962,6 @@ import java.util.List; * </table> */ public final class TvContentRating { - private static final String TAG = "TvContentRating"; - /** @hide */ public static final Uri SYSTEM_CONTENT_RATING_SYSTEM_XML = Uri.parse( "android.resource://system/" + com.android.internal.R.xml.tv_content_rating_systems); diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java index a826957272fb..ae6f5bc6415f 100644 --- a/media/java/android/media/tv/TvContract.java +++ b/media/java/android/media/tv/TvContract.java @@ -20,7 +20,6 @@ import android.annotation.SystemApi; import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContentUris; -import android.media.tv.TvContract.Programs; import android.net.Uri; import android.provider.BaseColumns; import android.util.ArraySet; diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java index 8feb7e6c2386..8d0e986a5b98 100644 --- a/media/java/android/media/tv/TvInputInfo.java +++ b/media/java/android/media/tv/TvInputInfo.java @@ -103,10 +103,10 @@ public final class TvInputInfo implements Parcelable { private static final String XML_START_TAG_NAME = "tv-input"; private static final String DELIMITER_INFO_IN_ID = "/"; - private static final String PREFIX_CEC_DEVICE = "CEC"; + private static final String PREFIX_HDMI_DEVICE = "HDMI"; private static final String PREFIX_HARDWARE_DEVICE = "HW"; - private static final int LENGTH_CEC_PHYSICAL_ADDRESS = 4; - private static final int LENGTH_CEC_LOGICAL_ADDRESS = 2; + private static final int LENGTH_HDMI_PHYSICAL_ADDRESS = 4; + private static final int LENGTH_HDMI_LOGICAL_ADDRESS = 2; private final ResolveInfo mService; private final String mId; @@ -155,7 +155,7 @@ public final class TvInputInfo implements Parcelable { * instantiating it from the given Context, ResolveInfo, and HdmiDeviceInfo. * * @param service The ResolveInfo returned from the package manager about this TV input service. - * @param cecInfo The HdmiDeviceInfo for a HDMI CEC logical device. + * @param deviceInfo The HdmiDeviceInfo for a HDMI CEC logical device. * @param parentId The ID of this TV input's parent input. {@code null} if none exists. * @param iconUri The {@link android.net.Uri} to load the icon image. * {@see android.content.ContentResolver#openInputStream}. If it is null, the application @@ -166,12 +166,12 @@ public final class TvInputInfo implements Parcelable { */ @SystemApi public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service, - HdmiDeviceInfo cecInfo, String parentId, String label, Uri iconUri) + HdmiDeviceInfo deviceInfo, String parentId, String label, Uri iconUri) throws XmlPullParserException, IOException { - boolean isConnectedToHdmiSwitch = (cecInfo.getPhysicalAddress() & 0x0FFF) != 0; - return createTvInputInfo(context, service, generateInputIdForHdmiCec( + boolean isConnectedToHdmiSwitch = (deviceInfo.getPhysicalAddress() & 0x0FFF) != 0; + return createTvInputInfo(context, service, generateInputIdForHdmiDevice( new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name), - cecInfo), parentId, TYPE_HDMI, label, iconUri, isConnectedToHdmiSwitch); + deviceInfo), parentId, TYPE_HDMI, label, iconUri, isConnectedToHdmiSwitch); } /** @@ -497,16 +497,16 @@ public final class TvInputInfo implements Parcelable { * Used to generate an input id from a ComponentName and HdmiDeviceInfo. * * @param name the component name for generating an input id. - * @param cecInfo HdmiDeviceInfo describing this TV input. - * @return the generated input id for the given {@code name} and {@code cecInfo}. - */ - private static final String generateInputIdForHdmiCec( - ComponentName name, HdmiDeviceInfo cecInfo) { - // Example of the format : "/CEC%04X%02X" - String format = String.format("%s%s%%0%sX%%0%sX", DELIMITER_INFO_IN_ID, PREFIX_CEC_DEVICE, - LENGTH_CEC_PHYSICAL_ADDRESS, LENGTH_CEC_LOGICAL_ADDRESS); + * @param deviceInfo HdmiDeviceInfo describing this TV input. + * @return the generated input id for the given {@code name} and {@code deviceInfo}. + */ + private static final String generateInputIdForHdmiDevice( + ComponentName name, HdmiDeviceInfo deviceInfo) { + // Example of the format : "/HDMI%04X%02X" + String format = String.format("%s%s%%0%sX%%0%sX", DELIMITER_INFO_IN_ID, PREFIX_HDMI_DEVICE, + LENGTH_HDMI_PHYSICAL_ADDRESS, LENGTH_HDMI_LOGICAL_ADDRESS); return name.flattenToShortString() + String.format(format, - cecInfo.getPhysicalAddress(), cecInfo.getLogicalAddress()); + deviceInfo.getPhysicalAddress(), deviceInfo.getLogicalAddress()); } /** diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java index 6a41c61dba18..408ee7bbb750 100644 --- a/media/java/android/media/tv/TvInputService.java +++ b/media/java/android/media/tv/TvInputService.java @@ -28,7 +28,6 @@ import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; -import android.os.Looper; import android.os.Message; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -42,7 +41,6 @@ import android.view.InputEventReceiver; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.Surface; -import android.view.SurfaceView; import android.view.View; import android.view.WindowManager; import android.view.accessibility.CaptioningManager; @@ -92,7 +90,7 @@ public abstract class TvInputService extends Service { * Handler instance to handle request from TV Input Manager Service. Should be run in the main * looper to be synchronously run with {@code Session.mHandler}. */ - private Handler mServiceHandler = new ServiceHandler(); + private final Handler mServiceHandler = new ServiceHandler(); private final RemoteCallbackList<ITvInputServiceCallback> mCallbacks = new RemoteCallbackList<ITvInputServiceCallback>(); @@ -142,15 +140,15 @@ public abstract class TvInputService extends Service { } @Override - public void notifyHdmiCecDeviceAdded(HdmiDeviceInfo cecDeviceInfo) { - mServiceHandler.obtainMessage(ServiceHandler.DO_ADD_HDMI_CEC_TV_INPUT, - cecDeviceInfo).sendToTarget(); + public void notifyHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) { + mServiceHandler.obtainMessage(ServiceHandler.DO_ADD_HDMI_TV_INPUT, + deviceInfo).sendToTarget(); } @Override - public void notifyHdmiCecDeviceRemoved(HdmiDeviceInfo cecDeviceInfo) { - mServiceHandler.obtainMessage(ServiceHandler.DO_REMOVE_HDMI_CEC_TV_INPUT, - cecDeviceInfo).sendToTarget(); + public void notifyHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) { + mServiceHandler.obtainMessage(ServiceHandler.DO_REMOVE_HDMI_TV_INPUT, + deviceInfo).sendToTarget(); } }; } @@ -203,27 +201,27 @@ public abstract class TvInputService extends Service { /** * Returns a new {@link TvInputInfo} object if this service is responsible for - * {@code cecDeviceInfo}; otherwise, return {@code null}. Override to modify default behavior - * of ignoring all HDMI CEC logical input device. + * {@code deviceInfo}; otherwise, return {@code null}. Override to modify default behavior of + * ignoring all HDMI logical input device. * - * @param cecDeviceInfo {@link HdmiDeviceInfo} object just added. + * @param deviceInfo {@link HdmiDeviceInfo} object just added. * @hide */ @SystemApi - public TvInputInfo onHdmiCecDeviceAdded(HdmiDeviceInfo cecDeviceInfo) { + public TvInputInfo onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) { return null; } /** - * Returns the input ID for {@code logicalAddress} if it is handled by this service; - * otherwise, return {@code null}. Override to modify default behavior of ignoring all HDMI CEC - * logical input device. + * Returns the input ID for {@code logicalAddress} if it is handled by this service; otherwise, + * return {@code null}. Override to modify default behavior of ignoring all HDMI logical input + * device. * - * @param cecDeviceInfo {@link HdmiDeviceInfo} object just removed. + * @param deviceInfo {@link HdmiDeviceInfo} object just removed. * @hide */ @SystemApi - public String onHdmiCecDeviceRemoved(HdmiDeviceInfo cecDeviceInfo) { + public String onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) { return null; } @@ -1164,8 +1162,8 @@ public abstract class TvInputService extends Service { private static final int DO_NOTIFY_SESSION_CREATED = 2; private static final int DO_ADD_HARDWARE_TV_INPUT = 3; private static final int DO_REMOVE_HARDWARE_TV_INPUT = 4; - private static final int DO_ADD_HDMI_CEC_TV_INPUT = 5; - private static final int DO_REMOVE_HDMI_CEC_TV_INPUT = 6; + private static final int DO_ADD_HDMI_TV_INPUT = 5; + private static final int DO_REMOVE_HDMI_TV_INPUT = 6; private void broadcastAddHardwareTvInput(int deviceId, TvInputInfo inputInfo) { int n = mCallbacks.beginBroadcast(); @@ -1179,12 +1177,11 @@ public abstract class TvInputService extends Service { mCallbacks.finishBroadcast(); } - private void broadcastAddHdmiCecTvInput( - int logicalAddress, TvInputInfo inputInfo) { + private void broadcastAddHdmiTvInput(int logicalAddress, TvInputInfo inputInfo) { int n = mCallbacks.beginBroadcast(); for (int i = 0; i < n; ++i) { try { - mCallbacks.getBroadcastItem(i).addHdmiCecTvInput(logicalAddress, inputInfo); + mCallbacks.getBroadcastItem(i).addHdmiTvInput(logicalAddress, inputInfo); } catch (RemoteException e) { Log.e(TAG, "Error while broadcasting.", e); } @@ -1286,17 +1283,17 @@ public abstract class TvInputService extends Service { } return; } - case DO_ADD_HDMI_CEC_TV_INPUT: { - HdmiDeviceInfo cecDeviceInfo = (HdmiDeviceInfo) msg.obj; - TvInputInfo inputInfo = onHdmiCecDeviceAdded(cecDeviceInfo); + case DO_ADD_HDMI_TV_INPUT: { + HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj; + TvInputInfo inputInfo = onHdmiDeviceAdded(deviceInfo); if (inputInfo != null) { - broadcastAddHdmiCecTvInput(cecDeviceInfo.getLogicalAddress(), inputInfo); + broadcastAddHdmiTvInput(deviceInfo.getLogicalAddress(), inputInfo); } return; } - case DO_REMOVE_HDMI_CEC_TV_INPUT: { - HdmiDeviceInfo cecDeviceInfo = (HdmiDeviceInfo) msg.obj; - String inputId = onHdmiCecDeviceRemoved(cecDeviceInfo); + case DO_REMOVE_HDMI_TV_INPUT: { + HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj; + String inputId = onHdmiDeviceRemoved(deviceInfo); if (inputId != null) { broadcastRemoveTvInput(inputId); } diff --git a/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java b/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java index 6bb1f2cd09af..e664fb965ae7 100644 --- a/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java +++ b/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java @@ -26,38 +26,70 @@ import android.view.animation.Interpolator; */ public class AppearAnimationUtils implements AppearAnimationCreator<View> { - public static final long APPEAR_DURATION = 220; + public static final long DEFAULT_APPEAR_DURATION = 220; - private final Interpolator mLinearOutSlowIn; + private final Interpolator mInterpolator; private final float mStartTranslation; private final AppearAnimationProperties mProperties = new AppearAnimationProperties(); private final float mDelayScale; + private final long mDuration; public AppearAnimationUtils(Context ctx) { - this(ctx, 1.0f, 1.0f); + this(ctx, DEFAULT_APPEAR_DURATION, + ctx.getResources().getDimensionPixelSize(R.dimen.appear_y_translation_start), + 1.0f, + AnimationUtils.loadInterpolator(ctx, android.R.interpolator.linear_out_slow_in)); } - public AppearAnimationUtils(Context ctx, float delayScaleFactor, - float translationScaleFactor) { - mLinearOutSlowIn = AnimationUtils.loadInterpolator( - ctx, android.R.interpolator.linear_out_slow_in); + public AppearAnimationUtils(Context ctx, long duration, float translationScaleFactor, + float delayScaleFactor, Interpolator interpolator) { + mInterpolator = interpolator; mStartTranslation = ctx.getResources().getDimensionPixelOffset( R.dimen.appear_y_translation_start) * translationScaleFactor; mDelayScale = delayScaleFactor; + mDuration = duration; } public void startAppearAnimation(View[][] objects, final Runnable finishListener) { startAppearAnimation(objects, finishListener, this); } + public void startAppearAnimation(View[] objects, final Runnable finishListener) { + startAppearAnimation(objects, finishListener, this); + } + public <T> void startAppearAnimation(T[][] objects, final Runnable finishListener, AppearAnimationCreator<T> creator) { AppearAnimationProperties properties = getDelays(objects); startAnimations(properties, objects, finishListener, creator); } + public <T> void startAppearAnimation(T[] objects, final Runnable finishListener, + AppearAnimationCreator<T> creator) { + AppearAnimationProperties properties = getDelays(objects); + startAnimations(properties, objects, finishListener, creator); + } + + private <T> void startAnimations(AppearAnimationProperties properties, T[] objects, + final Runnable finishListener, AppearAnimationCreator<T> creator) { + if (properties.maxDelayRowIndex == -1 || properties.maxDelayColIndex == -1) { + finishListener.run(); + return; + } + for (int row = 0; row < properties.delays.length; row++) { + long[] columns = properties.delays[row]; + long delay = columns[0]; + Runnable endRunnable = null; + if (properties.maxDelayRowIndex == row && properties.maxDelayColIndex == 0) { + endRunnable = finishListener; + } + creator.createAnimation(objects[row], delay, mDuration, + mStartTranslation, mInterpolator, endRunnable); + } + } + private <T> void startAnimations(AppearAnimationProperties properties, T[][] objects, - final Runnable finishListener, AppearAnimationCreator creator) {; + final Runnable finishListener, AppearAnimationCreator<T> creator) { if (properties.maxDelayRowIndex == -1 || properties.maxDelayColIndex == -1) { finishListener.run(); return; @@ -70,15 +102,32 @@ public class AppearAnimationUtils implements AppearAnimationCreator<View> { if (properties.maxDelayRowIndex == row && properties.maxDelayColIndex == col) { endRunnable = finishListener; } - creator.createAnimation(objects[row][col], delay, APPEAR_DURATION, - mStartTranslation, mLinearOutSlowIn, endRunnable); + creator.createAnimation(objects[row][col], delay, mDuration, + mStartTranslation, mInterpolator, endRunnable); } } + } + private <T> AppearAnimationProperties getDelays(T[] items) { + long maxDelay = -1; + mProperties.maxDelayColIndex = -1; + mProperties.maxDelayRowIndex = -1; + mProperties.delays = new long[items.length][]; + for (int row = 0; row < items.length; row++) { + mProperties.delays[row] = new long[1]; + long delay = calculateDelay(row, 0); + mProperties.delays[row][0] = delay; + if (items[row] != null && delay > maxDelay) { + maxDelay = delay; + mProperties.maxDelayColIndex = 0; + mProperties.maxDelayRowIndex = row; + } + } + return mProperties; } private <T> AppearAnimationProperties getDelays(T[][] items) { - long maxDelay = 0; + long maxDelay = -1; mProperties.maxDelayColIndex = -1; mProperties.maxDelayRowIndex = -1; mProperties.delays = new long[items.length][]; @@ -103,7 +152,7 @@ public class AppearAnimationUtils implements AppearAnimationCreator<View> { } public Interpolator getInterpolator() { - return mLinearOutSlowIn; + return mInterpolator; } public float getStartTranslation() { diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java index d5dcd71030f9..12bbd353198d 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java @@ -37,6 +37,7 @@ import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.Button; import android.widget.LinearLayout; @@ -108,8 +109,10 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit public KeyguardPatternView(Context context, AttributeSet attrs) { super(context, attrs); mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext); - mAppearAnimationUtils = new AppearAnimationUtils(context, 1.5f /* delayScale */, - 2.0f /* transitionScale */); + mAppearAnimationUtils = new AppearAnimationUtils(context, + AppearAnimationUtils.DEFAULT_APPEAR_DURATION, 1.5f /* delayScale */, + 2.0f /* transitionScale */, AnimationUtils.loadInterpolator( + mContext, android.R.interpolator.linear_out_slow_in)); } public void setKeyguardCallback(KeyguardSecurityCallback callback) { @@ -420,7 +423,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit this); if (!TextUtils.isEmpty(mHelpMessage.getText())) { mAppearAnimationUtils.createAnimation(mHelpMessage, 0, - AppearAnimationUtils.APPEAR_DURATION, + AppearAnimationUtils.DEFAULT_APPEAR_DURATION, mAppearAnimationUtils.getStartTranslation(), mAppearAnimationUtils.getInterpolator(), null /* finishRunnable */); diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher.xml b/packages/SystemUI/res/layout/keyguard_user_switcher.xml index 5648065b3b0e..7c918c2fa7e7 100644 --- a/packages/SystemUI/res/layout/keyguard_user_switcher.xml +++ b/packages/SystemUI/res/layout/keyguard_user_switcher.xml @@ -14,7 +14,8 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<com.android.systemui.statusbar.AlphaOptimizedLinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/keyguard_user_switcher" android:orientation="vertical" android:layout_height="wrap_content" @@ -22,4 +23,4 @@ android:gravity="end" android:visibility="gone" android:paddingTop="4dp"> -</LinearLayout> +</com.android.systemui.statusbar.AlphaOptimizedLinearLayout> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 97efb474c31a..51633dc8e0b4 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -768,13 +768,13 @@ <string name="disconnect_vpn">Disconnect VPN</string> <!-- Monitoring dialog device owner body text [CHAR LIMIT=300] --> - <string name="monitoring_description_device_owned">This device is managed by:\n<xliff:g id="organization">%1$s</xliff:g>\n\nYour administrator can monitor your network activity, including emails, apps and secure websites.\n\nFor more information, contact your administrator.</string> + <string name="monitoring_description_device_owned">This device is managed by:\n<xliff:g id="organization">%1$s</xliff:g>\n\nYour administrator can monitor your device and network activity, including emails, apps and secure websites.\n\nFor more information, contact your administrator.</string> <!-- Monitoring dialog non-legacy VPN text [CHAR LIMIT=300] --> - <string name="monitoring_description_vpn">You gave \"<xliff:g id="application">%1$s</xliff:g>\" permission to set up a VPN connection.\n\nThis app can monitor your network activity, including emails, apps and secure websites.</string> + <string name="monitoring_description_vpn">You gave \"<xliff:g id="application">%1$s</xliff:g>\" permission to set up a VPN connection.\n\nThis app can monitor your device and network activity, including emails, apps and secure websites.</string> <!-- Monitoring dialog legacy VPN text [CHAR LIMIT=300] --> - <string name="monitoring_description_legacy_vpn">You\'re connected to a VPN (\"<xliff:g id="application">%1$s</xliff:g>\").\n\nYour VPN service provider can monitor your network activity including emails, apps, and secure websites.</string> + <string name="monitoring_description_legacy_vpn">You\'re connected to a VPN (\"<xliff:g id="application">%1$s</xliff:g>\").\n\nYour VPN service provider can monitor your device and network activity including emails, apps, and secure websites.</string> <!-- Monitoring dialog non-legacy VPN with device owner text [CHAR LIMIT=300] --> <string name="monitoring_description_vpn_device_owned">This device is managed by:\n<xliff:g id="organization">%1$s</xliff:g>\n\nYour administrator is capable of monitoring your network activity including emails, apps, and secure websites. For more information, contact your administrator.\n\nAlso, you gave \"<xliff:g id="application">%2$s</xliff:g>\" permission to set up a VPN connection. This app can monitor network activity too.</string> diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java index 759d540feda1..a56b7a72c08e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java @@ -116,4 +116,9 @@ public class UserDetailItemView extends LinearLayout { boolean activated = ArrayUtils.contains(getDrawableState(), android.R.attr.state_activated); mName.setTypeface(activated ? mActivatedTypeface : mRegularTypeface); } + + @Override + public boolean hasOverlappingRendering() { + return false; + } } diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java index 98bdee0c16ea..2a782cc09935 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java @@ -738,12 +738,12 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener } } - private void startApplicationDetailsActivity(String packageName) { + private void startApplicationDetailsActivity(String packageName, int userId) { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.fromParts("package", packageName, null)); intent.setComponent(intent.resolveActivity(getContext().getPackageManager())); TaskStackBuilder.create(getContext()) - .addNextIntentWithParentStack(intent).startActivities(); + .addNextIntentWithParentStack(intent).startActivities(null, new UserHandle(userId)); } public boolean onInterceptTouchEvent(MotionEvent ev) { @@ -769,7 +769,7 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener ViewHolder viewHolder = (ViewHolder) selectedView.getTag(); if (viewHolder != null) { final TaskDescription ad = viewHolder.taskDescription; - startApplicationDetailsActivity(ad.packageName); + startApplicationDetailsActivity(ad.packageName, ad.userId); show(false); } else { throw new IllegalStateException("Oops, no tag on view " + selectedView); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index 1a32b81c767e..58d5df77c59e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -26,6 +26,7 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; import android.net.Uri; +import android.os.UserHandle; import android.provider.Settings; import android.util.AttributeSet; import android.view.LayoutInflater; @@ -474,7 +475,8 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV Uri.fromParts("package", baseIntent.getComponent().getPackageName(), null)); intent.setComponent(intent.resolveActivity(getContext().getPackageManager())); TaskStackBuilder.create(getContext()) - .addNextIntentWithParentStack(intent).startActivities(); + .addNextIntentWithParentStack(intent).startActivities(null, + new UserHandle(t.userId)); } @Override 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 899619761940..3b1408234887 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -82,6 +82,7 @@ public class KeyguardBouncer { public void hide(boolean destroyView) { if (mKeyguardView != null) { + mKeyguardView.setOnDismissAction(null); mKeyguardView.cleanUp(); } if (destroyView) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java index bf66c4104033..b66c3101304f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -16,10 +16,14 @@ package com.android.systemui.statusbar.phone; +import android.animation.LayoutTransition; import android.content.Context; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; +import android.view.ViewTreeObserver; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; @@ -48,6 +52,7 @@ public class KeyguardStatusBarView extends RelativeLayout private KeyguardUserSwitcher mKeyguardUserSwitcher; private int mSystemIconsSwitcherHiddenExpandedMargin; + private Interpolator mFastOutSlowInInterpolator; public KeyguardStatusBarView(Context context, AttributeSet attrs) { super(context, attrs); @@ -61,6 +66,8 @@ public class KeyguardStatusBarView extends RelativeLayout mMultiUserAvatar = (ImageView) findViewById(R.id.multi_user_avatar); mBatteryLevel = (TextView) findViewById(R.id.battery_level); loadDimens(); + mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(), + android.R.interpolator.fast_out_slow_in); updateUserSwitcher(); } @@ -70,7 +77,14 @@ public class KeyguardStatusBarView extends RelativeLayout } private void updateVisibilities() { - mMultiUserSwitch.setVisibility(!mKeyguardUserSwitcherShowing ? VISIBLE : GONE); + if (mMultiUserSwitch.getParent() != this && !mKeyguardUserSwitcherShowing) { + if (mMultiUserSwitch.getParent() != null) { + getOverlay().remove(mMultiUserSwitch); + } + addView(mMultiUserSwitch, 0); + } else if (mMultiUserSwitch.getParent() == this && mKeyguardUserSwitcherShowing) { + removeView(mMultiUserSwitch); + } mBatteryLevel.setVisibility(mBatteryCharging ? View.VISIBLE : View.GONE); } @@ -137,12 +151,71 @@ public class KeyguardStatusBarView extends RelativeLayout updateUserSwitcher(); } - public void setKeyguardUserSwitcherShowing(boolean showing) { + public void setKeyguardUserSwitcherShowing(boolean showing, boolean animate) { mKeyguardUserSwitcherShowing = showing; + if (animate) { + animateNextLayoutChange(); + } updateVisibilities(); updateSystemIconsLayoutParams(); } + private void animateNextLayoutChange() { + final int systemIconsCurrentX = mSystemIconsSuperContainer.getLeft(); + final boolean userSwitcherVisible = mMultiUserSwitch.getParent() == this; + getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + getViewTreeObserver().removeOnPreDrawListener(this); + boolean userSwitcherHiding = userSwitcherVisible + && mMultiUserSwitch.getParent() != KeyguardStatusBarView.this; + mSystemIconsSuperContainer.setX(systemIconsCurrentX); + mSystemIconsSuperContainer.animate() + .translationX(0) + .setDuration(400) + .setStartDelay(userSwitcherHiding ? 300 : 0) + .setInterpolator(mFastOutSlowInInterpolator) + .start(); + if (userSwitcherHiding) { + getOverlay().add(mMultiUserSwitch); + mMultiUserSwitch.animate() + .alpha(0f) + .setDuration(300) + .setStartDelay(0) + .setInterpolator(PhoneStatusBar.ALPHA_OUT) + .withEndAction(new Runnable() { + @Override + public void run() { + mMultiUserSwitch.setAlpha(1f); + getOverlay().remove(mMultiUserSwitch); + } + }) + .start(); + + } else { + mMultiUserSwitch.setAlpha(0f); + mMultiUserSwitch.animate() + .alpha(1f) + .setDuration(300) + .setStartDelay(200) + .setInterpolator(PhoneStatusBar.ALPHA_IN); + } + return true; + } + }); + + } + + @Override + public void setVisibility(int visibility) { + super.setVisibility(visibility); + if (visibility != View.VISIBLE) { + mSystemIconsSuperContainer.animate().cancel(); + mMultiUserSwitch.animate().cancel(); + mMultiUserSwitch.setAlpha(1f); + } + } + @Override public boolean hasOverlappingRendering() { return false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java index af302662e070..47325c82fab4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java @@ -65,7 +65,7 @@ public class MultiUserSwitch extends FrameLayout implements View.OnClickListener if (um.isUserSwitcherEnabled()) { if (mKeyguardMode) { if (mKeyguardUserSwitcher != null) { - mKeyguardUserSwitcher.show(); + mKeyguardUserSwitcher.show(true /* animate */); } } else { mQsPanel.showDetailAdapter(true, @@ -78,4 +78,9 @@ public class MultiUserSwitch extends FrameLayout implements View.OnClickListener getContext().startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); } } + + @Override + public boolean hasOverlappingRendering() { + return false; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index e70422b42780..74ae4a4f614b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -950,7 +950,7 @@ public class NotificationPanelView extends PanelView implements ? View.VISIBLE : View.INVISIBLE); if (mKeyguardUserSwitcher != null && mQsExpanded && !mStackScrollerOverscrolling) { - mKeyguardUserSwitcher.hide(); + mKeyguardUserSwitcher.hide(true /* animate */); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 1d678af18743..75e31e4f2d69 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -3341,7 +3341,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, public void showKeyguard() { setBarState(StatusBarState.KEYGUARD); - updateKeyguardState(false /* goingToFullShade */); + updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */); instantExpandNotificationsPanel(); mLeaveOpenOnKeyguardHide = false; if (mDraggedDownRow != null) { @@ -3413,7 +3413,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } else { instantCollapseNotificationPanel(); } - updateKeyguardState(staying); + updateKeyguardState(staying, false /* fromShadeLocked */); return staying; } @@ -3449,14 +3449,15 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, && mStatusBarKeyguardViewManager.isSecure()); } - private void updateKeyguardState(boolean goingToFullShade) { + private void updateKeyguardState(boolean goingToFullShade, boolean fromShadeLocked) { if (mState == StatusBarState.KEYGUARD) { mKeyguardIndicationController.setVisible(true); mNotificationPanel.resetViews(); - mKeyguardUserSwitcher.setKeyguard(true); + mKeyguardUserSwitcher.setKeyguard(true, fromShadeLocked); } else { mKeyguardIndicationController.setVisible(false); - mKeyguardUserSwitcher.setKeyguard(false); + mKeyguardUserSwitcher.setKeyguard(false, + goingToFullShade || mState == StatusBarState.SHADE_LOCKED || fromShadeLocked); } if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { mScrimController.setKeyguardShowing(true); @@ -3686,7 +3687,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } else { mNotificationPanel.animateToFullShade(0 /* delay */); setBarState(StatusBarState.SHADE_LOCKED); - updateKeyguardState(false /* goingToFullShade */); + updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */); if (row != null) { row.setUserLocked(false); } @@ -3699,7 +3700,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, public void goToKeyguard() { if (mState == StatusBarState.SHADE_LOCKED) { setBarState(StatusBarState.KEYGUARD); - updateKeyguardState(false /* goingToFullShade */); + updateKeyguardState(false /* goingToFullShade */, true /* fromShadeLocked*/); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java index 203196ebe802..18583ee401c0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java @@ -16,18 +16,25 @@ package com.android.systemui.statusbar.policy; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; import android.content.Context; import android.database.DataSetObserver; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewStub; +import android.view.animation.AnimationUtils; import android.widget.TextView; +import com.android.keyguard.AppearAnimationUtils; import com.android.systemui.R; import com.android.systemui.qs.tiles.UserDetailItemView; import com.android.systemui.statusbar.phone.KeyguardStatusBarView; import com.android.systemui.statusbar.phone.NotificationPanelView; +import com.android.systemui.statusbar.phone.PhoneStatusBar; import com.android.systemui.statusbar.phone.StatusBarHeaderView; import com.android.systemui.statusbar.phone.UserAvatarView; @@ -43,33 +50,42 @@ public class KeyguardUserSwitcher { private final KeyguardStatusBarView mStatusBarView; private final Adapter mAdapter; private final boolean mSimpleUserSwitcher; + private final AppearAnimationUtils mAppearAnimationUtils; + private final KeyguardUserSwitcherScrim mBackground; + private ObjectAnimator mBgAnimator; public KeyguardUserSwitcher(Context context, ViewStub userSwitcher, KeyguardStatusBarView statusBarView, NotificationPanelView panelView, UserSwitcherController userSwitcherController) { if (context.getResources().getBoolean(R.bool.config_keyguardUserSwitcher) || ALWAYS_ON) { mUserSwitcher = (ViewGroup) userSwitcher.inflate(); - mUserSwitcher.setBackground(new KeyguardUserSwitcherScrim(mUserSwitcher)); + mBackground = new KeyguardUserSwitcherScrim(mUserSwitcher); + mUserSwitcher.setBackground(mBackground); mStatusBarView = statusBarView; mStatusBarView.setKeyguardUserSwitcher(this); panelView.setKeyguardUserSwitcher(this); mAdapter = new Adapter(context, userSwitcherController); mAdapter.registerDataSetObserver(mDataSetObserver); mSimpleUserSwitcher = userSwitcherController.isSimpleUserSwitcher(); + mAppearAnimationUtils = new AppearAnimationUtils(context, 400, -0.5f, 0.5f, + AnimationUtils.loadInterpolator( + context, android.R.interpolator.fast_out_slow_in)); } else { mUserSwitcher = null; mStatusBarView = null; mAdapter = null; mSimpleUserSwitcher = false; + mAppearAnimationUtils = null; + mBackground = null; } } - public void setKeyguard(boolean keyguard) { + public void setKeyguard(boolean keyguard, boolean animate) { if (mUserSwitcher != null) { if (keyguard && shouldExpandByDefault()) { - show(); + show(animate); } else { - hide(); + hide(animate); } } } @@ -82,20 +98,79 @@ public class KeyguardUserSwitcher { return mSimpleUserSwitcher; } - public void show() { - if (mUserSwitcher != null) { - // TODO: animate + public void show(boolean animate) { + if (mUserSwitcher != null && mUserSwitcher.getVisibility() != View.VISIBLE) { + cancelAnimations(); mUserSwitcher.setVisibility(View.VISIBLE); - mStatusBarView.setKeyguardUserSwitcherShowing(true); + mStatusBarView.setKeyguardUserSwitcherShowing(true, animate); + if (animate) { + startAppearAnimation(); + } } } - public void hide() { + public void hide(boolean animate) { if (mUserSwitcher != null && mUserSwitcher.getVisibility() == View.VISIBLE) { - // TODO: animate - mUserSwitcher.setVisibility(View.GONE); - mStatusBarView.setKeyguardUserSwitcherShowing(false); + cancelAnimations(); + if (animate) { + startDisappearAnimation(); + } else { + mUserSwitcher.setVisibility(View.GONE); + } + mStatusBarView.setKeyguardUserSwitcherShowing(false, animate); + } + } + + private void cancelAnimations() { + int count = mUserSwitcher.getChildCount(); + for (int i = 0; i < count; i++) { + mUserSwitcher.getChildAt(i).animate().cancel(); + } + if (mBgAnimator != null) { + mBgAnimator.cancel(); } + mUserSwitcher.animate().cancel(); + } + + private void startAppearAnimation() { + int count = mUserSwitcher.getChildCount(); + View[] objects = new View[count]; + for (int i = 0; i < count; i++) { + objects[i] = mUserSwitcher.getChildAt(i); + } + mUserSwitcher.setClipChildren(false); + mUserSwitcher.setClipToPadding(false); + mAppearAnimationUtils.startAppearAnimation(objects, new Runnable() { + @Override + public void run() { + mUserSwitcher.setClipChildren(true); + mUserSwitcher.setClipToPadding(true); + } + }); + mBgAnimator = ObjectAnimator.ofInt(mBackground, "alpha", 0, 255); + mBgAnimator.setDuration(400); + mBgAnimator.setInterpolator(PhoneStatusBar.ALPHA_IN); + mBgAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mBgAnimator = null; + } + }); + mBgAnimator.start(); + } + + private void startDisappearAnimation() { + mUserSwitcher.animate() + .alpha(0f) + .setDuration(300) + .setInterpolator(PhoneStatusBar.ALPHA_OUT) + .withEndAction(new Runnable() { + @Override + public void run() { + mUserSwitcher.setVisibility(View.GONE); + mUserSwitcher.setAlpha(1f); + } + }); } private void refresh() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java index 3356afdcb37e..43630375633f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.policy; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; +import android.graphics.LightingColorFilter; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.RadialGradient; @@ -41,7 +42,9 @@ public class KeyguardUserSwitcherScrim extends Drawable private int mDarkColor; private int mTop; + private int mAlpha; private Paint mRadialGradientPaint = new Paint(); + private int mLayoutWidth; public KeyguardUserSwitcherScrim(View host) { host.addOnLayoutChangeListener(this); @@ -56,13 +59,21 @@ public class KeyguardUserSwitcherScrim extends Drawable float width = bounds.width() * OUTER_EXTENT; float height = (mTop + bounds.height()) * OUTER_EXTENT; canvas.translate(0, -mTop); - canvas.scale(1, height/width); + canvas.scale(1, height / width); canvas.drawRect(isLtr ? bounds.right - width : 0, 0, isLtr ? bounds.right : bounds.left + width, width, mRadialGradientPaint); } @Override public void setAlpha(int alpha) { + mAlpha = alpha; + updatePaint(); + invalidateSelf(); + } + + @Override + public int getAlpha() { + return mAlpha; } @Override @@ -78,15 +89,24 @@ public class KeyguardUserSwitcherScrim extends Drawable public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { if (left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom) { - int width = right - left; - float radius = width * OUTER_EXTENT; - boolean isLtr = getLayoutDirection() == LayoutDirection.LTR; - mRadialGradientPaint.setShader( - new RadialGradient(isLtr ? width : 0, 0, radius, - new int[] { mDarkColor, Color.TRANSPARENT}, - new float[] { Math.max(0f, width * INNER_EXTENT / radius), 1f}, - Shader.TileMode.CLAMP)); + mLayoutWidth = right - left; mTop = top; + updatePaint(); } } + + private void updatePaint() { + if (mLayoutWidth == 0) { + return; + } + float radius = mLayoutWidth * OUTER_EXTENT; + boolean isLtr = getLayoutDirection() == LayoutDirection.LTR; + mRadialGradientPaint.setShader( + new RadialGradient(isLtr ? mLayoutWidth : 0, 0, radius, + new int[] { Color.argb( + (int) (Color.alpha(mDarkColor) * mAlpha / 255f), 0, 0, 0), + Color.TRANSPARENT }, + new float[] { Math.max(0f, mLayoutWidth * INNER_EXTENT / radius), 1f }, + Shader.TileMode.CLAMP)); + } } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index ff3cd9d651b1..964acbdc3433 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -1314,7 +1314,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { mBackgroundDrawable = drawable; if (mDecor != null) { mDecor.setWindowBackground(drawable); - mDecor.setClipToOutline(drawable != null && mClipToOutline); } } } @@ -3389,10 +3388,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } mDecor.setWindowBackground(background); - if (background != null) { - mDecor.setClipToOutline(mClipToOutline); - } - final Drawable frame; if (mFrameResource != 0) { frame = getContext().getDrawable(mFrameResource); @@ -3402,6 +3397,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { mDecor.setWindowFrame(frame); mDecor.setElevation(mElevation); + mDecor.setClipToOutline(mClipToOutline); if (mTitle != null) { setTitle(mTitle); diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 59aef328ef76..77b14ac18742 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -5129,6 +5129,14 @@ public class BackupManagerService extends IBackupManager.Stub { } } + // The path needs to be canonical + if (info.path.contains("..") || info.path.contains("//")) { + if (MORE_DEBUG) { + Slog.w(TAG, "Dropping invalid path " + info.path); + } + return false; + } + // Otherwise we think this file is good to go return true; } @@ -5680,6 +5688,14 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF break; } + // The path needs to be canonical + if (info.path.contains("..") || info.path.contains("//")) { + if (MORE_DEBUG) { + Slog.w(TAG, "Dropping invalid path " + info.path); + } + okay = false; + } + // If the policy is satisfied, go ahead and set up to pipe the // data to the agent. if (DEBUG && okay && mAgent != null) { diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 6761f24afb08..f9baccdaeed8 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -251,10 +251,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { private int mNetworkPreference; private int mActiveDefaultNetwork = -1; // 0 is full bad, 100 is full good - private int mDefaultInetCondition = 0; private int mDefaultInetConditionPublished = 0; - private boolean mInetConditionChangeInFlight = false; - private int mDefaultConnectionSequence = 0; private Object mDnsLock = new Object(); private int mNumDnsEntries; @@ -274,19 +271,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { private static final int EVENT_CHANGE_MOBILE_DATA_ENABLED = 2; /** - * used internally to synchronize inet condition reports - * arg1 = networkType - * arg2 = condition (0 bad, 100 good) - */ - private static final int EVENT_INET_CONDITION_CHANGE = 4; - - /** - * used internally to mark the end of inet condition hold periods - * arg1 = networkType - */ - private static final int EVENT_INET_CONDITION_HOLD_END = 5; - - /** * used internally to clear a wakelock when transitioning * from one net to another. Clear happens when we get a new * network - EVENT_EXPIRE_NET_TRANSITION_WAKELOCK happens @@ -490,10 +474,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { mTypeLists[type] = new ArrayList<NetworkAgentInfo>(); } - private boolean isDefaultNetwork(NetworkAgentInfo nai) { - return mNetworkForRequestId.get(mDefaultRequest.requestId) == nai; - } - public boolean isTypeSupported(int type) { return isNetworkTypeValid(type) && mTypeLists[type] != null; } @@ -2052,6 +2032,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { nai.networkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null); } + if (isDefaultNetwork(nai)) { + mDefaultInetConditionPublished = 0; + } notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST); nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED); mNetworkAgentInfos.remove(msg.replyTo); @@ -2222,18 +2205,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { } break; } - case EVENT_INET_CONDITION_CHANGE: { - int netType = msg.arg1; - int condition = msg.arg2; - handleInetConditionChange(netType, condition); - break; - } - case EVENT_INET_CONDITION_HOLD_END: { - int netType = msg.arg1; - int sequence = msg.arg2; - handleInetConditionHoldEnd(netType, sequence); - break; - } case EVENT_APPLY_GLOBAL_HTTP_PROXY: { handleDeprecatedGlobalHttpProxy(); break; @@ -2428,99 +2399,15 @@ public class ConnectivityService extends IConnectivityManager.Stub { // 100 percent is full good, 0 is full bad. public void reportInetCondition(int networkType, int percentage) { - if (VDBG) log("reportNetworkCondition(" + networkType + ", " + percentage + ")"); - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.STATUS_BAR, - "ConnectivityService"); - - if (DBG) { - int pid = getCallingPid(); - int uid = getCallingUid(); - String s = pid + "(" + uid + ") reports inet is " + - (percentage > 50 ? "connected" : "disconnected") + " (" + percentage + ") on " + - "network Type " + networkType + " at " + GregorianCalendar.getInstance().getTime(); - mInetLog.add(s); - while(mInetLog.size() > INET_CONDITION_LOG_MAX_SIZE) { - mInetLog.remove(0); - } - } - mHandler.sendMessage(mHandler.obtainMessage( - EVENT_INET_CONDITION_CHANGE, networkType, percentage)); + if (percentage > 50) return; // don't handle good network reports + NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType); + if (nai != null) reportBadNetwork(nai.network); } public void reportBadNetwork(Network network) { //TODO } - private void handleInetConditionChange(int netType, int condition) { - if (mActiveDefaultNetwork == -1) { - if (DBG) log("handleInetConditionChange: no active default network - ignore"); - return; - } - if (mActiveDefaultNetwork != netType) { - if (DBG) log("handleInetConditionChange: net=" + netType + - " != default=" + mActiveDefaultNetwork + " - ignore"); - return; - } - if (VDBG) { - log("handleInetConditionChange: net=" + - netType + ", condition=" + condition + - ",mActiveDefaultNetwork=" + mActiveDefaultNetwork); - } - mDefaultInetCondition = condition; - int delay; - if (mInetConditionChangeInFlight == false) { - if (VDBG) log("handleInetConditionChange: starting a change hold"); - // setup a new hold to debounce this - if (mDefaultInetCondition > 50) { - delay = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.INET_CONDITION_DEBOUNCE_UP_DELAY, 500); - } else { - delay = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.INET_CONDITION_DEBOUNCE_DOWN_DELAY, 3000); - } - mInetConditionChangeInFlight = true; - mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_INET_CONDITION_HOLD_END, - mActiveDefaultNetwork, mDefaultConnectionSequence), delay); - } else { - // we've set the new condition, when this hold ends that will get picked up - if (VDBG) log("handleInetConditionChange: currently in hold - not setting new end evt"); - } - } - - private void handleInetConditionHoldEnd(int netType, int sequence) { - if (DBG) { - log("handleInetConditionHoldEnd: net=" + netType + - ", condition=" + mDefaultInetCondition + - ", published condition=" + mDefaultInetConditionPublished); - } - mInetConditionChangeInFlight = false; - - if (mActiveDefaultNetwork == -1) { - if (DBG) log("handleInetConditionHoldEnd: no active default network - ignoring"); - return; - } - if (mDefaultConnectionSequence != sequence) { - if (DBG) log("handleInetConditionHoldEnd: event hold for obsolete network - ignoring"); - return; - } - // TODO: Figure out why this optimization sometimes causes a - // change in mDefaultInetCondition to be missed and the - // UI to not be updated. - //if (mDefaultInetConditionPublished == mDefaultInetCondition) { - // if (DBG) log("no change in condition - aborting"); - // return; - //} - NetworkInfo networkInfo = getNetworkInfoForType(mActiveDefaultNetwork); - if (networkInfo.isConnected() == false) { - if (DBG) log("handleInetConditionHoldEnd: default network not connected - ignoring"); - return; - } - mDefaultInetConditionPublished = mDefaultInetCondition; - sendInetConditionBroadcast(networkInfo); - return; - } - public ProxyInfo getProxy() { // this information is already available as a world read/writable jvm property // so this API change wouldn't have a benifit. It also breaks the passing @@ -4206,6 +4093,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { private final NetworkRequest mDefaultRequest; + private boolean isDefaultNetwork(NetworkAgentInfo nai) { + return mNetworkForRequestId.get(mDefaultRequest.requestId) == nai; + } + public void registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo, LinkProperties linkProperties, NetworkCapabilities networkCapabilities, int currentScore, NetworkMisc networkMisc) { @@ -4532,6 +4423,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { mLegacyTypeTracker.remove(currentNetwork.networkInfo.getType(), currentNetwork); } + mDefaultInetConditionPublished = 100; mLegacyTypeTracker.add(newNetwork.networkInfo.getType(), newNetwork); } } @@ -4581,8 +4473,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { // to connected after our normal pause unless somebody reports us as // really disconnected mDefaultInetConditionPublished = 0; - mDefaultConnectionSequence++; - mInetConditionChangeInFlight = false; // TODO - read the tcp buffer size config string from somewhere // updateNetworkSettings(); } diff --git a/services/core/java/com/android/server/DockObserver.java b/services/core/java/com/android/server/DockObserver.java index af38664080ec..d05c280093fb 100644 --- a/services/core/java/com/android/server/DockObserver.java +++ b/services/core/java/com/android/server/DockObserver.java @@ -201,9 +201,6 @@ final class DockObserver extends SystemService { // There are many components in the system watching for this so as to // adjust audio routing, screen orientation, etc. getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL); - - // Release the wake lock that was acquired when the message was posted. - mWakeLock.release(); } } @@ -213,6 +210,7 @@ final class DockObserver extends SystemService { switch (msg.what) { case MSG_DOCK_STATE_CHANGED: handleDockStateChange(); + mWakeLock.release(); break; } } diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index 74f725f36f60..71f43b487214 100644 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -37,13 +37,12 @@ import android.media.AudioPort; import android.media.AudioPortConfig; import android.media.tv.ITvInputHardware; import android.media.tv.ITvInputHardwareCallback; -import android.media.tv.TvInputHardwareInfo; import android.media.tv.TvContract; +import android.media.tv.TvInputHardwareInfo; import android.media.tv.TvInputInfo; import android.media.tv.TvStreamConfig; import android.os.Handler; import android.os.IBinder; -import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; @@ -59,12 +58,10 @@ import com.android.server.SystemService; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Set; /** * A helper class for TvInputManagerService to handle TV input hardware. @@ -82,11 +79,11 @@ class TvInputHardwareManager implements TvInputHal.Callback { private final TvInputHal mHal = new TvInputHal(this); private final SparseArray<Connection> mConnections = new SparseArray<>(); private final List<TvInputHardwareInfo> mHardwareList = new ArrayList<>(); - private List<HdmiDeviceInfo> mHdmiCecDeviceList = new LinkedList<>(); + private final List<HdmiDeviceInfo> mHdmiDeviceList = new LinkedList<>(); /* A map from a device ID to the matching TV input ID. */ private final SparseArray<String> mHardwareInputIdMap = new SparseArray<>(); /* A map from a HDMI logical address to the matching TV input ID. */ - private final SparseArray<String> mHdmiCecInputIdMap = new SparseArray<>(); + private final SparseArray<String> mHdmiInputIdMap = new SparseArray<>(); private final Map<String, TvInputInfo> mInputMap = new ArrayMap<>(); private final AudioManager mAudioManager; @@ -119,7 +116,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { try { mHdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener); mHdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener); - mHdmiCecDeviceList.addAll(mHdmiControlService.getInputDevices()); + mHdmiDeviceList.addAll(mHdmiControlService.getInputDevices()); mHdmiControlService.setInputChangeListener(mHdmiInputChangeListener); } catch (RemoteException e) { Slog.w(TAG, "Error registering listeners to HdmiControlService:", e); @@ -163,12 +160,11 @@ class TvInputHardwareManager implements TvInputHal.Callback { buildHardwareListLocked(); TvInputHardwareInfo info = connection.getHardwareInfoLocked(); if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) { - // Remove HDMI CEC devices linked with this hardware. - for (Iterator<HdmiDeviceInfo> it = mHdmiCecDeviceList.iterator(); - it.hasNext(); ) { + // Remove HDMI devices linked with this hardware. + for (Iterator<HdmiDeviceInfo> it = mHdmiDeviceList.iterator(); it.hasNext();) { HdmiDeviceInfo deviceInfo = it.next(); if (deviceInfo.getPortId() == info.getHdmiPortId()) { - mHandler.obtainMessage(ListenerHandler.HDMI_CEC_DEVICE_REMOVED, 0, 0, + mHandler.obtainMessage(ListenerHandler.HDMI_DEVICE_REMOVED, 0, 0, deviceInfo).sendToTarget(); it.remove(); } @@ -220,9 +216,9 @@ class TvInputHardwareManager implements TvInputHal.Callback { } } - public List<HdmiDeviceInfo> getHdmiCecInputDeviceList() { + public List<HdmiDeviceInfo> getHdmiDeviceList() { synchronized (mLock) { - return Collections.unmodifiableList(mHdmiCecDeviceList); + return Collections.unmodifiableList(mHdmiDeviceList); } } @@ -283,7 +279,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { return -1; } - public void addHdmiCecTvInput(int logicalAddress, TvInputInfo info) { + public void addHdmiTvInput(int logicalAddress, TvInputInfo info) { if (info.getType() != TvInputInfo.TYPE_HDMI) { throw new IllegalArgumentException("info (" + info + ") has non-HDMI type."); } @@ -293,13 +289,13 @@ class TvInputHardwareManager implements TvInputHal.Callback { if (parentIndex < 0) { throw new IllegalArgumentException("info (" + info + ") has invalid parentId."); } - String oldInputId = mHdmiCecInputIdMap.get(logicalAddress); + String oldInputId = mHdmiInputIdMap.get(logicalAddress); if (oldInputId != null) { Slog.w(TAG, "Trying to override previous registration: old = " + mInputMap.get(oldInputId) + ":" + logicalAddress + ", new = " + info + ":" + logicalAddress); } - mHdmiCecInputIdMap.put(logicalAddress, info.getId()); + mHdmiInputIdMap.put(logicalAddress, info.getId()); mInputMap.put(info.getId(), info); } } @@ -311,9 +307,9 @@ class TvInputHardwareManager implements TvInputHal.Callback { if (hardwareIndex >= 0) { mHardwareInputIdMap.removeAt(hardwareIndex); } - int cecIndex = indexOfEqualValue(mHdmiCecInputIdMap, inputId); - if (cecIndex >= 0) { - mHdmiCecInputIdMap.removeAt(cecIndex); + int deviceIndex = indexOfEqualValue(mHdmiInputIdMap, inputId); + if (deviceIndex >= 0) { + mHdmiInputIdMap.removeAt(deviceIndex); } } } @@ -864,16 +860,16 @@ class TvInputHardwareManager implements TvInputHal.Callback { public void onStateChanged(String inputId, int state); public void onHardwareDeviceAdded(TvInputHardwareInfo info); public void onHardwareDeviceRemoved(TvInputHardwareInfo info); - public void onHdmiCecDeviceAdded(HdmiDeviceInfo cecDevice); - public void onHdmiCecDeviceRemoved(HdmiDeviceInfo cecDevice); + public void onHdmiDeviceAdded(HdmiDeviceInfo device); + public void onHdmiDeviceRemoved(HdmiDeviceInfo device); } private class ListenerHandler extends Handler { private static final int STATE_CHANGED = 1; private static final int HARDWARE_DEVICE_ADDED = 2; private static final int HARDWARE_DEVICE_REMOVED = 3; - private static final int HDMI_CEC_DEVICE_ADDED = 4; - private static final int HDMI_CEC_DEVICE_REMOVED = 5; + private static final int HDMI_DEVICE_ADDED = 4; + private static final int HDMI_DEVICE_REMOVED = 5; @Override public final void handleMessage(Message msg) { @@ -894,14 +890,14 @@ class TvInputHardwareManager implements TvInputHal.Callback { mListener.onHardwareDeviceRemoved(info); break; } - case HDMI_CEC_DEVICE_ADDED: { + case HDMI_DEVICE_ADDED: { HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj; - mListener.onHdmiCecDeviceAdded(info); + mListener.onHdmiDeviceAdded(info); break; } - case HDMI_CEC_DEVICE_REMOVED: { + case HDMI_DEVICE_REMOVED: { HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj; - mListener.onHdmiCecDeviceRemoved(info); + mListener.onHdmiDeviceRemoved(info); break; } default: { @@ -939,21 +935,21 @@ class TvInputHardwareManager implements TvInputHal.Callback { public void onStatusChanged(HdmiDeviceInfo deviceInfo, boolean activated) { synchronized (mLock) { if (activated) { - if (!mHdmiCecDeviceList.contains(deviceInfo)) { - mHdmiCecDeviceList.add(deviceInfo); + if (!mHdmiDeviceList.contains(deviceInfo)) { + mHdmiDeviceList.add(deviceInfo); } else { Slog.w(TAG, "The list already contains " + deviceInfo + "; ignoring."); return; } } else { - if (!mHdmiCecDeviceList.remove(deviceInfo)) { + if (!mHdmiDeviceList.remove(deviceInfo)) { Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring."); return; } } Message msg = mHandler.obtainMessage( - activated ? ListenerHandler.HDMI_CEC_DEVICE_ADDED - : ListenerHandler.HDMI_CEC_DEVICE_REMOVED, + activated ? ListenerHandler.HDMI_DEVICE_ADDED + : ListenerHandler.HDMI_DEVICE_REMOVED, 0, 0, deviceInfo); if (findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId()) != null) { msg.sendToTarget(); @@ -970,7 +966,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { String inputId; synchronized (mLock) { if (device.isCecDevice()) { - inputId = mHdmiCecInputIdMap.get(device.getLogicalAddress()); + inputId = mHdmiInputIdMap.get(device.getLogicalAddress()); } else { TvInputHardwareInfo hardwareInfo = findHardwareInfoForHdmiPortLocked(device.getPortId()); diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 14d1ec4af617..c5e30d5705f6 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -1933,13 +1933,13 @@ public final class TvInputManagerService extends SystemService { } } - List<HdmiDeviceInfo> cecDeviceInfoList = - mTvInputHardwareManager.getHdmiCecInputDeviceList(); - for (HdmiDeviceInfo cecDeviceInfo : cecDeviceInfoList) { + List<HdmiDeviceInfo> deviceInfoList = + mTvInputHardwareManager.getHdmiDeviceList(); + for (HdmiDeviceInfo deviceInfo : deviceInfoList) { try { - serviceState.mService.notifyHdmiCecDeviceAdded(cecDeviceInfo); + serviceState.mService.notifyHdmiDeviceAdded(deviceInfo); } catch (RemoteException e) { - Slog.e(TAG, "error in notifyHdmiCecDeviceAdded", e); + Slog.e(TAG, "error in notifyHdmiDeviceAdded", e); } } } @@ -2025,11 +2025,11 @@ public final class TvInputManagerService extends SystemService { } @Override - public void addHdmiCecTvInput(int logicalAddress, TvInputInfo inputInfo) { + public void addHdmiTvInput(int logicalAddress, TvInputInfo inputInfo) { ensureHardwarePermission(); ensureValidInput(inputInfo); synchronized (mLock) { - mTvInputHardwareManager.addHdmiCecTvInput(logicalAddress, inputInfo); + mTvInputHardwareManager.addHdmiTvInput(logicalAddress, inputInfo); addTvInputLocked(inputInfo); } } @@ -2275,32 +2275,32 @@ public final class TvInputManagerService extends SystemService { } @Override - public void onHdmiCecDeviceAdded(HdmiDeviceInfo cecDeviceInfo) { + public void onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) { synchronized (mLock) { UserState userState = getUserStateLocked(mCurrentUserId); // Broadcast the event to all hardware inputs. for (ServiceState serviceState : userState.serviceStateMap.values()) { if (!serviceState.mIsHardware || serviceState.mService == null) continue; try { - serviceState.mService.notifyHdmiCecDeviceAdded(cecDeviceInfo); + serviceState.mService.notifyHdmiDeviceAdded(deviceInfo); } catch (RemoteException e) { - Slog.e(TAG, "error in notifyHdmiCecDeviceAdded", e); + Slog.e(TAG, "error in notifyHdmiDeviceAdded", e); } } } } @Override - public void onHdmiCecDeviceRemoved(HdmiDeviceInfo cecDeviceInfo) { + public void onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) { synchronized (mLock) { UserState userState = getUserStateLocked(mCurrentUserId); // Broadcast the event to all hardware inputs. for (ServiceState serviceState : userState.serviceStateMap.values()) { if (!serviceState.mIsHardware || serviceState.mService == null) continue; try { - serviceState.mService.notifyHdmiCecDeviceRemoved(cecDeviceInfo); + serviceState.mService.notifyHdmiDeviceRemoved(deviceInfo); } catch (RemoteException e) { - Slog.e(TAG, "error in notifyHdmiCecDeviceRemoved", e); + Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e); } } } diff --git a/tools/layoutlib/rename_font/build_font_single.py b/tools/layoutlib/rename_font/build_font_single.py new file mode 100755 index 000000000000..d648b047b4b9 --- /dev/null +++ b/tools/layoutlib/rename_font/build_font_single.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python + +# Copyright (C) 2014 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. + +""" +Rename the PS name of the input font. + +OpenType fonts (*.otf) are not currently supported. They are copied to the destination without renaming. +XML files are also copied in case they are passed there by mistake. + +Usage: build_font.py /path/to/input_font.ttf /path/to/output_font.ttf + +""" + +import glob +import os +import re +import shutil +import sys +import xml.etree.ElementTree as etree + +# Prevent .pyc files from being created. +sys.dont_write_bytecode = True + +# fontTools is available at platform/external/fonttools +from fontTools import ttx + + +class FontInfo(object): + family = None + style = None + version = None + ends_in_regular = False + fullname = None + + +class InvalidFontException(Exception): + pass + + +# A constant to copy the font without modifying. This is useful when running +# locally and speed up the time to build the SDK. +COPY_ONLY = False + +# These constants represent the value of nameID parameter in the namerecord for +# different information. +# see http://scripts.sil.org/cms/scripts/page.php?item_id=IWS-Chapter08#3054f18b +NAMEID_FAMILY = 1 +NAMEID_STYLE = 2 +NAMEID_FULLNAME = 4 +NAMEID_VERSION = 5 + +# A list of extensions to process. +EXTENSIONS = ['.ttf', '.otf', '.xml'] + +def main(argv): + if len(argv) < 2: + sys.exit('Usage: build_font.py /path/to/input/font.ttf /path/to/out/font.ttf') + dest_path = argv[-1] + input_path = argv[0] + extension = os.path.splitext(input_path)[1].lower() + if extension in EXTENSIONS: + if not COPY_ONLY and extension == '.ttf': + convert_font(input_path, dest_path) + return + shutil.copy(input_path, dest_path) + + +def convert_font(input_path, dest_path): + filename = os.path.basename(input_path) + print 'Converting font: ' + filename + # the path to the output file. The file name is the fontfilename.ttx + ttx_path = dest_path[:-1] + 'x' + try: + # run ttx to generate an xml file in the output folder which represents all + # its info + ttx_args = ['-q', '-o', ttx_path, input_path] + ttx.main(ttx_args) + # now parse the xml file to change its PS name. + tree = etree.parse(ttx_path) + root = tree.getroot() + for name in root.iter('name'): + update_tag(name, get_font_info(name)) + tree.write(ttx_path, xml_declaration=True, encoding='utf-8') + # generate the udpated font now. + ttx_args = ['-q', '-o', dest_path, ttx_path] + ttx.main(ttx_args) + except InvalidFontException: + # In case of invalid fonts, we exit. + print filename + ' is not a valid font' + raise + except Exception as e: + print 'Error converting font: ' + filename + print e + # Some fonts are too big to be handled by the ttx library. + # Just copy paste them. + shutil.copy(input_path, dest_path) + try: + # delete the temp ttx file is it exists. + os.remove(ttx_path) + except OSError: + pass + + +def get_font_info(tag): + """ Returns a list of FontInfo representing the various sets of namerecords + found in the name table of the font. """ + fonts = [] + font = None + last_name_id = sys.maxint + for namerecord in tag.iter('namerecord'): + if 'nameID' in namerecord.attrib: + name_id = int(namerecord.attrib['nameID']) + # A new font should be created for each platform, encoding and language + # id. But, since the nameIDs are sorted, we use the easy approach of + # creating a new one when the nameIDs reset. + if name_id <= last_name_id and font is not None: + fonts.append(font) + font = None + last_name_id = name_id + if font is None: + font = FontInfo() + if name_id == NAMEID_FAMILY: + font.family = namerecord.text.strip() + if name_id == NAMEID_STYLE: + font.style = namerecord.text.strip() + if name_id == NAMEID_FULLNAME: + font.ends_in_regular = ends_in_regular(namerecord.text) + font.fullname = namerecord.text.strip() + if name_id == NAMEID_VERSION: + font.version = get_version(namerecord.text) + if font is not None: + fonts.append(font) + return fonts + + +def update_tag(tag, fonts): + last_name_id = sys.maxint + fonts_iterator = fonts.__iter__() + font = None + for namerecord in tag.iter('namerecord'): + if 'nameID' in namerecord.attrib: + name_id = int(namerecord.attrib['nameID']) + if name_id <= last_name_id: + font = fonts_iterator.next() + font = update_font_name(font) + last_name_id = name_id + if name_id == NAMEID_FAMILY: + namerecord.text = font.family + if name_id == NAMEID_FULLNAME: + namerecord.text = font.fullname + + +def update_font_name(font): + """ Compute the new font family name and font fullname. If the font has a + valid version, it's sanitized and appended to the font family name. The + font fullname is then created by joining the new family name and the + style. If the style is 'Regular', it is appended only if the original font + had it. """ + if font.family is None or font.style is None: + raise InvalidFontException('Font doesn\'t have proper family name or style') + if font.version is not None: + new_family = font.family + font.version + else: + new_family = font.family + if font.style is 'Regular' and not font.ends_in_regular: + font.fullname = new_family + else: + font.fullname = new_family + ' ' + font.style + font.family = new_family + return font + + +def ends_in_regular(string): + """ According to the specification, the font fullname should not end in + 'Regular' for plain fonts. However, some fonts don't obey this rule. We + keep the style info, to minimize the diff. """ + string = string.strip().split()[-1] + return string is 'Regular' + + +def get_version(string): + # The string must begin with 'Version n.nn ' + # to extract n.nn, we return the second entry in the split strings. + string = string.strip() + if not string.startswith('Version '): + raise InvalidFontException('mal-formed font version') + return sanitize(string.split()[1]) + + +def sanitize(string): + return re.sub(r'[^\w-]+', '', string) + +if __name__ == '__main__': + main(sys.argv[1:]) |