diff options
158 files changed, 4073 insertions, 1889 deletions
diff --git a/api/current.txt b/api/current.txt index 80f83cd..991186f 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1000,6 +1000,9 @@ package android { field public static final int scrollbars = 16842974; // 0x10100de field public static final int scrollingCache = 16843006; // 0x10100fe field public static final deprecated int searchButtonText = 16843269; // 0x1010205 + field public static final int searchKeyphrase = 16843873; // 0x1010461 + field public static final int searchKeyphraseId = 16843872; // 0x1010460 + field public static final int searchKeyphraseSupportedLocales = 16843874; // 0x1010462 field public static final int searchMode = 16843221; // 0x10101d5 field public static final int searchSettingsDescription = 16843402; // 0x101028a field public static final int searchSuggestAuthority = 16843222; // 0x10101d6 @@ -13681,6 +13684,7 @@ package android.media { method public void adjustStreamVolume(int, int, int); method public void adjustSuggestedStreamVolume(int, int, int); method public void adjustVolume(int, int); + method public int allocateAudioSessionId(); method public void dispatchMediaKeyEvent(android.view.KeyEvent); method public int getMode(); method public java.lang.String getParameters(java.lang.String); @@ -13741,6 +13745,7 @@ package android.media { field public static final int AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK = -3; // 0xfffffffd field public static final int AUDIOFOCUS_REQUEST_FAILED = 0; // 0x0 field public static final int AUDIOFOCUS_REQUEST_GRANTED = 1; // 0x1 + field public static final int ERROR = -1; // 0xffffffff field public static final java.lang.String EXTRA_RINGER_MODE = "android.media.EXTRA_RINGER_MODE"; field public static final java.lang.String EXTRA_SCO_AUDIO_PREVIOUS_STATE = "android.media.extra.SCO_AUDIO_PREVIOUS_STATE"; field public static final java.lang.String EXTRA_SCO_AUDIO_STATE = "android.media.extra.SCO_AUDIO_STATE"; @@ -15820,8 +15825,11 @@ package android.media.tv { field public static final int TYPE_ISDB_S = 262656; // 0x40200 field public static final int TYPE_ISDB_T = 262144; // 0x40000 field public static final int TYPE_ISDB_TB = 262400; // 0x40100 + field public static final int TYPE_NTSC = 1; // 0x1 field public static final int TYPE_OTHER = 0; // 0x0 + field public static final int TYPE_PAL = 2; // 0x2 field public static final int TYPE_PASSTHROUGH = 65536; // 0x10000 + field public static final int TYPE_SECAM = 3; // 0x3 field public static final int TYPE_S_DMB = 393472; // 0x60100 field public static final int TYPE_T_DMB = 393216; // 0x60000 } @@ -26010,21 +26018,28 @@ package android.service.notification { method public final void cancelNotification(java.lang.String); method public final void cancelNotifications(java.lang.String[]); method public android.service.notification.StatusBarNotification[] getActiveNotifications(); - method public android.service.notification.NotificationListenerService.Ranking getCurrentRanking(); + method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking(); method public android.os.IBinder onBind(android.content.Intent); method public void onListenerConnected(); - method public abstract void onNotificationPosted(android.service.notification.StatusBarNotification); - method public void onNotificationRankingUpdate(); - method public abstract void onNotificationRemoved(android.service.notification.StatusBarNotification); + method public void onNotificationPosted(android.service.notification.StatusBarNotification); + method public void onNotificationPosted(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap); + method public void onNotificationRankingUpdate(android.service.notification.NotificationListenerService.RankingMap); + method public void onNotificationRemoved(android.service.notification.StatusBarNotification); + method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap); field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService"; } - public static class NotificationListenerService.Ranking implements android.os.Parcelable { + public static class NotificationListenerService.Ranking { + method public java.lang.String getKey(); + method public int getRank(); + method public boolean isAmbient(); + method public boolean isInterceptedByDoNotDisturb(); + } + + public static class NotificationListenerService.RankingMap implements android.os.Parcelable { method public int describeContents(); method public java.lang.String[] getOrderedKeys(); - method public int getRank(java.lang.String); - method public boolean isAmbient(java.lang.String); - method public boolean isInterceptedByDoNotDisturb(java.lang.String); + method public android.service.notification.NotificationListenerService.Ranking getRanking(java.lang.String); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; } diff --git a/cmds/settings/src/com/android/commands/settings/SettingsCmd.java b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java index dce0a75..e6847a9 100644 --- a/cmds/settings/src/com/android/commands/settings/SettingsCmd.java +++ b/cmds/settings/src/com/android/commands/settings/SettingsCmd.java @@ -20,6 +20,7 @@ import android.app.ActivityManagerNative; import android.app.IActivityManager; import android.app.IActivityManager.ContentProviderHolder; import android.content.IContentProvider; +import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; @@ -33,7 +34,8 @@ public final class SettingsCmd { enum CommandVerb { UNSPECIFIED, GET, - PUT + PUT, + DELETE } static String[] mArgs; @@ -74,6 +76,8 @@ public final class SettingsCmd { mVerb = CommandVerb.GET; } else if ("put".equalsIgnoreCase(arg)) { mVerb = CommandVerb.PUT; + } else if ("delete".equalsIgnoreCase(arg)) { + mVerb = CommandVerb.DELETE; } else { // invalid System.err.println("Invalid command: " + arg); @@ -87,7 +91,7 @@ public final class SettingsCmd { break; // invalid } mTable = arg.toLowerCase(); - } else if (mVerb == CommandVerb.GET) { + } else if (mVerb == CommandVerb.GET || mVerb == CommandVerb.DELETE) { mKey = arg; if (mNextArg >= mArgs.length) { valid = true; @@ -136,6 +140,10 @@ public final class SettingsCmd { case PUT: putForUser(provider, mUser, mTable, mKey, mValue); break; + case DELETE: + System.out.println("Deleted " + + deleteForUser(provider, mUser, mTable, mKey) + " rows"); + break; default: System.err.println("Unspecified command"); break; @@ -211,9 +219,31 @@ public final class SettingsCmd { } } + int deleteForUser(IContentProvider provider, int userHandle, + final String table, final String key) { + Uri targetUri; + if ("system".equals(table)) targetUri = Settings.System.getUriFor(key); + else if ("secure".equals(table)) targetUri = Settings.Secure.getUriFor(key); + else if ("global".equals(table)) targetUri = Settings.Global.getUriFor(key); + else { + System.err.println("Invalid table; no delete performed"); + throw new IllegalArgumentException("Invalid table " + table); + } + + int num = 0; + try { + num = provider.delete(null, targetUri, null, null); + } catch (RemoteException e) { + System.err.println("Can't clear key " + key + " in " + table + " for user " + + userHandle); + } + return num; + } + private static void printUsage() { System.err.println("usage: settings [--user NUM] get namespace key"); System.err.println(" settings [--user NUM] put namespace key value"); + System.err.println(" settings [--user NUM] delete namespace key"); System.err.println("\n'namespace' is one of {system, secure, global}, case-insensitive"); System.err.println("If '--user NUM' is not given, the operations are performed on the owner user."); } diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index c29d75e..94ea2c5 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -354,9 +354,11 @@ public class ActivityView extends ViewGroup { private static class ActivityContainerWrapper { private final IActivityContainer mIActivityContainer; private final CloseGuard mGuard = CloseGuard.get(); + boolean mOpened; // Protected by mGuard. ActivityContainerWrapper(IActivityContainer container) { mIActivityContainer = container; + mOpened = true; mGuard.open("release"); } @@ -424,11 +426,16 @@ public class ActivityView extends ViewGroup { } void release() { - if (DEBUG) Log.v(TAG, "ActivityContainerWrapper: release called"); - try { - mIActivityContainer.release(); - mGuard.close(); - } catch (RemoteException e) { + synchronized (mGuard) { + if (mOpened) { + if (DEBUG) Log.v(TAG, "ActivityContainerWrapper: release called"); + try { + mIActivityContainer.release(); + mGuard.close(); + } catch (RemoteException e) { + } + mOpened = false; + } } } diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java index 85e970c..0d94721 100644 --- a/core/java/android/app/VoiceInteractor.java +++ b/core/java/android/app/VoiceInteractor.java @@ -293,7 +293,7 @@ public class VoiceInteractor { IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName, IVoiceInteractorCallback callback) throws RemoteException { - return interactor.startConfirmation(packageName, callback, mCommand, mArgs); + return interactor.startCommand(packageName, callback, mCommand, mArgs); } } diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index ff56720..9eea545 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -30,7 +30,7 @@ import java.util.List; * * <p>These properties are fixed for a given CameraDevice, and can be queried * through the {@link CameraManager CameraManager} - * interface in addition to through the CameraDevice interface.</p> + * interface with {@link CameraManager#getCameraCharacteristics}.</p> * * <p>{@link CameraCharacteristics} objects are immutable.</p> * @@ -555,7 +555,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p>List containing a subset of the optical image * stabilization (OIS) modes specified in * {@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode}.</p> - * <p>If OIS is not implemented for a given camera device, this should + * <p>If OIS is not implemented for a given camera device, this will * contain only OFF.</p> * * @see CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE @@ -612,7 +612,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri /** * <p>Direction the camera faces relative to - * device screen</p> + * device screen.</p> * @see #LENS_FACING_FRONT * @see #LENS_FACING_BACK */ @@ -622,7 +622,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri /** * <p>The set of noise reduction modes supported by this camera device.</p> * <p>This tag lists the valid modes for {@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode}.</p> - * <p>Full-capability camera devices must laways support OFF and FAST.</p> + * <p>Full-capability camera devices must always support OFF and FAST.</p> * * @see CaptureRequest#NOISE_REDUCTION_MODE */ @@ -778,18 +778,20 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<Byte>("android.request.pipelineMaxDepth", byte.class); /** - * <p>Optional. Defaults to 1. Defines how many sub-components + * <p>Defines how many sub-components * a result will be composed of.</p> * <p>In order to combat the pipeline latency, partial results * may be delivered to the application layer from the camera device as * soon as they are available.</p> - * <p>A value of 1 means that partial results are not supported.</p> + * <p>Optional; defaults to 1. A value of 1 means that partial + * results are not supported, and only the final TotalCaptureResult will + * be produced by the camera device.</p> * <p>A typical use case for this might be: after requesting an * auto-focus (AF) lock the new AF state might be available 50% * of the way through the pipeline. The camera device could * then immediately dispatch this state via a partial result to - * the framework/application layer, and the rest of the - * metadata via later partial results.</p> + * the application, and the rest of the metadata via later + * partial results.</p> */ public static final Key<Integer> REQUEST_PARTIAL_RESULT_COUNT = new Key<Integer>("android.request.partialResultCount", int.class); @@ -806,8 +808,6 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * to do this query each of android.request.availableRequestKeys, * android.request.availableResultKeys, * android.request.availableCharacteristicsKeys.</p> - * <p>XX: Maybe these should go into {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} - * as a table instead?</p> * <p>The following capabilities are guaranteed to be available on * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} <code>==</code> FULL devices:</p> * <ul> @@ -815,14 +815,11 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <li>MANUAL_POST_PROCESSING</li> * </ul> * <p>Other capabilities may be available on either FULL or LIMITED - * devices, but the app. should query this field to be sure.</p> + * devices, but the application should query this field to be sure.</p> * * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL - * @see #REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE - * @see #REQUEST_AVAILABLE_CAPABILITIES_OPTIONAL * @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR * @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING - * @see #REQUEST_AVAILABLE_CAPABILITIES_ZSL * @see #REQUEST_AVAILABLE_CAPABILITIES_DNG */ public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES = @@ -838,7 +835,6 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * at a more granular level than capabilities. This is especially * important for optional keys that are not listed under any capability * in {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}.</p> - * <p>TODO: This should be used by #getAvailableCaptureRequestKeys.</p> * * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES * @hide @@ -863,7 +859,6 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * at a more granular level than capabilities. This is especially * important for optional keys that are not listed under any capability * in {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}.</p> - * <p>TODO: This should be used by #getAvailableCaptureResultKeys.</p> * * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES * @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE @@ -879,7 +874,6 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * android.request.availableResultKeys (except that it applies for * CameraCharacteristics instead of CaptureResult). See above for more * details.</p> - * <p>TODO: This should be used by CameraCharacteristics#getKeys.</p> * @hide */ public static final Key<int[]> REQUEST_AVAILABLE_CHARACTERISTICS_KEYS = @@ -927,10 +921,15 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<android.util.Size[]>("android.scaler.availableJpegSizes", android.util.Size[].class); /** - * <p>The maximum ratio between active area width - * and crop region width, or between active area height and - * crop region height, if the crop region height is larger - * than width</p> + * <p>The maximum ratio between both active area width + * and crop region width, and active area height and + * crop region height.</p> + * <p>This represents the maximum amount of zooming possible by + * the camera device, or equivalently, the minimum cropping + * window size.</p> + * <p>Crop regions that have a width or height that is smaller + * than this ratio allows will be rounded up to the minimum + * allowed size by the camera device.</p> */ public static final Key<Float> SCALER_AVAILABLE_MAX_DIGITAL_ZOOM = new Key<Float>("android.scaler.availableMaxDigitalZoom", float.class); @@ -1339,9 +1338,9 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<android.util.Range<Integer>>("android.sensor.info.sensitivityRange", new TypeReference<android.util.Range<Integer>>() {{ }}); /** - * <p>Arrangement of color filters on sensor; + * <p>The arrangement of color filters on sensor; * represents the colors in the top-left 2x2 section of - * the sensor, in reading order</p> + * the sensor, in reading order.</p> * @see #SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB * @see #SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG * @see #SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG @@ -1666,10 +1665,9 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<int[]>("android.sensor.availableTestPatternModes", int[].class); /** - * <p>Which face detection modes are available, - * if any.</p> - * <p>OFF means face detection is disabled, it must - * be included in the list.</p> + * <p>The face detection modes that are available + * for this camera device.</p> + * <p>OFF is always supported.</p> * <p>SIMPLE means the device supports the * android.statistics.faceRectangles and * android.statistics.faceScores outputs.</p> @@ -1681,8 +1679,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<int[]>("android.statistics.info.availableFaceDetectModes", int[].class); /** - * <p>Maximum number of simultaneously detectable - * faces</p> + * <p>The maximum number of simultaneously detectable + * faces.</p> */ public static final Key<Integer> STATISTICS_INFO_MAX_FACE_COUNT = new Key<Integer>("android.statistics.info.maxFaceCount", int.class); @@ -1691,7 +1689,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p>The set of hot pixel map output modes supported by this camera device.</p> * <p>This tag lists valid output modes for {@link CaptureRequest#STATISTICS_HOT_PIXEL_MAP_MODE android.statistics.hotPixelMapMode}.</p> * <p>If no hotpixel map is available for this camera device, this will contain - * only OFF. If the hotpixel map is available, this should include both + * only OFF. If the hotpixel map is available, this will include both * the ON and OFF options.</p> * * @see CaptureRequest#STATISTICS_HOT_PIXEL_MAP_MODE diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index 90e5e4e..33e1915 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -157,8 +157,8 @@ public abstract class CameraMetadata<TKey> { /** * <p>The lens focus distance is not accurate, and the units used for - * {@link CaptureRequest#LENS_FOCUS_DISTANCE android.lens.focusDistance} do not correspond to any physical units. - * Setting the lens to the same focus distance on separate occasions may + * {@link CaptureRequest#LENS_FOCUS_DISTANCE android.lens.focusDistance} do not correspond to any physical units.</p> + * <p>Setting the lens to the same focus distance on separate occasions may * result in a different real focus distance, depending on factors such * as the orientation of the device, the age of the focusing mechanism, * and the device temperature. The focus distance value will still be @@ -172,20 +172,24 @@ public abstract class CameraMetadata<TKey> { public static final int LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED = 0; /** - * <p>The lens focus distance is measured in diopters. However, setting the lens - * to the same focus distance on separate occasions may result in a - * different real focus distance, depending on factors such as the - * orientation of the device, the age of the focusing mechanism, and - * the device temperature.</p> + * <p>The lens focus distance is measured in diopters.</p> + * <p>However, setting the lens to the same focus distance + * on separate occasions may result in a different real + * focus distance, depending on factors such as the + * orientation of the device, the age of the focusing + * mechanism, and the device temperature.</p> * @see CameraCharacteristics#LENS_INFO_FOCUS_DISTANCE_CALIBRATION */ public static final int LENS_INFO_FOCUS_DISTANCE_CALIBRATION_APPROXIMATE = 1; /** - * <p>The lens focus distance is measured in diopters. The lens mechanism is - * calibrated so that setting the same focus distance is repeatable on - * multiple occasions with good accuracy, and the focus distance corresponds - * to the real physical distance to the plane of best focus.</p> + * <p>The lens focus distance is measured in diopters, and + * is calibrated.</p> + * <p>The lens mechanism is calibrated so that setting the + * same focus distance is repeatable on multiple + * occasions with good accuracy, and the focus distance + * corresponds to the real physical distance to the plane + * of best focus.</p> * @see CameraCharacteristics#LENS_INFO_FOCUS_DISTANCE_CALIBRATION */ public static final int LENS_INFO_FOCUS_DISTANCE_CALIBRATION_CALIBRATED = 2; @@ -195,11 +199,13 @@ public abstract class CameraMetadata<TKey> { // /** + * <p>The camera device faces the same direction as the device's screen.</p> * @see CameraCharacteristics#LENS_FACING */ public static final int LENS_FACING_FRONT = 0; /** + * <p>The camera device faces the opposite direction as the device's screen.</p> * @see CameraCharacteristics#LENS_FACING */ public static final int LENS_FACING_BACK = 1; @@ -215,11 +221,10 @@ public abstract class CameraMetadata<TKey> { * <p>The full set of features supported by this capability makes * the camera2 api backwards compatible with the camera1 * (android.hardware.Camera) API.</p> - * <p>TODO: @hide this. Doesn't really mean anything except - * act as a catch-all for all the 'base' functionality.</p> * * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES + * @hide */ public static final int REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE = 0; @@ -228,15 +233,14 @@ public abstract class CameraMetadata<TKey> { * tags or functionality not encapsulated by one of the other * capabilities.</p> * <p>A typical example is all tags marked 'optional'.</p> - * <p>TODO: @hide. We may not need this if we @hide all the optional - * tags not belonging to a capability.</p> * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES + * @hide */ public static final int REQUEST_AVAILABLE_CAPABILITIES_OPTIONAL = 1; /** * <p>The camera device can be manually controlled (3A algorithms such - * as auto exposure, and auto focus can be bypassed). + * as auto-exposure, and auto-focus can be bypassed). * The camera device supports basic manual control of the sensor image * acquisition related stages. This means the following controls are * guaranteed to be supported:</p> @@ -257,11 +261,11 @@ public abstract class CameraMetadata<TKey> { * <li>{@link CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE android.sensor.info.sensitivityRange}</li> * </ul> * </li> - * <li>Manual lens control<ul> + * <li>Manual lens control (if the lens is adjustable)<ul> * <li>android.lens.*</li> * </ul> * </li> - * <li>Manual flash control<ul> + * <li>Manual flash control (if a flash unit is present)<ul> * <li>android.flash.*</li> * </ul> * </li> @@ -312,8 +316,6 @@ public abstract class CameraMetadata<TKey> { * </ul> * <p>If auto white balance is enabled, then the camera device * will accurately report the values applied by AWB in the result.</p> - * <p>The camera device will also support everything in MANUAL_SENSOR - * except manual lens control and manual flash control.</p> * <p>A given camera device may also support additional post-processing * controls, but this capability only covers the above list of controls.</p> * @@ -340,8 +342,8 @@ public abstract class CameraMetadata<TKey> { * (both input/output) will match the maximum available * resolution of JPEG streams.</li> * </ul> - * <p>@hide this, TODO: remove it when input related APIs are ready.</p> * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES + * @hide */ public static final int REQUEST_AVAILABLE_CAPABILITIES_ZSL = 4; @@ -355,7 +357,7 @@ public abstract class CameraMetadata<TKey> { * <li>RAW16 is reprocessable into both YUV_420_888 and JPEG * formats.</li> * <li>The maximum available resolution for RAW16 streams (both - * input/output) will match the either value in + * input/output) will match either the value in * {@link CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE android.sensor.info.pixelArraySize} or * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</li> * <li>All DNG-related optional metadata entries are provided @@ -373,13 +375,13 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>The camera device will only support centered crop regions.</p> + * <p>The camera device only supports centered crop regions.</p> * @see CameraCharacteristics#SCALER_CROPPING_TYPE */ public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0; /** - * <p>The camera device will support arbitrarily chosen crop regions.</p> + * <p>The camera device supports arbitrarily chosen crop regions.</p> * @see CameraCharacteristics#SCALER_CROPPING_TYPE */ public static final int SCALER_CROPPING_TYPE_FREEFORM = 1; @@ -525,7 +527,7 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>android.led.transmit control is used</p> + * <p>android.led.transmit control is used.</p> * @see CameraCharacteristics#LED_AVAILABLE_LEDS * @hide */ @@ -536,11 +538,14 @@ public abstract class CameraMetadata<TKey> { // /** + * <p>This camera device has only limited capabilities.</p> * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL */ public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; /** + * <p>This camera device is capable of supporting advanced imaging + * applications.</p> * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL */ public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; @@ -550,9 +555,9 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>Every frame has the requests immediately applied. - * (and furthermore for all results, - * <code>android.sync.frameNumber == android.request.frameCount</code>)</p> + * <p>Every frame has the requests immediately applied.</p> + * <p>Furthermore for all results, + * <code>android.sync.frameNumber == android.request.frameCount</code></p> * <p>Changing controls over multiple requests one after another will * produce results that have those controls applied atomically * each frame.</p> @@ -592,8 +597,8 @@ public abstract class CameraMetadata<TKey> { public static final int COLOR_CORRECTION_MODE_TRANSFORM_MATRIX = 0; /** - * <p>Must not slow down capture rate relative to sensor raw - * output.</p> + * <p>Color correction processing must not slow down + * capture rate relative to sensor raw output.</p> * <p>Advanced white balance adjustments above and beyond * the specified white balance pipeline may be applied.</p> * <p>If AWB is enabled with <code>{@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} != OFF</code>, then @@ -606,8 +611,9 @@ public abstract class CameraMetadata<TKey> { public static final int COLOR_CORRECTION_MODE_FAST = 1; /** - * <p>Capture rate (relative to sensor raw output) - * may be reduced by high quality.</p> + * <p>Color correction processing operates at improved + * quality but reduced capture rate (relative to sensor raw + * output).</p> * <p>Advanced white balance adjustments above and beyond * the specified white balance pipeline may be applied.</p> * <p>If AWB is enabled with <code>{@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} != OFF</code>, then @@ -658,8 +664,8 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>The camera device's autoexposure routine is disabled; - * the application-selected {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}, + * <p>The camera device's autoexposure routine is disabled.</p> + * <p>The application-selected {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}, * {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} and * {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} are used by the camera * device, along with android.flash.* fields, if there's @@ -674,7 +680,8 @@ public abstract class CameraMetadata<TKey> { /** * <p>The camera device's autoexposure routine is active, - * with no flash control. The application's values for + * with no flash control.</p> + * <p>The application's values for * {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}, * {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}, and * {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} are ignored. The @@ -691,10 +698,10 @@ public abstract class CameraMetadata<TKey> { /** * <p>Like ON, except that the camera device also controls * the camera's flash unit, firing it in low-light - * conditions. The flash may be fired during a - * precapture sequence (triggered by - * {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}) and may be fired - * for captures for which the + * conditions.</p> + * <p>The flash may be fired during a precapture sequence + * (triggered by {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}) and + * may be fired for captures for which the * {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} field is set to * STILL_CAPTURE</p> * @@ -707,10 +714,10 @@ public abstract class CameraMetadata<TKey> { /** * <p>Like ON, except that the camera device also controls * the camera's flash unit, always firing it for still - * captures. The flash may be fired during a precapture - * sequence (triggered by - * {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}) and will always - * be fired for captures for which the + * captures.</p> + * <p>The flash may be fired during a precapture sequence + * (triggered by {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}) and + * will always be fired for captures for which the * {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} field is set to * STILL_CAPTURE</p> * @@ -722,9 +729,10 @@ public abstract class CameraMetadata<TKey> { /** * <p>Like ON_AUTO_FLASH, but with automatic red eye - * reduction. If deemed necessary by the camera device, - * a red eye reduction flash will fire during the - * precapture sequence.</p> + * reduction.</p> + * <p>If deemed necessary by the camera device, a red eye + * reduction flash will fire during the precapture + * sequence.</p> * @see CaptureRequest#CONTROL_AE_MODE */ public static final int CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE = 4; @@ -741,8 +749,9 @@ public abstract class CameraMetadata<TKey> { /** * <p>The precapture metering sequence will be started - * by the camera device. The exact effect of the precapture - * trigger depends on the current AE mode and state.</p> + * by the camera device.</p> + * <p>The exact effect of the precapture trigger depends on + * the current AE mode and state.</p> * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER */ public static final int CONTROL_AE_PRECAPTURE_TRIGGER_START = 1; @@ -754,7 +763,7 @@ public abstract class CameraMetadata<TKey> { /** * <p>The auto-focus routine does not control the lens; * {@link CaptureRequest#LENS_FOCUS_DISTANCE android.lens.focusDistance} is controlled by the - * application</p> + * application.</p> * * @see CaptureRequest#LENS_FOCUS_DISTANCE * @see CaptureRequest#CONTROL_AF_MODE @@ -839,8 +848,11 @@ public abstract class CameraMetadata<TKey> { public static final int CONTROL_AF_MODE_CONTINUOUS_PICTURE = 4; /** - * <p>Extended depth of field (digital focus). AF - * trigger is ignored, AF state should always be + * <p>Extended depth of field (digital focus) mode.</p> + * <p>The camera device will produce images with an extended + * depth of field automatically; no special focusing + * operations need to be done before taking a picture.</p> + * <p>AF triggers are ignored, and the AF state will always be * INACTIVE.</p> * @see CaptureRequest#CONTROL_AF_MODE */ @@ -874,8 +886,8 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>The camera device's auto white balance routine is disabled; - * the application-selected color transform matrix + * <p>The camera device's auto-white balance routine is disabled.</p> + * <p>The application-selected color transform matrix * ({@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}) and gains * ({@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains}) are used by the camera * device for manual white balance control.</p> @@ -887,9 +899,12 @@ public abstract class CameraMetadata<TKey> { public static final int CONTROL_AWB_MODE_OFF = 0; /** - * <p>The camera device's auto white balance routine is active; - * the application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform} - * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored.</p> + * <p>The camera device's auto-white balance routine is active.</p> + * <p>The application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform} + * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored. + * For devices that support the MANUAL_POST_PROCESSING capability, the + * values used by the camera device for the transform and gains + * will be available in the capture result for this request.</p> * * @see CaptureRequest#COLOR_CORRECTION_GAINS * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM @@ -898,65 +913,125 @@ public abstract class CameraMetadata<TKey> { public static final int CONTROL_AWB_MODE_AUTO = 1; /** - * <p>The camera device's auto white balance routine is disabled; + * <p>The camera device's auto-white balance routine is disabled; * the camera device uses incandescent light as the assumed scene - * illumination for white balance. While the exact white balance - * transforms are up to the camera device, they will approximately - * match the CIE standard illuminant A.</p> + * illumination for white balance.</p> + * <p>While the exact white balance transforms are up to the + * camera device, they will approximately match the CIE + * standard illuminant A.</p> + * <p>The application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform} + * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored. + * For devices that support the MANUAL_POST_PROCESSING capability, the + * values used by the camera device for the transform and gains + * will be available in the capture result for this request.</p> + * + * @see CaptureRequest#COLOR_CORRECTION_GAINS + * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM * @see CaptureRequest#CONTROL_AWB_MODE */ public static final int CONTROL_AWB_MODE_INCANDESCENT = 2; /** - * <p>The camera device's auto white balance routine is disabled; + * <p>The camera device's auto-white balance routine is disabled; * the camera device uses fluorescent light as the assumed scene - * illumination for white balance. While the exact white balance - * transforms are up to the camera device, they will approximately - * match the CIE standard illuminant F2.</p> + * illumination for white balance.</p> + * <p>While the exact white balance transforms are up to the + * camera device, they will approximately match the CIE + * standard illuminant F2.</p> + * <p>The application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform} + * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored. + * For devices that support the MANUAL_POST_PROCESSING capability, the + * values used by the camera device for the transform and gains + * will be available in the capture result for this request.</p> + * + * @see CaptureRequest#COLOR_CORRECTION_GAINS + * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM * @see CaptureRequest#CONTROL_AWB_MODE */ public static final int CONTROL_AWB_MODE_FLUORESCENT = 3; /** - * <p>The camera device's auto white balance routine is disabled; + * <p>The camera device's auto-white balance routine is disabled; * the camera device uses warm fluorescent light as the assumed scene - * illumination for white balance. While the exact white balance - * transforms are up to the camera device, they will approximately - * match the CIE standard illuminant F4.</p> + * illumination for white balance.</p> + * <p>While the exact white balance transforms are up to the + * camera device, they will approximately match the CIE + * standard illuminant F4.</p> + * <p>The application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform} + * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored. + * For devices that support the MANUAL_POST_PROCESSING capability, the + * values used by the camera device for the transform and gains + * will be available in the capture result for this request.</p> + * + * @see CaptureRequest#COLOR_CORRECTION_GAINS + * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM * @see CaptureRequest#CONTROL_AWB_MODE */ public static final int CONTROL_AWB_MODE_WARM_FLUORESCENT = 4; /** - * <p>The camera device's auto white balance routine is disabled; + * <p>The camera device's auto-white balance routine is disabled; * the camera device uses daylight light as the assumed scene - * illumination for white balance. While the exact white balance - * transforms are up to the camera device, they will approximately - * match the CIE standard illuminant D65.</p> + * illumination for white balance.</p> + * <p>While the exact white balance transforms are up to the + * camera device, they will approximately match the CIE + * standard illuminant D65.</p> + * <p>The application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform} + * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored. + * For devices that support the MANUAL_POST_PROCESSING capability, the + * values used by the camera device for the transform and gains + * will be available in the capture result for this request.</p> + * + * @see CaptureRequest#COLOR_CORRECTION_GAINS + * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM * @see CaptureRequest#CONTROL_AWB_MODE */ public static final int CONTROL_AWB_MODE_DAYLIGHT = 5; /** - * <p>The camera device's auto white balance routine is disabled; + * <p>The camera device's auto-white balance routine is disabled; * the camera device uses cloudy daylight light as the assumed scene * illumination for white balance.</p> + * <p>The application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform} + * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored. + * For devices that support the MANUAL_POST_PROCESSING capability, the + * values used by the camera device for the transform and gains + * will be available in the capture result for this request.</p> + * + * @see CaptureRequest#COLOR_CORRECTION_GAINS + * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM * @see CaptureRequest#CONTROL_AWB_MODE */ public static final int CONTROL_AWB_MODE_CLOUDY_DAYLIGHT = 6; /** - * <p>The camera device's auto white balance routine is disabled; + * <p>The camera device's auto-white balance routine is disabled; * the camera device uses twilight light as the assumed scene * illumination for white balance.</p> + * <p>The application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform} + * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored. + * For devices that support the MANUAL_POST_PROCESSING capability, the + * values used by the camera device for the transform and gains + * will be available in the capture result for this request.</p> + * + * @see CaptureRequest#COLOR_CORRECTION_GAINS + * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM * @see CaptureRequest#CONTROL_AWB_MODE */ public static final int CONTROL_AWB_MODE_TWILIGHT = 7; /** - * <p>The camera device's auto white balance routine is disabled; + * <p>The camera device's auto-white balance routine is disabled; * the camera device uses shade light as the assumed scene * illumination for white balance.</p> + * <p>The application's values for {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform} + * and {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} are ignored. + * For devices that support the MANUAL_POST_PROCESSING capability, the + * values used by the camera device for the transform and gains + * will be available in the capture result for this request.</p> + * + * @see CaptureRequest#COLOR_CORRECTION_GAINS + * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM * @see CaptureRequest#CONTROL_AWB_MODE */ public static final int CONTROL_AWB_MODE_SHADE = 8; @@ -966,38 +1041,43 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>This request doesn't fall into the other - * categories. Default to preview-like + * <p>The goal of this request doesn't fall into the other + * categories. The camera device will default to preview-like * behavior.</p> * @see CaptureRequest#CONTROL_CAPTURE_INTENT */ public static final int CONTROL_CAPTURE_INTENT_CUSTOM = 0; /** - * <p>This request is for a preview-like usecase. The - * precapture trigger may be used to start off a metering - * w/flash sequence</p> + * <p>This request is for a preview-like use case.</p> + * <p>The precapture trigger may be used to start off a metering + * w/flash sequence.</p> * @see CaptureRequest#CONTROL_CAPTURE_INTENT */ public static final int CONTROL_CAPTURE_INTENT_PREVIEW = 1; /** * <p>This request is for a still capture-type - * usecase.</p> + * use case.</p> + * <p>If the flash unit is under automatic control, it may fire as needed.</p> * @see CaptureRequest#CONTROL_CAPTURE_INTENT */ public static final int CONTROL_CAPTURE_INTENT_STILL_CAPTURE = 2; /** * <p>This request is for a video recording - * usecase.</p> + * use case.</p> * @see CaptureRequest#CONTROL_CAPTURE_INTENT */ public static final int CONTROL_CAPTURE_INTENT_VIDEO_RECORD = 3; /** * <p>This request is for a video snapshot (still - * image while recording video) usecase</p> + * image while recording video) use case.</p> + * <p>The camera device should take the highest-quality image + * possible (given the other settings) without disrupting the + * frame rate of video recording.<br /> + * </p> * @see CaptureRequest#CONTROL_CAPTURE_INTENT */ public static final int CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT = 4; @@ -1006,15 +1086,16 @@ public abstract class CameraMetadata<TKey> { * <p>This request is for a ZSL usecase; the * application will stream full-resolution images and * reprocess one or several later for a final - * capture</p> + * capture.</p> * @see CaptureRequest#CONTROL_CAPTURE_INTENT */ public static final int CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG = 5; /** * <p>This request is for manual capture use case where - * the applications want to directly control the capture parameters - * (e.g. {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}, {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity} etc.).</p> + * the applications want to directly control the capture parameters.</p> + * <p>For example, the application may wish to manually control + * {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}, {@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}, etc.</p> * * @see CaptureRequest#SENSOR_EXPOSURE_TIME * @see CaptureRequest#SENSOR_SENSITIVITY @@ -1034,7 +1115,8 @@ public abstract class CameraMetadata<TKey> { /** * <p>A "monocolor" effect where the image is mapped into - * a single color. This will typically be grayscale.</p> + * a single color.</p> + * <p>This will typically be grayscale.</p> * @see CaptureRequest#CONTROL_EFFECT_MODE */ public static final int CONTROL_EFFECT_MODE_MONO = 1; @@ -1094,31 +1176,42 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>Full application control of pipeline. All 3A - * routines are disabled, no other settings in - * android.control.* have any effect</p> + * <p>Full application control of pipeline.</p> + * <p>All control by the device's metering and focusing (3A) + * routines is disabled, and no other settings in + * android.control.* have any effect, except that + * {@link CaptureRequest#CONTROL_CAPTURE_INTENT android.control.captureIntent} may be used by the camera + * device to select post-processing values for processing + * blocks that do not allow for manual control, or are not + * exposed by the camera API.</p> + * <p>However, the camera device's 3A routines may continue to + * collect statistics and update their internal state so that + * when control is switched to AUTO mode, good control values + * can be immediately applied.</p> + * + * @see CaptureRequest#CONTROL_CAPTURE_INTENT * @see CaptureRequest#CONTROL_MODE */ public static final int CONTROL_MODE_OFF = 0; /** - * <p>Use settings for each individual 3A routine. - * Manual control of capture parameters is disabled. All + * <p>Use settings for each individual 3A routine.</p> + * <p>Manual control of capture parameters is disabled. All * controls in android.control.* besides sceneMode take - * effect</p> + * effect.</p> * @see CaptureRequest#CONTROL_MODE */ public static final int CONTROL_MODE_AUTO = 1; /** - * <p>Use specific scene mode. Enabling this disables - * control.aeMode, control.awbMode and control.afMode - * controls; the camera device will ignore those settings while - * USE_SCENE_MODE is active (except for FACE_PRIORITY - * scene mode). Other control entries are still active. - * This setting can only be used if scene mode is supported - * (i.e. {@link CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES android.control.availableSceneModes} contain some modes - * other than DISABLED).</p> + * <p>Use a specific scene mode.</p> + * <p>Enabling this disables control.aeMode, control.awbMode and + * control.afMode controls; the camera device will ignore + * those settings while USE_SCENE_MODE is active (except for + * FACE_PRIORITY scene mode). Other control entries are still + * active. This setting can only be used if scene mode is + * supported (i.e. {@link CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES android.control.availableSceneModes} + * contain some modes other than DISABLED).</p> * * @see CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES * @see CaptureRequest#CONTROL_MODE @@ -1128,7 +1221,12 @@ public abstract class CameraMetadata<TKey> { /** * <p>Same as OFF mode, except that this capture will not be * used by camera device background auto-exposure, auto-white balance and - * auto-focus algorithms to update their statistics.</p> + * auto-focus algorithms (3A) to update their statistics.</p> + * <p>Specifically, the 3A routines are locked to the last + * values set from a request with AUTO, OFF, or + * USE_SCENE_MODE, and any statistics or state updates + * collected from manual captures with OFF_KEEP_STATE will be + * discarded by the camera device.</p> * @see CaptureRequest#CONTROL_MODE */ public static final int CONTROL_MODE_OFF_KEEP_STATE = 3; @@ -1146,8 +1244,9 @@ public abstract class CameraMetadata<TKey> { /** * <p>If face detection support exists, use face * detection data for auto-focus, auto-white balance, and - * auto-exposure routines. If face detection statistics are - * disabled (i.e. {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} is set to OFF), + * auto-exposure routines.</p> + * <p>If face detection statistics are disabled + * (i.e. {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} is set to OFF), * this should still operate correctly (but will not return * face detection statistics to the framework).</p> * <p>Unlike the other scene modes, {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode}, @@ -1163,8 +1262,8 @@ public abstract class CameraMetadata<TKey> { public static final int CONTROL_SCENE_MODE_FACE_PRIORITY = 1; /** - * <p>Optimized for photos of quickly moving objects. - * Similar to SPORTS.</p> + * <p>Optimized for photos of quickly moving objects.</p> + * <p>Similar to SPORTS.</p> * @see CaptureRequest#CONTROL_SCENE_MODE */ public static final int CONTROL_SCENE_MODE_ACTION = 2; @@ -1233,8 +1332,8 @@ public abstract class CameraMetadata<TKey> { public static final int CONTROL_SCENE_MODE_FIREWORKS = 12; /** - * <p>Optimized for photos of quickly moving people. - * Similar to ACTION.</p> + * <p>Optimized for photos of quickly moving people.</p> + * <p>Similar to ACTION.</p> * @see CaptureRequest#CONTROL_SCENE_MODE */ public static final int CONTROL_SCENE_MODE_SPORTS = 13; @@ -1266,11 +1365,13 @@ public abstract class CameraMetadata<TKey> { // /** + * <p>Video stabilization is disabled.</p> * @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE */ public static final int CONTROL_VIDEO_STABILIZATION_MODE_OFF = 0; /** + * <p>Video stabilization is enabled.</p> * @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE */ public static final int CONTROL_VIDEO_STABILIZATION_MODE_ON = 1; @@ -1280,21 +1381,20 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>No edge enhancement is applied</p> + * <p>No edge enhancement is applied.</p> * @see CaptureRequest#EDGE_MODE */ public static final int EDGE_MODE_OFF = 0; /** - * <p>Must not slow down frame rate relative to sensor + * <p>Apply edge enhancement at a quality level that does not slow down frame rate relative to sensor * output</p> * @see CaptureRequest#EDGE_MODE */ public static final int EDGE_MODE_FAST = 1; /** - * <p>Frame rate may be reduced by high - * quality</p> + * <p>Apply high-quality edge enhancement, at a cost of reducing output frame rate.</p> * @see CaptureRequest#EDGE_MODE */ public static final int EDGE_MODE_HIGH_QUALITY = 2; @@ -1327,10 +1427,10 @@ public abstract class CameraMetadata<TKey> { // /** + * <p>No hot pixel correction is applied.</p> * <p>The frame rate must not be reduced relative to sensor raw output * for this option.</p> - * <p>No hot pixel correction is applied. - * The hotpixel map may be returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}.</p> + * <p>The hotpixel map may be returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}.</p> * * @see CaptureResult#STATISTICS_HOT_PIXEL_MAP * @see CaptureRequest#HOT_PIXEL_MODE @@ -1338,10 +1438,9 @@ public abstract class CameraMetadata<TKey> { public static final int HOT_PIXEL_MODE_OFF = 0; /** - * <p>The frame rate must not be reduced relative to sensor raw output - * for this option.</p> - * <p>Hot pixel correction is applied. - * The hotpixel map may be returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}.</p> + * <p>Hot pixel correction is applied, without reducing frame + * rate relative to sensor raw output.</p> + * <p>The hotpixel map may be returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}.</p> * * @see CaptureResult#STATISTICS_HOT_PIXEL_MAP * @see CaptureRequest#HOT_PIXEL_MODE @@ -1349,10 +1448,9 @@ public abstract class CameraMetadata<TKey> { public static final int HOT_PIXEL_MODE_FAST = 1; /** - * <p>The frame rate may be reduced relative to sensor raw output - * for this option.</p> - * <p>A high-quality hot pixel correction is applied. - * The hotpixel map may be returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}.</p> + * <p>High-quality hot pixel correction is applied, at a cost + * of reducing frame rate relative to sensor raw output.</p> + * <p>The hotpixel map may be returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}.</p> * * @see CaptureResult#STATISTICS_HOT_PIXEL_MAP * @see CaptureRequest#HOT_PIXEL_MODE @@ -1380,21 +1478,21 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>No noise reduction is applied</p> + * <p>No noise reduction is applied.</p> * @see CaptureRequest#NOISE_REDUCTION_MODE */ public static final int NOISE_REDUCTION_MODE_OFF = 0; /** - * <p>Must not slow down frame rate relative to sensor - * output</p> + * <p>Noise reduction is applied without reducing frame rate relative to sensor + * output.</p> * @see CaptureRequest#NOISE_REDUCTION_MODE */ public static final int NOISE_REDUCTION_MODE_FAST = 1; /** - * <p>May slow down frame rate to provide highest - * quality</p> + * <p>High-quality noise reduction is applied, at the cost of reducing frame rate + * relative to sensor output.</p> * @see CaptureRequest#NOISE_REDUCTION_MODE */ public static final int NOISE_REDUCTION_MODE_HIGH_QUALITY = 2; @@ -1404,8 +1502,9 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>Default. No test pattern mode is used, and the camera + * <p>No test pattern mode is used, and the camera * device returns captures from the image sensor.</p> + * <p>This is the default if the key is not set.</p> * @see CaptureRequest#SENSOR_TEST_PATTERN_MODE */ public static final int SENSOR_TEST_PATTERN_MODE_OFF = 0; @@ -1509,19 +1608,21 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>No lens shading correction is applied</p> + * <p>No lens shading correction is applied.</p> * @see CaptureRequest#SHADING_MODE */ public static final int SHADING_MODE_OFF = 0; /** - * <p>Must not slow down frame rate relative to sensor raw output</p> + * <p>Apply lens shading corrections, without slowing + * frame rate relative to sensor raw output</p> * @see CaptureRequest#SHADING_MODE */ public static final int SHADING_MODE_FAST = 1; /** - * <p>Frame rate may be reduced by high quality</p> + * <p>Apply high-quality lens shading correction, at the + * cost of reduced frame rate.</p> * @see CaptureRequest#SHADING_MODE */ public static final int SHADING_MODE_HIGH_QUALITY = 2; @@ -1531,20 +1632,28 @@ public abstract class CameraMetadata<TKey> { // /** + * <p>Do not include face detection statistics in capture + * results.</p> * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE */ public static final int STATISTICS_FACE_DETECT_MODE_OFF = 0; /** - * <p>Optional Return rectangle and confidence - * only</p> + * <p>Return face rectangle and confidence values only.</p> + * <p>In this mode, only android.statistics.faceRectangles and + * android.statistics.faceScores outputs are valid.</p> * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE */ public static final int STATISTICS_FACE_DETECT_MODE_SIMPLE = 1; /** - * <p>Optional Return all face - * metadata</p> + * <p>Return all face + * metadata.</p> + * <p>In this mode, + * android.statistics.faceRectangles, + * android.statistics.faceScores, + * android.statistics.faceIds, and + * android.statistics.faceLandmarks outputs are valid.</p> * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE */ public static final int STATISTICS_FACE_DETECT_MODE_FULL = 2; @@ -1554,11 +1663,13 @@ public abstract class CameraMetadata<TKey> { // /** + * <p>Do not include a lens shading map in the capture result.</p> * @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE */ public static final int STATISTICS_LENS_SHADING_MAP_MODE_OFF = 0; /** + * <p>Include a lens shading map in the capture result.</p> * @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE */ public static final int STATISTICS_LENS_SHADING_MAP_MODE_ON = 1; @@ -1582,15 +1693,15 @@ public abstract class CameraMetadata<TKey> { public static final int TONEMAP_MODE_CONTRAST_CURVE = 0; /** - * <p>Advanced gamma mapping and color enhancement may be applied.</p> - * <p>Should not slow down frame rate relative to raw sensor output.</p> + * <p>Advanced gamma mapping and color enhancement may be applied, without + * reducing frame rate compared to raw sensor output.</p> * @see CaptureRequest#TONEMAP_MODE */ public static final int TONEMAP_MODE_FAST = 1; /** - * <p>Advanced gamma mapping and color enhancement may be applied.</p> - * <p>May slow down frame rate relative to raw sensor output.</p> + * <p>High-quality gamma mapping and color enhancement will be applied, at + * the cost of reduced frame rate compared to raw sensor output.</p> * @see CaptureRequest#TONEMAP_MODE */ public static final int TONEMAP_MODE_HIGH_QUALITY = 2; @@ -1600,7 +1711,8 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>AE is off or recently reset. When a camera device is opened, it starts in + * <p>AE is off or recently reset.</p> + * <p>When a camera device is opened, it starts in * this state. This is a transient state, the camera device may skip reporting * this state in capture result.</p> * @see CaptureResult#CONTROL_AE_STATE @@ -1609,7 +1721,8 @@ public abstract class CameraMetadata<TKey> { /** * <p>AE doesn't yet have a good set of control values - * for the current scene. This is a transient state, the camera device may skip + * for the current scene.</p> + * <p>This is a transient state, the camera device may skip * reporting this state in capture result.</p> * @see CaptureResult#CONTROL_AE_STATE */ @@ -1638,11 +1751,13 @@ public abstract class CameraMetadata<TKey> { /** * <p>AE has been asked to do a precapture sequence - * (through the {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger} START), - * and is currently executing it. Once PRECAPTURE - * completes, AE will transition to CONVERGED or - * FLASH_REQUIRED as appropriate. This is a transient state, the - * camera device may skip reporting this state in capture result.</p> + * and is currently executing it.</p> + * <p>Precapture can be triggered through setting + * {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger} to START.</p> + * <p>Once PRECAPTURE completes, AE will transition to CONVERGED + * or FLASH_REQUIRED as appropriate. This is a transient + * state, the camera device may skip reporting this state in + * capture result.</p> * * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER * @see CaptureResult#CONTROL_AE_STATE @@ -1654,61 +1769,78 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>AF off or has not yet tried to scan/been asked - * to scan. When a camera device is opened, it starts in - * this state. This is a transient state, the camera device may - * skip reporting this state in capture result.</p> + * <p>AF is off or has not yet tried to scan/been asked + * to scan.</p> + * <p>When a camera device is opened, it starts in this + * state. This is a transient state, the camera device may + * skip reporting this state in capture + * result.</p> * @see CaptureResult#CONTROL_AF_STATE */ public static final int CONTROL_AF_STATE_INACTIVE = 0; /** - * <p>if CONTINUOUS_* modes are supported. AF is - * currently doing an AF scan initiated by a continuous - * autofocus mode. This is a transient state, the camera device may - * skip reporting this state in capture result.</p> + * <p>AF is currently performing an AF scan initiated the + * camera device in a continuous autofocus mode.</p> + * <p>Only used by CONTINUOUS_* AF modes. This is a transient + * state, the camera device may skip reporting this state in + * capture result.</p> * @see CaptureResult#CONTROL_AF_STATE */ public static final int CONTROL_AF_STATE_PASSIVE_SCAN = 1; /** - * <p>if CONTINUOUS_* modes are supported. AF currently - * believes it is in focus, but may restart scanning at - * any time. This is a transient state, the camera device may skip - * reporting this state in capture result.</p> + * <p>AF currently believes it is in focus, but may + * restart scanning at any time.</p> + * <p>Only used by CONTINUOUS_* AF modes. This is a transient + * state, the camera device may skip reporting this state in + * capture result.</p> * @see CaptureResult#CONTROL_AF_STATE */ public static final int CONTROL_AF_STATE_PASSIVE_FOCUSED = 2; /** - * <p>if AUTO or MACRO modes are supported. AF is doing - * an AF scan because it was triggered by AF trigger. This is a - * transient state, the camera device may skip reporting - * this state in capture result.</p> + * <p>AF is performing an AF scan because it was + * triggered by AF trigger.</p> + * <p>Only used by AUTO or MACRO AF modes. This is a transient + * state, the camera device may skip reporting this state in + * capture result.</p> * @see CaptureResult#CONTROL_AF_STATE */ public static final int CONTROL_AF_STATE_ACTIVE_SCAN = 3; /** - * <p>if any AF mode besides OFF is supported. AF - * believes it is focused correctly and is - * locked.</p> + * <p>AF believes it is focused correctly and has locked + * focus.</p> + * <p>This state is reached only after an explicit START AF trigger has been + * sent ({@link CaptureRequest#CONTROL_AF_TRIGGER android.control.afTrigger}), when good focus has been obtained.</p> + * <p>The lens will remain stationary until the AF mode ({@link CaptureRequest#CONTROL_AF_MODE android.control.afMode}) is changed or + * a new AF trigger is sent to the camera device ({@link CaptureRequest#CONTROL_AF_TRIGGER android.control.afTrigger}).</p> + * + * @see CaptureRequest#CONTROL_AF_MODE + * @see CaptureRequest#CONTROL_AF_TRIGGER * @see CaptureResult#CONTROL_AF_STATE */ public static final int CONTROL_AF_STATE_FOCUSED_LOCKED = 4; /** - * <p>if any AF mode besides OFF is supported. AF has - * failed to focus successfully and is - * locked.</p> + * <p>AF has failed to focus successfully and has locked + * focus.</p> + * <p>This state is reached only after an explicit START AF trigger has been + * sent ({@link CaptureRequest#CONTROL_AF_TRIGGER android.control.afTrigger}), when good focus cannot be obtained.</p> + * <p>The lens will remain stationary until the AF mode ({@link CaptureRequest#CONTROL_AF_MODE android.control.afMode}) is changed or + * a new AF trigger is sent to the camera device ({@link CaptureRequest#CONTROL_AF_TRIGGER android.control.afTrigger}).</p> + * + * @see CaptureRequest#CONTROL_AF_MODE + * @see CaptureRequest#CONTROL_AF_TRIGGER * @see CaptureResult#CONTROL_AF_STATE */ public static final int CONTROL_AF_STATE_NOT_FOCUSED_LOCKED = 5; /** - * <p>if CONTINUOUS_* modes are supported. AF finished a - * passive scan without finding focus, and may restart - * scanning at any time. This is a transient state, the camera + * <p>AF finished a passive scan without finding focus, + * and may restart scanning at any time.</p> + * <p>Only used by CONTINUOUS_* AF modes. This is a transient state, the camera * device may skip reporting this state in capture result.</p> * @see CaptureResult#CONTROL_AF_STATE */ @@ -1719,16 +1851,19 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>AWB is not in auto mode. When a camera device is opened, it - * starts in this state. This is a transient state, the camera device may - * skip reporting this state in capture result.</p> + * <p>AWB is not in auto mode, or has not yet started metering.</p> + * <p>When a camera device is opened, it starts in this + * state. This is a transient state, the camera device may + * skip reporting this state in capture + * result.</p> * @see CaptureResult#CONTROL_AWB_STATE */ public static final int CONTROL_AWB_STATE_INACTIVE = 0; /** * <p>AWB doesn't yet have a good set of control - * values for the current scene. This is a transient state, the camera device + * values for the current scene.</p> + * <p>This is a transient state, the camera device * may skip reporting this state in capture result.</p> * @see CaptureResult#CONTROL_AWB_STATE */ @@ -1776,8 +1911,9 @@ public abstract class CameraMetadata<TKey> { public static final int FLASH_STATE_FIRED = 3; /** - * <p>Flash partially illuminated this frame. This is usually due to the next - * or previous frame having the flash fire, and the flash spilling into this capture + * <p>Flash partially illuminated this frame.</p> + * <p>This is usually due to the next or previous frame having + * the flash fire, and the flash spilling into this capture * due to hardware limitations.</p> * @see CaptureResult#FLASH_STATE */ @@ -1800,8 +1936,10 @@ public abstract class CameraMetadata<TKey> { public static final int LENS_STATE_STATIONARY = 0; /** - * <p>Any of the lens parameters ({@link CaptureRequest#LENS_FOCAL_LENGTH android.lens.focalLength}, {@link CaptureRequest#LENS_FOCUS_DISTANCE android.lens.focusDistance}, - * {@link CaptureRequest#LENS_FILTER_DENSITY android.lens.filterDensity} or {@link CaptureRequest#LENS_APERTURE android.lens.aperture}) is changing.</p> + * <p>One or several of the lens parameters + * ({@link CaptureRequest#LENS_FOCAL_LENGTH android.lens.focalLength}, {@link CaptureRequest#LENS_FOCUS_DISTANCE android.lens.focusDistance}, + * {@link CaptureRequest#LENS_FILTER_DENSITY android.lens.filterDensity} or {@link CaptureRequest#LENS_APERTURE android.lens.aperture}) is + * currently changing.</p> * * @see CaptureRequest#LENS_APERTURE * @see CaptureRequest#LENS_FILTER_DENSITY @@ -1816,16 +1954,22 @@ public abstract class CameraMetadata<TKey> { // /** + * <p>The camera device does not detect any flickering illumination + * in the current scene.</p> * @see CaptureResult#STATISTICS_SCENE_FLICKER */ public static final int STATISTICS_SCENE_FLICKER_NONE = 0; /** + * <p>The camera device detects illumination flickering at 50Hz + * in the current scene.</p> * @see CaptureResult#STATISTICS_SCENE_FLICKER */ public static final int STATISTICS_SCENE_FLICKER_50HZ = 1; /** + * <p>The camera device detects illumination flickering at 60Hz + * in the current scene.</p> * @see CaptureResult#STATISTICS_SCENE_FLICKER */ public static final int STATISTICS_SCENE_FLICKER_60HZ = 2; @@ -1835,8 +1979,8 @@ public abstract class CameraMetadata<TKey> { // /** - * <p>The current result is not yet fully synchronized to any request. - * Synchronization is in progress, and reading metadata from this + * <p>The current result is not yet fully synchronized to any request.</p> + * <p>Synchronization is in progress, and reading metadata from this * result may include a mix of data that have taken effect since the * last synchronization time.</p> * <p>In some future result, within {@link CameraCharacteristics#SYNC_MAX_LATENCY android.sync.maxLatency} frames, @@ -1851,10 +1995,10 @@ public abstract class CameraMetadata<TKey> { public static final int SYNC_FRAME_NUMBER_CONVERGING = -1; /** - * <p>The current result's synchronization status is unknown. The - * result may have already converged, or it may be in progress. - * Reading from this result may include some mix of settings from - * past requests.</p> + * <p>The current result's synchronization status is unknown.</p> + * <p>The result may have already converged, or it may be in + * progress. Reading from this result may include some mix + * of settings from past requests.</p> * <p>After a settings change, the new settings will eventually all * take effect for the output buffers and results. However, this * value will not change when that happens. Altering settings diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index 0ff8cce..bf7bd37 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -733,8 +733,16 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * included at all in the request settings. When included and * set to START, the camera device will trigger the autoexposure * precapture metering sequence.</p> - * <p>The effect of auto-exposure (AE) precapture trigger depends - * on the current AE mode and state; see + * <p>The precapture sequence should triggered before starting a + * high-quality still capture for final metering decisions to + * be made, and for firing pre-capture flash pulses to estimate + * scene brightness and required final capture flash power, when + * the flash is enabled.</p> + * <p>Normally, this entry should be set to START for only a + * single request, and the application should wait until the + * sequence completes before starting a new one.</p> + * <p>The exact effect of auto-exposure (AE) precapture trigger + * depends on the current AE mode and state; see * {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE precapture state transition * details.</p> * @@ -800,7 +808,11 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * autofocus algorithm. If autofocus is disabled, this trigger has no effect.</p> * <p>When set to CANCEL, the camera device will cancel any active trigger, * and return to its initial AF state.</p> - * <p>See {@link CaptureResult#CONTROL_AF_STATE android.control.afState} for what that means for each AF mode.</p> + * <p>Generally, applications should set this entry to START or CANCEL for only a + * single capture, and then return it to IDLE (or not set at all). Specifying + * START for multiple captures in a row means restarting the AF operation over + * and over again.</p> + * <p>See {@link CaptureResult#CONTROL_AF_STATE android.control.afState} for what the trigger means for each AF mode.</p> * * @see CaptureResult#CONTROL_AF_STATE * @see #CONTROL_AF_TRIGGER_IDLE @@ -813,9 +825,11 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> /** * <p>Whether auto-white balance (AWB) is currently locked to its * latest calculated values.</p> - * <p>Note that AWB lock is only meaningful for AUTO - * mode; in other modes, AWB is already fixed to a specific - * setting.</p> + * <p>Note that AWB lock is only meaningful when + * {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} is in the AUTO mode; in other modes, + * AWB is already fixed to a specific setting.</p> + * + * @see CaptureRequest#CONTROL_AWB_MODE */ public static final Key<Boolean> CONTROL_AWB_LOCK = new Key<Boolean>("android.control.awbLock", boolean.class); @@ -825,17 +839,21 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * transform fields, and what its illumination target * is.</p> * <p>This control is only effective if {@link CaptureRequest#CONTROL_MODE android.control.mode} is AUTO.</p> - * <p>When set to the ON mode, the camera device's auto white balance + * <p>When set to the ON mode, the camera device's auto-white balance * routine is enabled, overriding the application's selected * {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}, {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} and * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}.</p> - * <p>When set to the OFF mode, the camera device's auto white balance + * <p>When set to the OFF mode, the camera device's auto-white balance * routine is disabled. The application manually controls the white * balance by {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}, {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} * and {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}.</p> - * <p>When set to any other modes, the camera device's auto white balance - * routine is disabled. The camera device uses each particular illumination - * target for white balance adjustment.</p> + * <p>When set to any other modes, the camera device's auto-white + * balance routine is disabled. The camera device uses each + * particular illumination target for white balance + * adjustment. The application's values for + * {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}, + * {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} and + * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} are ignored.</p> * * @see CaptureRequest#COLOR_CORRECTION_GAINS * @see CaptureRequest#COLOR_CORRECTION_MODE @@ -886,8 +904,8 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * strategy.</p> * <p>This control (except for MANUAL) is only effective if * <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} != OFF</code> and any 3A routine is active.</p> - * <p>ZERO_SHUTTER_LAG must be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} - * contains ZSL. MANUAL must be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} + * <p>ZERO_SHUTTER_LAG will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} + * contains ZSL. MANUAL will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} * contains MANUAL_SENSOR.</p> * * @see CaptureRequest#CONTROL_MODE @@ -962,7 +980,9 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * <p>This is the mode that that is active when * <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} == USE_SCENE_MODE</code>. Aside from FACE_PRIORITY, * these modes will disable {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode}, - * {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, and {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode} while in use.</p> + * {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, and {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode} while in use. + * The scene modes available for a given camera device are listed in + * {@link CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES android.control.availableSceneModes}.</p> * <p>The interpretation and implementation of these scene modes is left * to the implementor of the camera device. Their behavior will not be * consistent across all devices, and any given device may only implement @@ -970,6 +990,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * * @see CaptureRequest#CONTROL_AE_MODE * @see CaptureRequest#CONTROL_AF_MODE + * @see CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES * @see CaptureRequest#CONTROL_AWB_MODE * @see CaptureRequest#CONTROL_MODE * @see #CONTROL_SCENE_MODE_DISABLED @@ -996,6 +1017,8 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> /** * <p>Whether video stabilization is * active.</p> + * <p>Video stabilization automatically translates and scales images from the camera + * in order to stabilize motion between consecutive frames.</p> * <p>If enabled, video stabilization can modify the * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to keep the video stream * stabilized</p> @@ -1110,14 +1133,14 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> /** * <p>Compression quality of the final JPEG * image.</p> - * <p>85-95 is typical usage range</p> + * <p>85-95 is typical usage range.</p> */ public static final Key<Byte> JPEG_QUALITY = new Key<Byte>("android.jpeg.quality", byte.class); /** * <p>Compression quality of JPEG - * thumbnail</p> + * thumbnail.</p> */ public static final Key<Byte> JPEG_THUMBNAIL_QUALITY = new Key<Byte>("android.jpeg.thumbnailQuality", byte.class); @@ -1229,12 +1252,18 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> /** * <p>Sets whether the camera device uses optical image stabilization (OIS) * when capturing images.</p> - * <p>OIS is used to compensate for motion blur due to small movements of - * the camera during capture. Unlike digital image stabilization, OIS makes - * use of mechanical elements to stabilize the camera sensor, and thus - * allows for longer exposure times before camera shake becomes - * apparent.</p> - * <p>This is not expected to be supported on most devices.</p> + * <p>OIS is used to compensate for motion blur due to small + * movements of the camera during capture. Unlike digital image + * stabilization ({@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE android.control.videoStabilizationMode}), OIS + * makes use of mechanical elements to stabilize the camera + * sensor, and thus allows for longer exposure times before + * camera shake becomes apparent.</p> + * <p>Not all devices will support OIS; see + * {@link CameraCharacteristics#LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION android.lens.info.availableOpticalStabilization} for + * available controls.</p> + * + * @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE + * @see CameraCharacteristics#LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION * @see #LENS_OPTICAL_STABILIZATION_MODE_OFF * @see #LENS_OPTICAL_STABILIZATION_MODE_ON */ @@ -1242,16 +1271,15 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> new Key<Integer>("android.lens.opticalStabilizationMode", int.class); /** - * <p>Mode of operation for the noise reduction. - * algorithm</p> + * <p>Mode of operation for the noise reduction algorithm.</p> * <p>Noise filtering control. OFF means no noise reduction * will be applied by the camera device.</p> - * <p>This must be set to a valid mode in + * <p>This must be set to a valid mode from * {@link CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES android.noiseReduction.availableNoiseReductionModes}.</p> * <p>FAST/HIGH_QUALITY both mean camera device determined noise filtering * will be applied. HIGH_QUALITY mode indicates that the camera device * will use the highest-quality noise filtering algorithms, - * even if it slows down capture rate. FAST means the camera device should not + * even if it slows down capture rate. FAST means the camera device will not * slow down capture rate when applying noise filtering.</p> * * @see CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES @@ -1435,7 +1463,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * <p>When enabled, the sensor sends a test pattern instead of * doing a real exposure from the camera.</p> * <p>When a test pattern is enabled, all manual sensor controls specified - * by android.sensor.* should be ignored. All other controls should + * by android.sensor.* will be ignored. All other controls should * work as normal.</p> * <p>For example, if manual flash is enabled, flash firing should still * occur (and that the test pattern remain unmodified, since the flash @@ -1490,7 +1518,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> new Key<Integer>("android.shading.mode", int.class); /** - * <p>State of the face detector + * <p>Control for the face detector * unit.</p> * <p>Whether face detection is enabled, and whether it * should output just the basic fields or the full set of @@ -1508,7 +1536,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> /** * <p>Operating mode for hotpixel map generation.</p> * <p>If set to ON, a hotpixel map is returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}. - * If set to OFF, no hotpixel map should be returned.</p> + * If set to OFF, no hotpixel map will be returned.</p> * <p>This must be set to a valid mode from {@link CameraCharacteristics#STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES android.statistics.info.availableHotPixelMapModes}.</p> * * @see CaptureResult#STATISTICS_HOT_PIXEL_MAP @@ -1521,7 +1549,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * <p>Whether the camera device will output the lens * shading map in output result metadata.</p> * <p>When set to ON, - * android.statistics.lensShadingMap must be provided in + * android.statistics.lensShadingMap will be provided in * the output result metadata.</p> * @see #STATISTICS_LENS_SHADING_MAP_MODE_OFF * @see #STATISTICS_LENS_SHADING_MAP_MODE_ON diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index ce3de1d..3d17ed3 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -579,8 +579,16 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * included at all in the request settings. When included and * set to START, the camera device will trigger the autoexposure * precapture metering sequence.</p> - * <p>The effect of auto-exposure (AE) precapture trigger depends - * on the current AE mode and state; see + * <p>The precapture sequence should triggered before starting a + * high-quality still capture for final metering decisions to + * be made, and for firing pre-capture flash pulses to estimate + * scene brightness and required final capture flash power, when + * the flash is enabled.</p> + * <p>Normally, this entry should be set to START for only a + * single request, and the application should wait until the + * sequence completes before starting a new one.</p> + * <p>The exact effect of auto-exposure (AE) precapture trigger + * depends on the current AE mode and state; see * {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE precapture state transition * details.</p> * @@ -592,7 +600,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { new Key<Integer>("android.control.aePrecaptureTrigger", int.class); /** - * <p>Current state of auto-exposure (AE) algorithm.</p> + * <p>Current state of the auto-exposure (AE) algorithm.</p> * <p>Switching between or enabling AE modes ({@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode}) always * resets the AE state to INACTIVE. Similarly, switching between {@link CaptureRequest#CONTROL_MODE android.control.mode}, * or {@link CaptureRequest#CONTROL_SCENE_MODE android.control.sceneMode} if <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} == USE_SCENE_MODE</code> resets all @@ -844,7 +852,11 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * autofocus algorithm. If autofocus is disabled, this trigger has no effect.</p> * <p>When set to CANCEL, the camera device will cancel any active trigger, * and return to its initial AF state.</p> - * <p>See {@link CaptureResult#CONTROL_AF_STATE android.control.afState} for what that means for each AF mode.</p> + * <p>Generally, applications should set this entry to START or CANCEL for only a + * single capture, and then return it to IDLE (or not set at all). Specifying + * START for multiple captures in a row means restarting the AF operation over + * and over again.</p> + * <p>See {@link CaptureResult#CONTROL_AF_STATE android.control.afState} for what the trigger means for each AF mode.</p> * * @see CaptureResult#CONTROL_AF_STATE * @see #CONTROL_AF_TRIGGER_IDLE @@ -1034,13 +1046,13 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <td align="center">PASSIVE_SCAN</td> * <td align="center">AF_TRIGGER</td> * <td align="center">FOCUSED_LOCKED</td> - * <td align="center">Immediate trans. If focus is good, Lens now locked</td> + * <td align="center">Immediate transition, if focus is good. Lens now locked</td> * </tr> * <tr> * <td align="center">PASSIVE_SCAN</td> * <td align="center">AF_TRIGGER</td> * <td align="center">NOT_FOCUSED_LOCKED</td> - * <td align="center">Immediate trans. if focus is bad, Lens now locked</td> + * <td align="center">Immediate transition, if focus is bad. Lens now locked</td> * </tr> * <tr> * <td align="center">PASSIVE_SCAN</td> @@ -1064,13 +1076,13 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <td align="center">PASSIVE_FOCUSED</td> * <td align="center">AF_TRIGGER</td> * <td align="center">FOCUSED_LOCKED</td> - * <td align="center">Immediate trans. Lens now locked</td> + * <td align="center">Immediate transition, lens now locked</td> * </tr> * <tr> * <td align="center">PASSIVE_UNFOCUSED</td> * <td align="center">AF_TRIGGER</td> * <td align="center">NOT_FOCUSED_LOCKED</td> - * <td align="center">Immediate trans. Lens now locked</td> + * <td align="center">Immediate transition, lens now locked</td> * </tr> * <tr> * <td align="center">FOCUSED_LOCKED</td> @@ -1137,13 +1149,13 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <td align="center">PASSIVE_SCAN</td> * <td align="center">AF_TRIGGER</td> * <td align="center">FOCUSED_LOCKED</td> - * <td align="center">Eventual trans. once focus good, Lens now locked</td> + * <td align="center">Eventual transition once the focus is good. Lens now locked</td> * </tr> * <tr> * <td align="center">PASSIVE_SCAN</td> * <td align="center">AF_TRIGGER</td> * <td align="center">NOT_FOCUSED_LOCKED</td> - * <td align="center">Eventual trans. if cannot focus, Lens now locked</td> + * <td align="center">Eventual transition if cannot find focus. Lens now locked</td> * </tr> * <tr> * <td align="center">PASSIVE_SCAN</td> @@ -1254,9 +1266,11 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>Whether auto-white balance (AWB) is currently locked to its * latest calculated values.</p> - * <p>Note that AWB lock is only meaningful for AUTO - * mode; in other modes, AWB is already fixed to a specific - * setting.</p> + * <p>Note that AWB lock is only meaningful when + * {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode} is in the AUTO mode; in other modes, + * AWB is already fixed to a specific setting.</p> + * + * @see CaptureRequest#CONTROL_AWB_MODE */ public static final Key<Boolean> CONTROL_AWB_LOCK = new Key<Boolean>("android.control.awbLock", boolean.class); @@ -1266,17 +1280,21 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * transform fields, and what its illumination target * is.</p> * <p>This control is only effective if {@link CaptureRequest#CONTROL_MODE android.control.mode} is AUTO.</p> - * <p>When set to the ON mode, the camera device's auto white balance + * <p>When set to the ON mode, the camera device's auto-white balance * routine is enabled, overriding the application's selected * {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}, {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} and * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}.</p> - * <p>When set to the OFF mode, the camera device's auto white balance + * <p>When set to the OFF mode, the camera device's auto-white balance * routine is disabled. The application manually controls the white * balance by {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}, {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} * and {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}.</p> - * <p>When set to any other modes, the camera device's auto white balance - * routine is disabled. The camera device uses each particular illumination - * target for white balance adjustment.</p> + * <p>When set to any other modes, the camera device's auto-white + * balance routine is disabled. The camera device uses each + * particular illumination target for white balance + * adjustment. The application's values for + * {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}, + * {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} and + * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} are ignored.</p> * * @see CaptureRequest#COLOR_CORRECTION_GAINS * @see CaptureRequest#COLOR_CORRECTION_MODE @@ -1327,8 +1345,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * strategy.</p> * <p>This control (except for MANUAL) is only effective if * <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} != OFF</code> and any 3A routine is active.</p> - * <p>ZERO_SHUTTER_LAG must be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} - * contains ZSL. MANUAL must be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} + * <p>ZERO_SHUTTER_LAG will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} + * contains ZSL. MANUAL will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} * contains MANUAL_SENSOR.</p> * * @see CaptureRequest#CONTROL_MODE @@ -1533,7 +1551,9 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p>This is the mode that that is active when * <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} == USE_SCENE_MODE</code>. Aside from FACE_PRIORITY, * these modes will disable {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode}, - * {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, and {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode} while in use.</p> + * {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, and {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode} while in use. + * The scene modes available for a given camera device are listed in + * {@link CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES android.control.availableSceneModes}.</p> * <p>The interpretation and implementation of these scene modes is left * to the implementor of the camera device. Their behavior will not be * consistent across all devices, and any given device may only implement @@ -1541,6 +1561,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * * @see CaptureRequest#CONTROL_AE_MODE * @see CaptureRequest#CONTROL_AF_MODE + * @see CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES * @see CaptureRequest#CONTROL_AWB_MODE * @see CaptureRequest#CONTROL_MODE * @see #CONTROL_SCENE_MODE_DISABLED @@ -1567,6 +1588,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>Whether video stabilization is * active.</p> + * <p>Video stabilization automatically translates and scales images from the camera + * in order to stabilize motion between consecutive frames.</p> * <p>If enabled, video stabilization can modify the * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to keep the video stream * stabilized</p> @@ -1698,14 +1721,14 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>Compression quality of the final JPEG * image.</p> - * <p>85-95 is typical usage range</p> + * <p>85-95 is typical usage range.</p> */ public static final Key<Byte> JPEG_QUALITY = new Key<Byte>("android.jpeg.quality", byte.class); /** * <p>Compression quality of JPEG - * thumbnail</p> + * thumbnail.</p> */ public static final Key<Byte> JPEG_THUMBNAIL_QUALITY = new Key<Byte>("android.jpeg.thumbnailQuality", byte.class); @@ -1817,12 +1840,18 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>Sets whether the camera device uses optical image stabilization (OIS) * when capturing images.</p> - * <p>OIS is used to compensate for motion blur due to small movements of - * the camera during capture. Unlike digital image stabilization, OIS makes - * use of mechanical elements to stabilize the camera sensor, and thus - * allows for longer exposure times before camera shake becomes - * apparent.</p> - * <p>This is not expected to be supported on most devices.</p> + * <p>OIS is used to compensate for motion blur due to small + * movements of the camera during capture. Unlike digital image + * stabilization ({@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE android.control.videoStabilizationMode}), OIS + * makes use of mechanical elements to stabilize the camera + * sensor, and thus allows for longer exposure times before + * camera shake becomes apparent.</p> + * <p>Not all devices will support OIS; see + * {@link CameraCharacteristics#LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION android.lens.info.availableOpticalStabilization} for + * available controls.</p> + * + * @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE + * @see CameraCharacteristics#LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION * @see #LENS_OPTICAL_STABILIZATION_MODE_OFF * @see #LENS_OPTICAL_STABILIZATION_MODE_ON */ @@ -1866,16 +1895,15 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { new Key<Integer>("android.lens.state", int.class); /** - * <p>Mode of operation for the noise reduction. - * algorithm</p> + * <p>Mode of operation for the noise reduction algorithm.</p> * <p>Noise filtering control. OFF means no noise reduction * will be applied by the camera device.</p> - * <p>This must be set to a valid mode in + * <p>This must be set to a valid mode from * {@link CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES android.noiseReduction.availableNoiseReductionModes}.</p> * <p>FAST/HIGH_QUALITY both mean camera device determined noise filtering * will be applied. HIGH_QUALITY mode indicates that the camera device * will use the highest-quality noise filtering algorithms, - * even if it slows down capture rate. FAST means the camera device should not + * even if it slows down capture rate. FAST means the camera device will not * slow down capture rate when applying noise filtering.</p> * * @see CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES @@ -2170,7 +2198,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p>When enabled, the sensor sends a test pattern instead of * doing a real exposure from the camera.</p> * <p>When a test pattern is enabled, all manual sensor controls specified - * by android.sensor.* should be ignored. All other controls should + * by android.sensor.* will be ignored. All other controls should * work as normal.</p> * <p>For example, if manual flash is enabled, flash firing should still * occur (and that the test pattern remain unmodified, since the flash @@ -2225,7 +2253,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { new Key<Integer>("android.shading.mode", int.class); /** - * <p>State of the face detector + * <p>Control for the face detector * unit.</p> * <p>Whether face detection is enabled, and whether it * should output just the basic fields or the full set of @@ -2241,9 +2269,13 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { new Key<Integer>("android.statistics.faceDetectMode", int.class); /** - * <p>List of unique IDs for detected - * faces</p> - * <p>Only available if faceDetectMode == FULL</p> + * <p>List of unique IDs for detected faces.</p> + * <p>Each detected face is given a unique ID that is valid for as long as the face is visible + * to the camera device. A face that leaves the field of view and later returns may be + * assigned a new ID.</p> + * <p>Only available if {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} == FULL</p> + * + * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE * @hide */ public static final Key<int[]> STATISTICS_FACE_IDS = @@ -2251,8 +2283,13 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>List of landmarks for detected - * faces</p> - * <p>Only available if faceDetectMode == FULL</p> + * faces.</p> + * <p>The coordinate system is that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with + * <code>(0, 0)</code> being the top-left pixel of the active array.</p> + * <p>Only available if {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} == FULL</p> + * + * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE + * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE * @hide */ public static final Key<int[]> STATISTICS_FACE_LANDMARKS = @@ -2260,8 +2297,13 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>List of the bounding rectangles for detected - * faces</p> - * <p>Only available if faceDetectMode != OFF</p> + * faces.</p> + * <p>The coordinate system is that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with + * <code>(0, 0)</code> being the top-left pixel of the active array.</p> + * <p>Only available if {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} != OFF</p> + * + * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE + * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE * @hide */ public static final Key<android.graphics.Rect[]> STATISTICS_FACE_RECTANGLES = @@ -2270,8 +2312,9 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>List of the face confidence scores for * detected faces</p> - * <p>Only available if faceDetectMode != OFF. The value should be - * meaningful (for example, setting 100 at all times is illegal).</p> + * <p>Only available if {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} != OFF.</p> + * + * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE * @hide */ public static final Key<byte[]> STATISTICS_FACE_SCORES = @@ -2435,12 +2478,13 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * The camera device uses this entry to tell the application what the scene * illuminant frequency is.</p> * <p>When manual exposure control is enabled - * (<code>{@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} == OFF</code> or <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} == OFF</code>), - * the {@link CaptureRequest#CONTROL_AE_ANTIBANDING_MODE android.control.aeAntibandingMode} doesn't do the antibanding, and the - * application can ensure it selects exposure times that do not cause banding - * issues by looking into this metadata field. See {@link CaptureRequest#CONTROL_AE_ANTIBANDING_MODE android.control.aeAntibandingMode} - * for more details.</p> - * <p>Report NONE if there doesn't appear to be flickering illumination.</p> + * (<code>{@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} == OFF</code> or <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} == + * OFF</code>), the {@link CaptureRequest#CONTROL_AE_ANTIBANDING_MODE android.control.aeAntibandingMode} doesn't perform + * antibanding, and the application can ensure it selects + * exposure times that do not cause banding issues by looking + * into this metadata field. See + * {@link CaptureRequest#CONTROL_AE_ANTIBANDING_MODE android.control.aeAntibandingMode} for more details.</p> + * <p>Reports NONE if there doesn't appear to be flickering illumination.</p> * * @see CaptureRequest#CONTROL_AE_ANTIBANDING_MODE * @see CaptureRequest#CONTROL_AE_MODE @@ -2455,7 +2499,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { /** * <p>Operating mode for hotpixel map generation.</p> * <p>If set to ON, a hotpixel map is returned in {@link CaptureResult#STATISTICS_HOT_PIXEL_MAP android.statistics.hotPixelMap}. - * If set to OFF, no hotpixel map should be returned.</p> + * If set to OFF, no hotpixel map will be returned.</p> * <p>This must be set to a valid mode from {@link CameraCharacteristics#STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES android.statistics.info.availableHotPixelMapModes}.</p> * * @see CaptureResult#STATISTICS_HOT_PIXEL_MAP @@ -2483,7 +2527,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p>Whether the camera device will output the lens * shading map in output result metadata.</p> * <p>When set to ON, - * android.statistics.lensShadingMap must be provided in + * android.statistics.lensShadingMap will be provided in * the output result metadata.</p> * @see #STATISTICS_LENS_SHADING_MAP_MODE_OFF * @see #STATISTICS_LENS_SHADING_MAP_MODE_ON diff --git a/core/java/android/hardware/camera2/params/StreamConfiguration.java b/core/java/android/hardware/camera2/params/StreamConfiguration.java index dd862b5..a6fc10f 100644 --- a/core/java/android/hardware/camera2/params/StreamConfiguration.java +++ b/core/java/android/hardware/camera2/params/StreamConfiguration.java @@ -30,7 +30,8 @@ import android.util.Size; * Immutable class to store the available stream * {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS configurations} to set up * {@link android.view.Surface Surfaces} for creating a {@link CameraCaptureSession capture session} - * with {@link CameraDevice#createCaptureSession}. <!-- TODO: link to input stream configuration --> + * with {@link CameraDevice#createCaptureSession}. + * <!-- TODO: link to input stream configuration --> * * <p>This is the authoritative list for all input/output formats (and sizes respectively * for that format) that are supported by a camera device.</p> diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java index af27e1d..8b42bcd 100644 --- a/core/java/android/net/RouteInfo.java +++ b/core/java/android/net/RouteInfo.java @@ -361,7 +361,7 @@ public class RouteInfo implements Parcelable { RouteInfo target = (RouteInfo) obj; - return Objects.equals(mDestination, target.getDestination()) && + return Objects.equals(mDestination, target.getDestinationLinkAddress()) && Objects.equals(mGateway, target.getGateway()) && Objects.equals(mInterface, target.getInterface()); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index ae536c1..1001677 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6035,6 +6035,7 @@ public final class Settings { /** * Battery level [1-99] at which low power mode automatically turns on. + * If 0, it will not automatically turn on. * @hide */ public static final String LOW_POWER_MODE_TRIGGER_LEVEL = "low_power_trigger_level"; diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index fd475cd..8bd0f4d 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -29,6 +29,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; +import android.util.ArrayMap; import android.util.Log; import java.util.List; @@ -54,7 +55,7 @@ public abstract class NotificationListenerService extends Service { + "[" + getClass().getSimpleName() + "]"; private INotificationListenerWrapper mWrapper = null; - private Ranking mRanking; + private RankingMap mRankingMap; private INotificationManager mNoMan; @@ -75,7 +76,43 @@ public abstract class NotificationListenerService extends Service { * object as well as its identifying information (tag and id) and source * (package name). */ - public abstract void onNotificationPosted(StatusBarNotification sbn); + public void onNotificationPosted(StatusBarNotification sbn) { + // optional + } + + /** + * Implement this method to learn about new notifications as they are posted by apps. + * + * @param sbn A data structure encapsulating the original {@link android.app.Notification} + * object as well as its identifying information (tag and id) and source + * (package name). + * @param rankingMap The current ranking map that can be used to retrieve ranking information + * for active notifications, including the newly posted one. + */ + public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) { + onNotificationPosted(sbn); + } + + /** + * Implement this method to learn when notifications are removed. + * <P> + * This might occur because the user has dismissed the notification using system UI (or another + * notification listener) or because the app has withdrawn the notification. + * <P> + * NOTE: The {@link StatusBarNotification} object you receive will be "light"; that is, the + * result from {@link StatusBarNotification#getNotification} may be missing some heavyweight + * fields such as {@link android.app.Notification#contentView} and + * {@link android.app.Notification#largeIcon}. However, all other fields on + * {@link StatusBarNotification}, sufficient to match this call with a prior call to + * {@link #onNotificationPosted(StatusBarNotification)}, will be intact. + * + * @param sbn A data structure encapsulating at least the original information (tag and id) + * and source (package name) used to post the {@link android.app.Notification} that + * was just removed. + */ + public void onNotificationRemoved(StatusBarNotification sbn) { + // optional + } /** * Implement this method to learn when notifications are removed. @@ -93,8 +130,13 @@ public abstract class NotificationListenerService extends Service { * @param sbn A data structure encapsulating at least the original information (tag and id) * and source (package name) used to post the {@link android.app.Notification} that * was just removed. + * @param rankingMap The current ranking map that can be used to retrieve ranking information + * for active notifications. + * */ - public abstract void onNotificationRemoved(StatusBarNotification sbn); + public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) { + onNotificationRemoved(sbn); + } /** * Implement this method to learn about when the listener is enabled and connected to @@ -107,10 +149,11 @@ public abstract class NotificationListenerService extends Service { /** * Implement this method to be notified when the notification ranking changes. - * <P> - * Call {@link #getCurrentRanking()} to retrieve the new ranking. + * + * @param rankingMap The current ranking map that can be used to retrieve ranking information + * for active notifications. */ - public void onNotificationRankingUpdate() { + public void onNotificationRankingUpdate(RankingMap rankingMap) { // optional } @@ -241,16 +284,19 @@ public abstract class NotificationListenerService extends Service { * * <p> * The returned object represents the current ranking snapshot and only - * applies for currently active notifications. Hence you must retrieve a - * new Ranking after each notification event such as - * {@link #onNotificationPosted(StatusBarNotification)}, - * {@link #onNotificationRemoved(StatusBarNotification)}, etc. + * applies for currently active notifications. + * <p> + * Generally you should use the RankingMap that is passed with events such + * as {@link #onNotificationPosted(StatusBarNotification, RankingMap)}, + * {@link #onNotificationRemoved(StatusBarNotification, RankingMap)}, and + * so on. This method should only be used when needing access outside of + * such events, for example to retrieve the RankingMap right after + * initialization. * - * @return A {@link NotificationListenerService.Ranking} object providing - * access to ranking information + * @return A {@link RankingMap} object providing access to ranking information */ - public Ranking getCurrentRanking() { - return mRanking; + public RankingMap getCurrentRanking() { + return mRankingMap; } @Override @@ -313,7 +359,7 @@ public abstract class NotificationListenerService extends Service { synchronized (mWrapper) { applyUpdate(update); try { - NotificationListenerService.this.onNotificationPosted(sbn); + NotificationListenerService.this.onNotificationPosted(sbn, mRankingMap); } catch (Throwable t) { Log.w(TAG, "Error running onNotificationPosted", t); } @@ -326,7 +372,7 @@ public abstract class NotificationListenerService extends Service { synchronized (mWrapper) { applyUpdate(update); try { - NotificationListenerService.this.onNotificationRemoved(sbn); + NotificationListenerService.this.onNotificationRemoved(sbn, mRankingMap); } catch (Throwable t) { Log.w(TAG, "Error running onNotificationRemoved", t); } @@ -351,7 +397,7 @@ public abstract class NotificationListenerService extends Service { synchronized (mWrapper) { applyUpdate(update); try { - NotificationListenerService.this.onNotificationRankingUpdate(); + NotificationListenerService.this.onNotificationRankingUpdate(mRankingMap); } catch (Throwable t) { Log.w(TAG, "Error running onNotificationRankingUpdate", t); } @@ -360,7 +406,65 @@ public abstract class NotificationListenerService extends Service { } private void applyUpdate(NotificationRankingUpdate update) { - mRanking = new Ranking(update); + mRankingMap = new RankingMap(update); + } + + /** + * Provides access to ranking information on a currently active + * notification. + * + * <p> + * Note that this object is not updated on notification events (such as + * {@link #onNotificationPosted(StatusBarNotification, RankingMap)}, + * {@link #onNotificationRemoved(StatusBarNotification)}, etc.). Make sure + * to retrieve a new Ranking from the current {@link RankingMap} whenever + * a notification event occurs. + */ + public static class Ranking { + private final String mKey; + private final int mRank; + private final boolean mIsAmbient; + private final boolean mIsInterceptedByDnd; + + private Ranking(String key, int rank, boolean isAmbient, boolean isInterceptedByDnd) { + mKey = key; + mRank = rank; + mIsAmbient = isAmbient; + mIsInterceptedByDnd = isInterceptedByDnd; + } + + /** + * Returns the key of the notification this Ranking applies to. + */ + public String getKey() { + return mKey; + } + + /** + * Returns the rank of the notification. + * + * @return the rank of the notification, that is the 0-based index in + * the list of active notifications. + */ + public int getRank() { + return mRank; + } + + /** + * Returns whether the notification is an ambient notification, that is + * a notification that doesn't require the user's immediate attention. + */ + public boolean isAmbient() { + return mIsAmbient; + } + + /** + * Returns whether the notification was intercepted by + * "Do not disturb". + */ + public boolean isInterceptedByDoNotDisturb() { + return mIsInterceptedByDnd; + } } /** @@ -371,11 +475,14 @@ public abstract class NotificationListenerService extends Service { * Note that this object represents a ranking snapshot that only applies to * notifications active at the time of retrieval. */ - public static class Ranking implements Parcelable { + public static class RankingMap implements Parcelable { private final NotificationRankingUpdate mRankingUpdate; + private final ArrayMap<String, Ranking> mRankingCache; + private boolean mRankingCacheInitialized; - private Ranking(NotificationRankingUpdate rankingUpdate) { + private RankingMap(NotificationRankingUpdate rankingUpdate) { mRankingUpdate = rankingUpdate; + mRankingCache = new ArrayMap<>(rankingUpdate.getOrderedKeys().length); } /** @@ -389,56 +496,37 @@ public abstract class NotificationListenerService extends Service { } /** - * Returns the rank of the notification with the given key, that is the - * index of <code>key</code> in the array of keys returned by - * {@link #getOrderedKeys()}. + * Returns the Ranking for the notification with the given key. * - * @return The rank of the notification with the given key; -1 when the - * given key is unknown. + * @return the Ranking of the notification with the given key; + * <code>null</code> when the key is unknown. */ - public int getRank(String key) { - // TODO: Optimize. - String[] orderedKeys = mRankingUpdate.getOrderedKeys(); - for (int i = 0; i < orderedKeys.length; i++) { - if (orderedKeys[i].equals(key)) { - return i; + public Ranking getRanking(String key) { + synchronized (mRankingCache) { + if (!mRankingCacheInitialized) { + initializeRankingCache(); + mRankingCacheInitialized = true; } } - return -1; + return mRankingCache.get(key); } - /** - * Returns whether the notification with the given key was intercepted - * by "Do not disturb". - */ - public boolean isInterceptedByDoNotDisturb(String key) { - // TODO: Optimize. - for (String interceptedKey : mRankingUpdate.getDndInterceptedKeys()) { - if (interceptedKey.equals(key)) { - return true; - } - } - return false; - } - - /** - * Returns whether the notification with the given key is an ambient - * notification, that is a notification that doesn't require the user's - * immediate attention. - */ - public boolean isAmbient(String key) { - // TODO: Optimize. - int firstAmbientIndex = mRankingUpdate.getFirstAmbientIndex(); - if (firstAmbientIndex < 0) { - return false; - } + private void initializeRankingCache() { String[] orderedKeys = mRankingUpdate.getOrderedKeys(); - for (int i = firstAmbientIndex; i < orderedKeys.length; i++) { - if (orderedKeys[i].equals(key)) { - return true; + int firstAmbientIndex = mRankingUpdate.getFirstAmbientIndex(); + for (int i = 0; i < orderedKeys.length; i++) { + String key = orderedKeys[i]; + boolean isAmbient = firstAmbientIndex > -1 && firstAmbientIndex <= i; + boolean isInterceptedByDnd = false; + // TODO: Optimize. + for (String s : mRankingUpdate.getDndInterceptedKeys()) { + if (s.equals(key)) { + isInterceptedByDnd = true; + break; + } } + mRankingCache.put(key, new Ranking(key, i, isAmbient, isInterceptedByDnd)); } - return false; } // ----------- Parcelable @@ -453,16 +541,16 @@ public abstract class NotificationListenerService extends Service { dest.writeParcelable(mRankingUpdate, flags); } - public static final Creator<Ranking> CREATOR = new Creator<Ranking>() { + public static final Creator<RankingMap> CREATOR = new Creator<RankingMap>() { @Override - public Ranking createFromParcel(Parcel source) { + public RankingMap createFromParcel(Parcel source) { NotificationRankingUpdate rankingUpdate = source.readParcelable(null); - return new Ranking(rankingUpdate); + return new RankingMap(rankingUpdate); } @Override - public Ranking[] newArray(int size) { - return new Ranking[size]; + public RankingMap[] newArray(int size) { + return new RankingMap[size]; } }; } diff --git a/core/java/android/service/voice/DspInfo.java b/core/java/android/service/voice/DspInfo.java new file mode 100644 index 0000000..0862309 --- /dev/null +++ b/core/java/android/service/voice/DspInfo.java @@ -0,0 +1,56 @@ +/* + * 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.service.voice; + +import java.util.UUID; + +/** + * Properties of the DSP hardware on the device. + * @hide + */ +public class DspInfo { + /** + * Unique voice engine Id (changes with each version). + */ + public final UUID voiceEngineId; + + /** + * Human readable voice detection engine implementor. + */ + public final String voiceEngineImplementor; + /** + * Human readable voice detection engine description. + */ + public final String voiceEngineDescription; + /** + * Human readable voice detection engine version + */ + public final int voiceEngineVersion; + /** + * Rated power consumption when detection is active. + */ + public final int powerConsumptionMw; + + public DspInfo(UUID voiceEngineId, String voiceEngineImplementor, + String voiceEngineDescription, int version, int powerConsumptionMw) { + this.voiceEngineId = voiceEngineId; + this.voiceEngineImplementor = voiceEngineImplementor; + this.voiceEngineDescription = voiceEngineDescription; + this.voiceEngineVersion = version; + this.powerConsumptionMw = powerConsumptionMw; + } +} diff --git a/core/java/android/service/voice/KeyphraseEnrollmentInfo.java b/core/java/android/service/voice/KeyphraseEnrollmentInfo.java new file mode 100644 index 0000000..ebe41ce --- /dev/null +++ b/core/java/android/service/voice/KeyphraseEnrollmentInfo.java @@ -0,0 +1,246 @@ +/* + * 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.service.voice; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.util.AttributeSet; +import android.util.Slog; +import android.util.Xml; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.List; + +/** @hide */ +public class KeyphraseEnrollmentInfo { + private static final String TAG = "KeyphraseEnrollmentInfo"; + /** + * Name under which a Hotword enrollment component publishes information about itself. + * This meta-data should reference an XML resource containing a + * <code><{@link + * android.R.styleable#VoiceEnrollmentApplication + * voice-enrollment-application}></code> tag. + */ + private static final String VOICE_KEYPHRASE_META_DATA = "android.voice_enrollment"; + /** + * Activity Action: Show activity for managing the keyphrases for hotword detection. + * This needs to be defined by an activity that supports enrolling users for hotword/keyphrase + * detection. + */ + public static final String ACTION_MANAGE_VOICE_KEYPHRASES = + "com.android.intent.action.MANAGE_VOICE_KEYPHRASES"; + /** + * Intent extra: The intent extra for un-enrolling a user for a particular keyphrase. + */ + public static final String EXTRA_VOICE_KEYPHRASE_UNENROLL = + "com.android.intent.extra.VOICE_KEYPHRASE_UNENROLL"; + /** + * Intent extra: The hint text to be shown on the voice keyphrase management UI. + */ + public static final String EXTRA_VOICE_KEYPHRASE_HINT_TEXT = + "com.android.intent.extra.VOICE_KEYPHRASE_HINT_TEXT"; + /** + * Intent extra: The voice locale to use while managing the keyphrase. + */ + public static final String EXTRA_VOICE_KEYPHRASE_LOCALE = + "com.android.intent.extra.VOICE_KEYPHRASE_LOCALE"; + + private KeyphraseInfo[] mKeyphrases; + private String mEnrollmentPackage; + private String mParseError; + + public KeyphraseEnrollmentInfo(PackageManager pm) { + // Find the apps that supports enrollment for hotword keyhphrases, + // Pick a privileged app and obtain the information about the supported keyphrases + // from its metadata. + List<ResolveInfo> ris = pm.queryIntentActivities( + new Intent(ACTION_MANAGE_VOICE_KEYPHRASES), PackageManager.MATCH_DEFAULT_ONLY); + if (ris == null || ris.isEmpty()) { + // No application capable of enrolling for voice keyphrases is present. + mParseError = "No enrollment application found"; + return; + } + + boolean found = false; + ApplicationInfo ai = null; + for (ResolveInfo ri : ris) { + try { + ai = pm.getApplicationInfo( + ri.activityInfo.packageName, PackageManager.GET_META_DATA); + if ((ai.flags & ApplicationInfo.FLAG_PRIVILEGED) == 0) { + // The application isn't privileged (/system/priv-app). + // The enrollment application needs to be a privileged system app. + Slog.w(TAG, ai.packageName + "is not a privileged system app"); + continue; + } + if (!Manifest.permission.MANAGE_VOICE_KEYPHRASES.equals(ai.permission)) { + // The application trying to manage keyphrases doesn't + // require the MANAGE_VOICE_KEYPHRASES permission. + Slog.w(TAG, ai.packageName + " does not require MANAGE_VOICE_KEYPHRASES"); + continue; + } + mEnrollmentPackage = ai.packageName; + found = true; + break; + } catch (PackageManager.NameNotFoundException e) { + Slog.w(TAG, "error parsing voice enrollment meta-data", e); + } + } + + if (!found) { + mKeyphrases = null; + mParseError = "No suitable enrollment application found"; + return; + } + + XmlResourceParser parser = null; + try { + parser = ai.loadXmlMetaData(pm, VOICE_KEYPHRASE_META_DATA); + if (parser == null) { + mParseError = "No " + VOICE_KEYPHRASE_META_DATA + " meta-data for " + + ai.packageName; + return; + } + + Resources res = pm.getResourcesForApplication(ai); + AttributeSet attrs = Xml.asAttributeSet(parser); + + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && type != XmlPullParser.START_TAG) { + } + + String nodeName = parser.getName(); + if (!"voice-enrollment-application".equals(nodeName)) { + mParseError = "Meta-data does not start with voice-enrollment-application tag"; + return; + } + + TypedArray array = res.obtainAttributes(attrs, + com.android.internal.R.styleable.VoiceEnrollmentApplication); + int searchKeyphraseId = array.getInt( + com.android.internal.R.styleable.VoiceEnrollmentApplication_searchKeyphraseId, + -1); + if (searchKeyphraseId != -1) { + String searchKeyphrase = array.getString(com.android.internal.R.styleable + .VoiceEnrollmentApplication_searchKeyphrase); + String searchKeyphraseSupportedLocales = + array.getString(com.android.internal.R.styleable + .VoiceEnrollmentApplication_searchKeyphraseSupportedLocales); + String[] supportedLocales = new String[0]; + // Get all the supported locales from the comma-delimted string. + if (searchKeyphraseSupportedLocales != null + && !searchKeyphraseSupportedLocales.isEmpty()) { + supportedLocales = searchKeyphraseSupportedLocales.split(","); + } + mKeyphrases = new KeyphraseInfo[1]; + mKeyphrases[0] = new KeyphraseInfo( + searchKeyphraseId, searchKeyphrase, supportedLocales); + } else { + mParseError = "searchKeyphraseId not specified in meta-data"; + return; + } + } catch (XmlPullParserException e) { + mParseError = "Error parsing keyphrase enrollment meta-data: " + e; + Slog.w(TAG, "error parsing keyphrase enrollment meta-data", e); + return; + } catch (IOException e) { + mParseError = "Error parsing keyphrase enrollment meta-data: " + e; + Slog.w(TAG, "error parsing keyphrase enrollment meta-data", e); + return; + } catch (PackageManager.NameNotFoundException e) { + mParseError = "Error parsing keyphrase enrollment meta-data: " + e; + Slog.w(TAG, "error parsing keyphrase enrollment meta-data", e); + return; + } finally { + if (parser != null) parser.close(); + } + } + + public String getParseError() { + return mParseError; + } + + /** + * @return An array of available keyphrases that can be enrolled on the system. + * It may be null if no keyphrases can be enrolled. + */ + public KeyphraseInfo[] getKeyphrases() { + return mKeyphrases; + } + + /** + * Returns an intent to launch an activity that manages the given keyphrase + * for the locale. + * + * @param enroll Indicates if the intent should enroll the user or un-enroll them. + * @param keyphrase The keyphrase that the user needs to be enrolled to. + * @param locale The locale for which the enrollment needs to be performed. + * @return An {@link Intent} to manage the keyphrase. This can be null if managing the + * given keyphrase/locale combination isn't possible. + */ + public Intent getManageKeyphraseIntent(boolean enroll, String keyphrase, String locale) { + if (mEnrollmentPackage == null || mEnrollmentPackage.isEmpty()) { + Slog.w(TAG, "No enrollment application exists"); + return null; + } + + if (isKeyphraseEnrollmentSupported(keyphrase, locale)) { + Intent intent = new Intent(ACTION_MANAGE_VOICE_KEYPHRASES) + .setPackage(mEnrollmentPackage) + .putExtra(EXTRA_VOICE_KEYPHRASE_HINT_TEXT, keyphrase) + .putExtra(EXTRA_VOICE_KEYPHRASE_LOCALE, locale); + if (!enroll) intent.putExtra(EXTRA_VOICE_KEYPHRASE_UNENROLL, true); + return intent; + } + return null; + } + + /** + * Indicates if enrollment is supported for the given keyphrase & locale. + * + * @param keyphrase The keyphrase that the user needs to be enrolled to. + * @param locale The locale for which the enrollment needs to be performed. + * @return true, if an enrollment client supports the given keyphrase and the given locale. + */ + public boolean isKeyphraseEnrollmentSupported(String keyphrase, String locale) { + if (mKeyphrases == null || mKeyphrases.length == 0) { + Slog.w(TAG, "Enrollment application doesn't support keyphrases"); + return false; + } + for (KeyphraseInfo keyphraseInfo : mKeyphrases) { + // Check if the given keyphrase is supported in the locale provided by + // the enrollment application. + String supportedKeyphrase = keyphraseInfo.keyphrase; + if (supportedKeyphrase.equalsIgnoreCase(keyphrase) + && keyphraseInfo.supportedLocales.contains(locale)) { + return true; + } + } + Slog.w(TAG, "Enrollment application doesn't support the given keyphrase"); + return false; + } +} diff --git a/core/java/android/service/voice/KeyphraseInfo.java b/core/java/android/service/voice/KeyphraseInfo.java new file mode 100644 index 0000000..d266e1a --- /dev/null +++ b/core/java/android/service/voice/KeyphraseInfo.java @@ -0,0 +1,27 @@ +package android.service.voice; + +import android.util.ArraySet; + +/** + * A Voice Keyphrase. + * @hide + */ +public class KeyphraseInfo { + public final int id; + public final String keyphrase; + public final ArraySet<String> supportedLocales; + + public KeyphraseInfo(int id, String keyphrase, String[] supportedLocales) { + this.id = id; + this.keyphrase = keyphrase; + this.supportedLocales = new ArraySet<String>(supportedLocales.length); + for (String locale : supportedLocales) { + this.supportedLocales.add(locale); + } + } + + @Override + public String toString() { + return "id=" + id + ", keyphrase=" + keyphrase + ", supported-locales=" + supportedLocales; + } +} diff --git a/core/java/android/service/voice/SoundTriggerManager.java b/core/java/android/service/voice/SoundTriggerManager.java new file mode 100644 index 0000000..2d049b9 --- /dev/null +++ b/core/java/android/service/voice/SoundTriggerManager.java @@ -0,0 +1,73 @@ +/* + * 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.service.voice; + +import android.hardware.soundtrigger.SoundTrigger; +import android.hardware.soundtrigger.SoundTrigger.ModuleProperties; + +import java.util.ArrayList; + +/** + * Manager for {@link SoundTrigger} APIs. + * Currently this just acts as an abstraction over all SoundTrigger API calls. + * @hide + */ +public class SoundTriggerManager { + /** The {@link DspInfo} for the system, or null if none exists. */ + public DspInfo dspInfo; + + public SoundTriggerManager() { + ArrayList <ModuleProperties> modules = new ArrayList<>(); + int status = SoundTrigger.listModules(modules); + if (status != SoundTrigger.STATUS_OK || modules.size() == 0) { + // TODO(sansid, elaurent): Figure out how to handle errors in listing the modules here. + dspInfo = null; + } else { + // TODO(sansid, elaurent): Figure out how to determine which module corresponds to the + // DSP hardware. + ModuleProperties properties = modules.get(0); + dspInfo = new DspInfo(properties.uuid, properties.implementor, properties.description, + properties.version, properties.powerConsumptionMw); + } + } + + /** + * @return True, if the keyphrase is supported on DSP for the given locale. + */ + public boolean isKeyphraseSupported(String keyphrase, String locale) { + // TODO(sansid): We also need to look into a SoundTrigger API that let's us + // query this. For now just return supported if there's a DSP available. + return dspInfo != null; + } + + /** + * @return True, if the keyphrase is has been enrolled for the given locale. + */ + public boolean isKeyphraseEnrolled(String keyphrase, String locale) { + // TODO(sansid, elaurent): Query SoundTrigger to list currently loaded sound models. + // They have been enrolled. + return false; + } + + /** + * @return True, if a recognition for the keyphrase is active for the given locale. + */ + public boolean isKeyphraseActive(String keyphrase, String locale) { + // TODO(sansid, elaurent): Check if the recognition for the keyphrase is currently active. + return false; + } +} diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java index e15489b..e0329f8 100644 --- a/core/java/android/service/voice/VoiceInteractionService.java +++ b/core/java/android/service/voice/VoiceInteractionService.java @@ -17,7 +17,6 @@ package android.service.voice; import android.annotation.SdkConstant; -import android.app.Instrumentation; import android.app.Service; import android.content.Context; import android.content.Intent; @@ -25,8 +24,11 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; + +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceInteractionManagerService; + /** * Top-level service of the current global voice interactor, which is providing * support for hotwording, the back-end of a {@link android.app.VoiceInteractor}, etc. @@ -51,6 +53,16 @@ public class VoiceInteractionService extends Service { public static final String SERVICE_INTERFACE = "android.service.voice.VoiceInteractionService"; + // TODO(sansid): Unhide these. + /** @hide */ + public static final int KEYPHRASE_UNAVAILABLE = 0; + /** @hide */ + public static final int KEYPHRASE_UNENROLLED = 1; + /** @hide */ + public static final int KEYPHRASE_ENROLLED = 2; + /** @hide */ + public static final int KEYPHRASE_ACTIVE = 3; + /** * Name under which a VoiceInteractionService component publishes information about itself. * This meta-data should reference an XML resource containing a @@ -64,6 +76,9 @@ public class VoiceInteractionService extends Service { IVoiceInteractionManagerService mSystemService; + private SoundTriggerManager mSoundTriggerManager; + private KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo; + public void startSession(Bundle args) { try { mSystemService.startSession(mInterface, args); @@ -76,6 +91,8 @@ public class VoiceInteractionService extends Service { super.onCreate(); mSystemService = IVoiceInteractionManagerService.Stub.asInterface( ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE)); + mKeyphraseEnrollmentInfo = new KeyphraseEnrollmentInfo(getPackageManager()); + mSoundTriggerManager = new SoundTriggerManager(); } @Override @@ -85,4 +102,44 @@ public class VoiceInteractionService extends Service { } return null; } + + /** + * Gets the state of always-on hotword detection for the given keyphrase and locale + * on this system. + * Availability implies that the hardware on this system is capable of listening for + * the given keyphrase or not. + * The return code is one of {@link #KEYPHRASE_UNAVAILABLE}, {@link #KEYPHRASE_UNENROLLED} + * {@link #KEYPHRASE_ENROLLED} or {@link #KEYPHRASE_ACTIVE}. + * + * @param keyphrase The keyphrase whose availability is being checked. + * @param locale The locale for which the availability is being checked. + * @return Indicates if always-on hotword detection is available for the given keyphrase. + * TODO(sansid): Unhide this. + * @hide + */ + public final int getAlwaysOnKeyphraseAvailability(String keyphrase, String locale) { + // The available keyphrases is a combination of DSP availability and + // the keyphrases that have an enrollment application for them. + if (!mSoundTriggerManager.isKeyphraseSupported(keyphrase, locale) + || !mKeyphraseEnrollmentInfo.isKeyphraseEnrollmentSupported(keyphrase, locale)) { + return KEYPHRASE_UNAVAILABLE; + } + if (!mSoundTriggerManager.isKeyphraseEnrolled(keyphrase, locale)) { + return KEYPHRASE_UNENROLLED; + } + if (!mSoundTriggerManager.isKeyphraseActive(keyphrase, locale)) { + return KEYPHRASE_ENROLLED; + } else { + return KEYPHRASE_ACTIVE; + } + } + + /** + * @return Details of keyphrases available for enrollment. + * @hide + */ + @VisibleForTesting + protected final KeyphraseEnrollmentInfo getKeyphraseEnrollmentInfo() { + return mKeyphraseEnrollmentInfo; + } } diff --git a/core/java/android/speech/tts/RequestConfig.java b/core/java/android/speech/tts/RequestConfig.java index 4b5385f..84880c0 100644 --- a/core/java/android/speech/tts/RequestConfig.java +++ b/core/java/android/speech/tts/RequestConfig.java @@ -1,3 +1,18 @@ +/* + * 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.speech.tts; import android.media.AudioManager; diff --git a/core/java/android/speech/tts/RequestConfigHelper.java b/core/java/android/speech/tts/RequestConfigHelper.java index b25c985..3b5490b 100644 --- a/core/java/android/speech/tts/RequestConfigHelper.java +++ b/core/java/android/speech/tts/RequestConfigHelper.java @@ -1,3 +1,18 @@ +/* + * 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.speech.tts; import android.speech.tts.TextToSpeechClient.EngineStatus; diff --git a/core/java/android/speech/tts/SynthesisRequestV2.java b/core/java/android/speech/tts/SynthesisRequestV2.java index 130e3f9..a42aa16 100644 --- a/core/java/android/speech/tts/SynthesisRequestV2.java +++ b/core/java/android/speech/tts/SynthesisRequestV2.java @@ -1,3 +1,18 @@ +/* + * 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.speech.tts; import android.os.Bundle; diff --git a/core/java/android/speech/tts/VoiceInfo.java b/core/java/android/speech/tts/VoiceInfo.java index 16b9a97..71629dc 100644 --- a/core/java/android/speech/tts/VoiceInfo.java +++ b/core/java/android/speech/tts/VoiceInfo.java @@ -1,3 +1,18 @@ +/* + * 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.speech.tts; import android.os.Bundle; diff --git a/core/jni/Android.mk b/core/jni/Android.mk index de46804..15dfed1 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -89,6 +89,7 @@ LOCAL_SRC_FILES:= \ android_util_Process.cpp \ android_util_StringBlock.cpp \ android_util_XmlBlock.cpp \ + android/graphics/AndroidPicture.cpp \ android/graphics/AutoDecodeCancel.cpp \ android/graphics/Bitmap.cpp \ android/graphics/BitmapFactory.cpp \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index f8e6bc3..f2b9bac 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -244,9 +244,6 @@ AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength) mArgBlockLength(argBlockLength) { SkGraphics::Init(); - // this sets our preference for 16bit images during decode - // in case the src is opaque and 24bit - SkImageDecoder::SetDeviceConfig(SkBitmap::kRGB_565_Config); // There is also a global font cache, but its budget is specified in code // see SkFontHost_android.cpp @@ -825,8 +822,9 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) mOptions.add(opt); } - // Whether the profile should start upon app startup or be delayed by some random offset. - property_get("dalvik.vm.profile.start-immediately", propBuf, "0"); + // Whether the profile should start upon app startup or be delayed by some random offset + // (in seconds) that is bound between 0 and a fixed value. + property_get("dalvik.vm.profile.start-immed", propBuf, "0"); if (propBuf[0] == '1') { opt.optionString = "-Xprofile-start-immediately"; mOptions.add(opt); diff --git a/core/jni/android/graphics/AndroidPicture.cpp b/core/jni/android/graphics/AndroidPicture.cpp new file mode 100644 index 0000000..5977ab2 --- /dev/null +++ b/core/jni/android/graphics/AndroidPicture.cpp @@ -0,0 +1,112 @@ +/* + * 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. + */ + +#include "AndroidPicture.h" +#include "SkCanvas.h" +#include "SkStream.h" + +AndroidPicture::AndroidPicture(const AndroidPicture* src) { + if (NULL != src) { + mWidth = src->width(); + mHeight = src->height(); + if (NULL != src->mPicture.get()) { + mPicture.reset(SkRef(src->mPicture.get())); + } if (NULL != src->mRecorder.get()) { + mPicture.reset(src->makePartialCopy()); + } + } else { + mWidth = 0; + mHeight = 0; + } +} + +SkCanvas* AndroidPicture::beginRecording(int width, int height) { + mPicture.reset(NULL); + mRecorder.reset(new SkPictureRecorder); + mWidth = width; + mHeight = height; + return mRecorder->beginRecording(width, height, NULL, 0); +} + +void AndroidPicture::endRecording() { + if (NULL != mRecorder.get()) { + mPicture.reset(mRecorder->endRecording()); + mRecorder.reset(NULL); + } +} + +int AndroidPicture::width() const { + if (NULL != mPicture.get()) { + SkASSERT(mPicture->width() == mWidth); + SkASSERT(mPicture->height() == mHeight); + } + + return mWidth; +} + +int AndroidPicture::height() const { + if (NULL != mPicture.get()) { + SkASSERT(mPicture->width() == mWidth); + SkASSERT(mPicture->height() == mHeight); + } + + return mHeight; +} + +AndroidPicture* AndroidPicture::CreateFromStream(SkStream* stream) { + AndroidPicture* newPict = new AndroidPicture; + + newPict->mPicture.reset(SkPicture::CreateFromStream(stream)); + if (NULL != newPict->mPicture.get()) { + newPict->mWidth = newPict->mPicture->width(); + newPict->mHeight = newPict->mPicture->height(); + } + + return newPict; +} + +void AndroidPicture::serialize(SkWStream* stream) const { + if (NULL != mRecorder.get()) { + SkAutoTDelete<SkPicture> tempPict(this->makePartialCopy()); + tempPict->serialize(stream); + } else if (NULL != mPicture.get()) { + mPicture->serialize(stream); + } else { + SkPicture empty; + empty.serialize(stream); + } +} + +void AndroidPicture::draw(SkCanvas* canvas) { + if (NULL != mRecorder.get()) { + this->endRecording(); + SkASSERT(NULL != mPicture.get()); + } + if (NULL != mPicture.get()) { + // TODO: remove this const_cast once pictures are immutable + const_cast<SkPicture*>(mPicture.get())->draw(canvas); + } +} + +SkPicture* AndroidPicture::makePartialCopy() const { + SkASSERT(NULL != mRecorder.get()); + + SkPictureRecorder reRecorder; + + SkCanvas* canvas = reRecorder.beginRecording(mWidth, mHeight, NULL, 0); + mRecorder->partialReplay(canvas); + return reRecorder.endRecording(); +} diff --git a/core/jni/android/graphics/AndroidPicture.h b/core/jni/android/graphics/AndroidPicture.h new file mode 100644 index 0000000..f434941 --- /dev/null +++ b/core/jni/android/graphics/AndroidPicture.h @@ -0,0 +1,63 @@ +/* + * 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. + */ + +#ifndef ANDROID_PICTURE_H +#define ANDROID_PICTURE_H + +#include "SkPicture.h" +#include "SkPictureRecorder.h" +#include "SkRefCnt.h" +#include "SkTemplates.h" + +class SkCanvas; +class SkPicture; +class SkPictureRecorder; +class SkStream; +class SkWStream; + +// Skia's SkPicture class has been split into an SkPictureRecorder +// and an SkPicture. AndroidPicture recreates the functionality +// of the old SkPicture interface by flip-flopping between the two +// new classes. +class AndroidPicture { +public: + explicit AndroidPicture(const AndroidPicture* src = NULL); + + SkCanvas* beginRecording(int width, int height); + + void endRecording(); + + int width() const; + + int height() const; + + static AndroidPicture* CreateFromStream(SkStream* stream); + + void serialize(SkWStream* stream) const; + + void draw(SkCanvas* canvas); + +private: + int mWidth; + int mHeight; + SkAutoTUnref<const SkPicture> mPicture; + SkAutoTDelete<SkPictureRecorder> mRecorder; + + // Make a copy of a picture that is in the midst of being recorded. The + // resulting picture will have balanced saves and restores. + SkPicture* makePartialCopy() const; +}; +#endif // ANDROID_PICTURE_H diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 0328517..9998995 100644 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -361,24 +361,50 @@ static jboolean Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) { } static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle, - jint width, jint height, jint configHandle, jint allocSize) { + jint width, jint height, jint configHandle, jint allocSize, + jboolean requestPremul) { SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle); - if (width * height * SkBitmap::ComputeBytesPerPixel(config) > allocSize) { + SkColorType colorType = SkBitmapConfigToColorType(config); + + // ARGB_4444 is a deprecated format, convert automatically to 8888 + if (colorType == kARGB_4444_SkColorType) { + colorType = kN32_SkColorType; + } + + if (width * height * SkColorTypeBytesPerPixel(colorType) > allocSize) { // done in native as there's no way to get BytesPerPixel in Java doThrowIAE(env, "Bitmap not large enough to support new configuration"); return; } SkPixelRef* ref = bitmap->pixelRef(); - SkSafeRef(ref); - bitmap->setConfig(config, width, height); + ref->ref(); + SkAlphaType alphaType; + if (bitmap->colorType() != kRGB_565_SkColorType + && bitmap->alphaType() == kOpaque_SkAlphaType) { + // If the original bitmap was set to opaque, keep that setting, unless it + // was 565, which is required to be opaque. + alphaType = kOpaque_SkAlphaType; + } else { + // Otherwise respect the premultiplied request. + alphaType = requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType; + } + bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType)); + // FIXME: Skia thinks of an SkPixelRef as having a constant SkImageInfo (except for + // its alphatype), so it would make more sense from Skia's perspective to create a + // new SkPixelRef. That said, libhwui uses the pointer to the SkPixelRef as a key + // for its cache, so it won't realize this is the same Java Bitmap. + SkImageInfo& info = const_cast<SkImageInfo&>(ref->info()); + // Use the updated from the SkBitmap, which may have corrected an invalid alphatype. + // (e.g. 565 non-opaque) + info = bitmap->info(); bitmap->setPixelRef(ref); // notifyPixelsChanged will increment the generation ID even though the actual pixel data // hasn't been touched. This signals the renderer that the bitmap (including width, height, - // and config) has changed. + // colortype and alphatype) has changed. ref->notifyPixelsChanged(); - SkSafeUnref(ref); + ref->unref(); } // These must match the int values in Bitmap.java @@ -799,7 +825,7 @@ static JNINativeMethod gBitmapMethods[] = { (void*)Bitmap_copy }, { "nativeDestructor", "(J)V", (void*)Bitmap_destructor }, { "nativeRecycle", "(J)Z", (void*)Bitmap_recycle }, - { "nativeReconfigure", "(JIIII)V", (void*)Bitmap_reconfigure }, + { "nativeReconfigure", "(JIIIIZ)V", (void*)Bitmap_reconfigure }, { "nativeCompress", "(JIILjava/io/OutputStream;[B)Z", (void*)Bitmap_compress }, { "nativeErase", "(JI)V", (void*)Bitmap_erase }, diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp index 3b44f97..6de3b9e 100644 --- a/core/jni/android/graphics/Canvas.cpp +++ b/core/jni/android/graphics/Canvas.cpp @@ -19,11 +19,14 @@ #include <android_runtime/AndroidRuntime.h> #include "SkCanvas.h" +#include "SkClipStack.h" #include "SkDevice.h" +#include "SkDeque.h" #include "SkDrawFilter.h" #include "SkGraphics.h" #include "SkPorterDuff.h" #include "SkShader.h" +#include "SkTArray.h" #include "SkTemplates.h" #ifdef USE_MINIKIN @@ -43,11 +46,42 @@ namespace android { +class ClipCopier : public SkCanvas::ClipVisitor { +public: + ClipCopier(SkCanvas* dstCanvas) : m_dstCanvas(dstCanvas) {} + + virtual void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) { + m_dstCanvas->clipRect(rect, op, antialias); + } + virtual void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) { + m_dstCanvas->clipRRect(rrect, op, antialias); + } + virtual void clipPath(const SkPath& path, SkRegion::Op op, bool antialias) { + m_dstCanvas->clipPath(path, op, antialias); + } + +private: + SkCanvas* m_dstCanvas; +}; + // Holds an SkCanvas reference plus additional native data. class NativeCanvasWrapper { +private: + struct SaveRec { + int saveCount; + SkCanvas::SaveFlags saveFlags; + }; + public: NativeCanvasWrapper(SkCanvas* canvas) - : mCanvas(canvas) { } + : mCanvas(canvas) + , mSaveStack(NULL) { + SkASSERT(canvas); + } + + ~NativeCanvasWrapper() { + delete mSaveStack; + } SkCanvas* getCanvas() const { return mCanvas.get(); @@ -56,28 +90,127 @@ public: void setCanvas(SkCanvas* canvas) { SkASSERT(canvas); mCanvas.reset(canvas); - } -private: - SkAutoTUnref<SkCanvas> mCanvas; -}; + delete mSaveStack; + mSaveStack = NULL; + } -class ClipCopier : public SkCanvas::ClipVisitor { -public: - ClipCopier(SkCanvas* dstCanvas) : m_dstCanvas(dstCanvas) {} + int save(SkCanvas::SaveFlags flags) { + int count = mCanvas->save(); + recordPartialSave(flags); + return count; + } - virtual void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) { - m_dstCanvas->clipRect(rect, op, antialias); + int saveLayer(const SkRect* bounds, const SkPaint* paint, + SkCanvas::SaveFlags flags) { + int count = mCanvas->saveLayer(bounds, paint, + static_cast<SkCanvas::SaveFlags>(flags | SkCanvas::kMatrixClip_SaveFlag)); + recordPartialSave(flags); + return count; } - virtual void clipRRect(const SkRRect& rrect, SkRegion::Op op, bool antialias) { - m_dstCanvas->clipRRect(rrect, op, antialias); + + int saveLayerAlpha(const SkRect* bounds, U8CPU alpha, + SkCanvas::SaveFlags flags) { + int count = mCanvas->saveLayerAlpha(bounds, alpha, + static_cast<SkCanvas::SaveFlags>(flags | SkCanvas::kMatrixClip_SaveFlag)); + recordPartialSave(flags); + return count; } - virtual void clipPath(const SkPath& path, SkRegion::Op op, bool antialias) { - m_dstCanvas->clipPath(path, op, antialias); + + void restore() { + const SaveRec* rec = (NULL == mSaveStack) + ? NULL + : static_cast<SaveRec*>(mSaveStack->back()); + int currentSaveCount = mCanvas->getSaveCount() - 1; + SkASSERT(NULL == rec || currentSaveCount >= rec->saveCount); + + if (NULL == rec || rec->saveCount != currentSaveCount) { + // Fast path - no record for this frame. + mCanvas->restore(); + return; + } + + bool preserveMatrix = !(rec->saveFlags & SkCanvas::kMatrix_SaveFlag); + bool preserveClip = !(rec->saveFlags & SkCanvas::kClip_SaveFlag); + + SkMatrix savedMatrix; + if (preserveMatrix) { + savedMatrix = mCanvas->getTotalMatrix(); + } + + SkTArray<SkClipStack::Element> savedClips; + if (preserveClip) { + saveClipsForFrame(savedClips, currentSaveCount); + } + + mCanvas->restore(); + + if (preserveMatrix) { + mCanvas->setMatrix(savedMatrix); + } + + if (preserveClip && !savedClips.empty()) { + applyClips(savedClips); + } + + mSaveStack->pop_back(); } private: - SkCanvas* m_dstCanvas; + void recordPartialSave(SkCanvas::SaveFlags flags) { + // A partial save is a save operation which doesn't capture the full canvas state. + // (either kMatrix_SaveFlags or kClip_SaveFlag is missing). + + // Mask-out non canvas state bits. + flags = static_cast<SkCanvas::SaveFlags>(flags & SkCanvas::kMatrixClip_SaveFlag); + + if (SkCanvas::kMatrixClip_SaveFlag == flags) { + // not a partial save. + return; + } + + if (NULL == mSaveStack) { + mSaveStack = new SkDeque(sizeof(struct SaveRec), 8); + } + + SaveRec* rec = static_cast<SaveRec*>(mSaveStack->push_back()); + // Store the save counter in the SkClipStack domain. + // (0-based, equal to the number of save ops on the stack). + rec->saveCount = mCanvas->getSaveCount() - 1; + rec->saveFlags = flags; + } + + void saveClipsForFrame(SkTArray<SkClipStack::Element>& clips, + int frameSaveCount) { + SkClipStack::Iter clipIterator(*mCanvas->getClipStack(), + SkClipStack::Iter::kTop_IterStart); + while (const SkClipStack::Element* elem = clipIterator.next()) { + if (elem->getSaveCount() < frameSaveCount) { + // done with the current frame. + break; + } + SkASSERT(elem->getSaveCount() == frameSaveCount); + clips.push_back(*elem); + } + } + + void applyClips(const SkTArray<SkClipStack::Element>& clips) { + ClipCopier clipCopier(mCanvas); + + // The clip stack stores clips in device space. + SkMatrix origMatrix = mCanvas->getTotalMatrix(); + mCanvas->resetMatrix(); + + // We pushed the clips in reverse order. + for (int i = clips.count() - 1; i >= 0; --i) { + clips[i].replay(&clipCopier); + } + + mCanvas->setMatrix(origMatrix); + } + + SkAutoTUnref<SkCanvas> mCanvas; + SkDeque* mSaveStack; // lazily allocated, tracks partial saves. }; // Returns true if the SkCanvas's clip is non-empty. @@ -88,11 +221,15 @@ static jboolean hasNonEmptyClip(const SkCanvas& canvas) { class SkCanvasGlue { public: + // Get the native wrapper for a given handle. + static inline NativeCanvasWrapper* getNativeWrapper(jlong nativeHandle) { + SkASSERT(nativeHandle); + return reinterpret_cast<NativeCanvasWrapper*>(nativeHandle); + } // Get the SkCanvas for a given native handle. static inline SkCanvas* getNativeCanvas(jlong nativeHandle) { - SkASSERT(nativeHandle); - NativeCanvasWrapper* wrapper = reinterpret_cast<NativeCanvasWrapper*>(nativeHandle); + NativeCanvasWrapper* wrapper = getNativeWrapper(nativeHandle); SkCanvas* canvas = wrapper->getCanvas(); SkASSERT(canvas); @@ -186,56 +323,56 @@ public: } static jint save(JNIEnv*, jobject, jlong canvasHandle, jint flagsHandle) { - SkCanvas* canvas = getNativeCanvas(canvasHandle); + NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle); SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle); - return static_cast<jint>(canvas->save(flags)); + return static_cast<jint>(wrapper->save(flags)); } static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t, jfloat r, jfloat b, - jlong paintHandle, jint flags) { - SkCanvas* canvas = getNativeCanvas(canvasHandle); + jlong paintHandle, jint flagsHandle) { + NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle); SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle); + SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle); SkRect bounds; bounds.set(l, t, r, b); - int result = canvas->saveLayer(&bounds, paint, - static_cast<SkCanvas::SaveFlags>(flags)); - return static_cast<jint>(result); + return static_cast<jint>(wrapper->saveLayer(&bounds, paint, flags)); } static jint saveLayerAlpha(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t, jfloat r, jfloat b, - jint alpha, jint flags) { - SkCanvas* canvas = getNativeCanvas(canvasHandle); + jint alpha, jint flagsHandle) { + NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle); + SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle); SkRect bounds; bounds.set(l, t, r, b); - int result = canvas->saveLayerAlpha(&bounds, alpha, - static_cast<SkCanvas::SaveFlags>(flags)); - return static_cast<jint>(result); + return static_cast<jint>(wrapper->saveLayerAlpha(&bounds, alpha, flags)); } static void restore(JNIEnv* env, jobject, jlong canvasHandle) { - SkCanvas* canvas = getNativeCanvas(canvasHandle); - if (canvas->getSaveCount() <= 1) { // cannot restore anymore + NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle); + if (wrapper->getCanvas()->getSaveCount() <= 1) { // cannot restore anymore doThrowISE(env, "Underflow in restore"); return; } - canvas->restore(); + wrapper->restore(); } static jint getSaveCount(JNIEnv*, jobject, jlong canvasHandle) { - SkCanvas* canvas = getNativeCanvas(canvasHandle); - return static_cast<jint>(canvas->getSaveCount()); + return static_cast<jint>(getNativeCanvas(canvasHandle)->getSaveCount()); } static void restoreToCount(JNIEnv* env, jobject, jlong canvasHandle, jint restoreCount) { - SkCanvas* canvas = getNativeCanvas(canvasHandle); + NativeCanvasWrapper* wrapper = getNativeWrapper(canvasHandle); if (restoreCount < 1) { doThrowIAE(env, "Underflow in restoreToCount"); return; } - canvas->restoreToCount(restoreCount); + + while (wrapper->getCanvas()->getSaveCount() > restoreCount) { + wrapper->restore(); + } } static void translate(JNIEnv*, jobject, jlong canvasHandle, diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index 64ad223..a4337cc 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -3,6 +3,7 @@ #include "jni.h" #include "JNIHelp.h" #include "GraphicsJNI.h" +#include "AndroidPicture.h" #include "SkCanvas.h" #include "SkDevice.h" @@ -345,13 +346,13 @@ android::TypefaceImpl* GraphicsJNI::getNativeTypeface(JNIEnv* env, jobject paint return p; } -SkPicture* GraphicsJNI::getNativePicture(JNIEnv* env, jobject picture) +AndroidPicture* GraphicsJNI::getNativePicture(JNIEnv* env, jobject picture) { SkASSERT(env); SkASSERT(picture); SkASSERT(env->IsInstanceOf(picture, gPicture_class)); jlong pictureHandle = env->GetLongField(picture, gPicture_nativeInstanceID); - SkPicture* p = reinterpret_cast<SkPicture*>(pictureHandle); + AndroidPicture* p = reinterpret_cast<AndroidPicture*>(pictureHandle); SkASSERT(p); return p; } diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h index 73dd11b..2e2f920 100644 --- a/core/jni/android/graphics/GraphicsJNI.h +++ b/core/jni/android/graphics/GraphicsJNI.h @@ -14,7 +14,7 @@ class SkBitmapRegionDecoder; class SkCanvas; class SkPaint; -class SkPicture; +class AndroidPicture; class GraphicsJNI { public: @@ -50,7 +50,7 @@ public: static SkPaint* getNativePaint(JNIEnv*, jobject paint); static android::TypefaceImpl* getNativeTypeface(JNIEnv*, jobject paint); static SkBitmap* getNativeBitmap(JNIEnv*, jobject bitmap); - static SkPicture* getNativePicture(JNIEnv*, jobject picture); + static AndroidPicture* getNativePicture(JNIEnv*, jobject picture); static SkRegion* getNativeRegion(JNIEnv*, jobject region); // Given the 'native' long held by the Rasterizer.java object, return a diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp index a8a3dae..0683f73 100644 --- a/core/jni/android/graphics/Picture.cpp +++ b/core/jni/android/graphics/Picture.cpp @@ -17,9 +17,9 @@ #include "jni.h" #include "GraphicsJNI.h" #include <android_runtime/AndroidRuntime.h> +#include "AndroidPicture.h" #include "SkCanvas.h" -#include "SkPicture.h" #include "SkStream.h" #include "SkTemplates.h" #include "CreateJavaOutputStreamAdaptor.h" @@ -29,45 +29,41 @@ namespace android { class SkPictureGlue { public: static jlong newPicture(JNIEnv* env, jobject, jlong srcHandle) { - const SkPicture* src = reinterpret_cast<SkPicture*>(srcHandle); - if (src) { - return reinterpret_cast<jlong>(new SkPicture(*src)); - } else { - return reinterpret_cast<jlong>(new SkPicture); - } + const AndroidPicture* src = reinterpret_cast<AndroidPicture*>(srcHandle); + return reinterpret_cast<jlong>(new AndroidPicture(src)); } static jlong deserialize(JNIEnv* env, jobject, jobject jstream, jbyteArray jstorage) { - SkPicture* picture = NULL; + AndroidPicture* picture = NULL; SkStream* strm = CreateJavaInputStreamAdaptor(env, jstream, jstorage); if (strm) { - picture = SkPicture::CreateFromStream(strm); + picture = AndroidPicture::CreateFromStream(strm); delete strm; } return reinterpret_cast<jlong>(picture); } static void killPicture(JNIEnv* env, jobject, jlong pictureHandle) { - SkPicture* picture = reinterpret_cast<SkPicture*>(pictureHandle); + AndroidPicture* picture = reinterpret_cast<AndroidPicture*>(pictureHandle); SkASSERT(picture); - picture->unref(); + delete picture; } static void draw(JNIEnv* env, jobject, jlong canvasHandle, jlong pictureHandle) { SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle); - SkPicture* picture = reinterpret_cast<SkPicture*>(pictureHandle); + AndroidPicture* picture = reinterpret_cast<AndroidPicture*>(pictureHandle); SkASSERT(canvas); SkASSERT(picture); picture->draw(canvas); } static jboolean serialize(JNIEnv* env, jobject, jlong pictureHandle, - jobject jstream, jbyteArray jstorage) { - SkPicture* picture = reinterpret_cast<SkPicture*>(pictureHandle); + jobject jstream, jbyteArray jstorage) { + AndroidPicture* picture = reinterpret_cast<AndroidPicture*>(pictureHandle); SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage); - + if (NULL != strm) { picture->serialize(strm); delete strm; @@ -78,19 +74,21 @@ public: static jint getWidth(JNIEnv* env, jobject jpic) { NPE_CHECK_RETURN_ZERO(env, jpic); - int width = GraphicsJNI::getNativePicture(env, jpic)->width(); + AndroidPicture* pict = GraphicsJNI::getNativePicture(env, jpic); + int width = pict->width(); return static_cast<jint>(width); } static jint getHeight(JNIEnv* env, jobject jpic) { NPE_CHECK_RETURN_ZERO(env, jpic); - int height = GraphicsJNI::getNativePicture(env, jpic)->height(); + AndroidPicture* pict = GraphicsJNI::getNativePicture(env, jpic); + int height = pict->height(); return static_cast<jint>(height); } static jlong beginRecording(JNIEnv* env, jobject, jlong pictHandle, - jint w, jint h) { - SkPicture* pict = reinterpret_cast<SkPicture*>(pictHandle); + jint w, jint h) { + AndroidPicture* pict = reinterpret_cast<AndroidPicture*>(pictHandle); // beginRecording does not ref its return value, it just returns it. SkCanvas* canvas = pict->beginRecording(w, h); // the java side will wrap this guy in a Canvas.java, which will call @@ -101,7 +99,7 @@ public: } static void endRecording(JNIEnv* env, jobject, jlong pictHandle) { - SkPicture* pict = reinterpret_cast<SkPicture*>(pictHandle); + AndroidPicture* pict = reinterpret_cast<AndroidPicture*>(pictHandle); pict->endRecording(); } }; diff --git a/core/jni/android/graphics/pdf/PdfDocument.cpp b/core/jni/android/graphics/pdf/PdfDocument.cpp index d54aaa8..3812c27 100644 --- a/core/jni/android/graphics/pdf/PdfDocument.cpp +++ b/core/jni/android/graphics/pdf/PdfDocument.cpp @@ -24,6 +24,7 @@ #include "SkCanvas.h" #include "SkDocument.h" #include "SkPicture.h" +#include "SkPictureRecorder.h" #include "SkStream.h" #include "SkRect.h" @@ -32,15 +33,22 @@ namespace android { struct PageRecord { PageRecord(int width, int height, const SkRect& contentRect) - : mPicture(new SkPicture()), mWidth(width), mHeight(height) { + : mPictureRecorder(new SkPictureRecorder()) + , mPicture(NULL) + , mWidth(width) + , mHeight(height) { mContentRect = contentRect; } ~PageRecord() { - mPicture->unref(); + delete mPictureRecorder; + if (NULL != mPicture) { + mPicture->unref(); + } } - SkPicture* const mPicture; + SkPictureRecorder* mPictureRecorder; + SkPicture* mPicture; const int mWidth; const int mHeight; SkRect mContentRect; @@ -62,8 +70,8 @@ public: mPages.push_back(page); mCurrentPage = page; - SkCanvas* canvas = page->mPicture->beginRecording( - contentRect.width(), contentRect.height(), 0); + SkCanvas* canvas = page->mPictureRecorder->beginRecording( + contentRect.width(), contentRect.height(), NULL, 0); // We pass this canvas to Java where it is used to construct // a Java Canvas object which dereferences the pointer when it @@ -75,7 +83,11 @@ public: void finishPage() { assert(mCurrentPage != NULL); - mCurrentPage->mPicture->endRecording(); + assert(mCurrentPage->mPictureRecorder != NULL); + assert(mCurrentPage->mPicture == NULL); + mCurrentPage->mPicture = mCurrentPage->mPictureRecorder->endRecording(); + delete mCurrentPage->mPictureRecorder; + mCurrentPage->mPictureRecorder = NULL; mCurrentPage = NULL; } @@ -89,7 +101,7 @@ public: canvas->clipRect(page->mContentRect); canvas->translate(page->mContentRect.left(), page->mContentRect.top()); - canvas->drawPicture(*page->mPicture); + canvas->drawPicture(page->mPicture); document->endPage(); } @@ -97,11 +109,10 @@ public: } void close() { + assert(NULL == mCurrentPage); for (unsigned i = 0; i < mPages.size(); i++) { delete mPages[i]; } - delete mCurrentPage; - mCurrentPage = NULL; } private: diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index bf47dd3..ee4c619 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -244,6 +244,12 @@ android_media_AudioSystem_isSourceActive(JNIEnv *env, jobject thiz, jint source) } static jint +android_media_AudioSystem_newAudioSessionId(JNIEnv *env, jobject thiz) +{ + return AudioSystem::newAudioSessionId(); +} + +static jint android_media_AudioSystem_setParameters(JNIEnv *env, jobject thiz, jstring keyValuePairs) { const jchar* c_keyValuePairs = env->GetStringCritical(keyValuePairs, 0); @@ -1295,6 +1301,7 @@ static JNINativeMethod gMethods[] = { {"isStreamActive", "(II)Z", (void *)android_media_AudioSystem_isStreamActive}, {"isStreamActiveRemotely","(II)Z", (void *)android_media_AudioSystem_isStreamActiveRemotely}, {"isSourceActive", "(I)Z", (void *)android_media_AudioSystem_isSourceActive}, + {"newAudioSessionId", "()I", (void *)android_media_AudioSystem_newAudioSessionId}, {"setDeviceConnectionState", "(IILjava/lang/String;)I", (void *)android_media_AudioSystem_setDeviceConnectionState}, {"getDeviceConnectionState", "(ILjava/lang/String;)I", (void *)android_media_AudioSystem_getDeviceConnectionState}, {"setPhoneState", "(I)I", (void *)android_media_AudioSystem_setPhoneState}, diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index 11f87cc..3d14aaf 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -95,6 +95,7 @@ sp<Surface> android_view_Surface_getSurface(JNIEnv* env, jobject surfaceObj) { env->GetLongField(surfaceObj, gSurfaceClassInfo.mNativeObject)); env->MonitorExit(lock); } + env->DeleteLocalRef(lock); return sur; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index f3b5ccf..3067cdd0 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2071,6 +2071,14 @@ android:description="@string/permdesc_bindVoiceInteraction" android:protectionLevel="signature" /> + <!-- Must be required by hotword enrollment application, + to ensure that only the system can interact with it. + @hide <p>Not for use by third-party applications.</p> --> + <permission android:name="android.permission.MANAGE_VOICE_KEYPHRASES" + android:label="@string/permlab_manageVoiceKeyphrases" + android:description="@string/permdesc_manageVoiceKeyphrases" + android:protectionLevel="signature|system" /> + <!-- Must be required by a {@link com.android.media.remotedisplay.RemoteDisplayProvider}, to ensure that only the system can bind to it. @hide --> @@ -2699,7 +2707,8 @@ android:theme="@style/Theme.Holo.Dialog.Alert" android:finishOnCloseSystemDialogs="true" android:excludeFromRecents="true" - android:multiprocess="true"> + android:multiprocess="true" + android:documentLaunchMode="never"> <intent-filter> <action android:name="android.intent.action.CHOOSER" /> <category android:name="android.intent.category.DEFAULT" /> diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml index bdf27c8..0564a8f 100644 --- a/core/res/res/layout/notification_template_material_big_base.xml +++ b/core/res/res/layout/notification_template_material_big_base.xml @@ -145,7 +145,7 @@ android:layout_marginStart="8dp" android:layout_marginEnd="8dp" android:visibility="gone" - style="@style/Widget.Material.Light.ProgressBar.Horizontal" + style="@style/Widget.StatusBar.Material.ProgressBar" /> </LinearLayout> <ImageView diff --git a/core/res/res/layout/notification_template_material_big_media.xml b/core/res/res/layout/notification_template_material_big_media.xml index c89b9f9..f8e1986 100644 --- a/core/res/res/layout/notification_template_material_big_media.xml +++ b/core/res/res/layout/notification_template_material_big_media.xml @@ -38,7 +38,6 @@ android:minHeight="@dimen/notification_large_icon_height" android:paddingTop="2dp" android:orientation="vertical" - android:background="@color/notification_media_info_bg" > <LinearLayout android:id="@+id/line1" @@ -147,7 +146,7 @@ android:layout_height="6dp" android:layout_gravity="top" android:visibility="gone" - style="@style/Widget.StatusBar.Material.ProgressBar" + style="@style/Widget.StatusBar.Material.ProgressBar.Media" /> </FrameLayout> </LinearLayout> diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml index 6f8c3a9..1de5add 100644 --- a/core/res/res/layout/notification_template_material_big_text.xml +++ b/core/res/res/layout/notification_template_material_big_text.xml @@ -99,7 +99,7 @@ android:layout_marginEnd="8dp" android:visibility="gone" android:layout_weight="0" - style="@style/Widget.Material.Light.ProgressBar.Horizontal" + style="@style/Widget.StatusBar.Material.ProgressBar" /> <TextView android:id="@+id/big_text" android:textAppearance="@style/TextAppearance.StatusBar.Material.EventContent" diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml index 8d82a17..6052fb0 100644 --- a/core/res/res/values-watch/config.xml +++ b/core/res/res/values-watch/config.xml @@ -36,4 +36,8 @@ <!-- Maximum velocity to initiate a fling, as measured in dips per second. --> <dimen name="config_viewMaxFlingVelocity">8000dp</dimen> + <!-- Number of notifications to keep in the notification service historical archive. + Reduced intentionally for watches to retain minimal memory footprint --> + <integer name="config_notificationServiceArchiveSize">1</integer> + </resources> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 5b362fc..2fea91e 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -6390,6 +6390,16 @@ <attr name="settingsActivity" /> </declare-styleable> + <!-- Use <code>voice-enrollment-application</code> + as the root tag of the XML resource that escribes the supported keyphrases (hotwords) + by the enrollment application. + Described here are the attributes that can be included in that tag. --> + <declare-styleable name="VoiceEnrollmentApplication"> + <attr name="searchKeyphraseId" format="integer" /> + <attr name="searchKeyphrase" format="string" /> + <attr name="searchKeyphraseSupportedLocales" format="string" /> + </declare-styleable> + <!-- Attributes used to style the Action Bar. --> <declare-styleable name="ActionBar"> <!-- The type of navigation to use. --> diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index 9bf2ce8..9f6c7ad 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -135,7 +135,6 @@ <color name="notification_action_legacy_color_filter">#ff555555</color> <color name="notification_media_action_bg">#00000000</color> - <color name="notification_media_info_bg">#40FFFFFF</color> <color name="notification_media_progress">#FFFFFFFF</color> <!-- Keyguard colors --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 9716f0c..9ff9b11 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -595,10 +595,11 @@ <integer name="config_shutdownBatteryTemperature">680</integer> <!-- Display low battery warning when battery level dips to this value --> - <integer name="config_lowBatteryWarningLevel">20</integer> + <integer name="config_lowBatteryWarningLevel">15</integer> - <!-- Close low battery warning when battery level reaches this value --> - <integer name="config_lowBatteryCloseWarningLevel">25</integer> + <!-- Close low battery warning when battery level reaches the lowBatteryWarningLevel + plus this --> + <integer name="config_lowBatteryCloseWarningBump">5</integer> <!-- Default color for notification LED. --> <color name="config_defaultNotificationColor">#ffffffff</color> @@ -627,6 +628,9 @@ <!-- Default value for LED off time when the battery is low on charge in miliseconds --> <integer name="config_notificationsBatteryLedOff">2875</integer> + <!-- Number of notifications to keep in the notification service historical archive --> + <integer name="config_notificationServiceArchiveSize">250</integer> + <!-- Allow the menu hard key to be disabled in LockScreen on some devices --> <bool name="config_disableMenuKeyInLockScreen">false</bool> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 8f3ee60..2792c93 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2185,6 +2185,9 @@ <public type="attr" name="translateY" /> <public type="attr" name="selectableItemBackgroundBorderless" /> <public type="attr" name="elegantTextHeight" /> + <public type="attr" name="searchKeyphraseId" /> + <public type="attr" name="searchKeyphrase" /> + <public type="attr" name="searchKeyphraseSupportedLocales" /> <public-padding type="dimen" name="l_resource_pad" end="0x01050010" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 2903ac2..f1d9dc3 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1122,6 +1122,12 @@ interface of a voice interaction service. Should never be needed for normal apps.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_manageVoiceKeyphrases">manage voice keyphrases</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_manageVoiceKeyphrases">Allows the holder to manage the keyphrases for voice hotword detection. + Should never be needed for normal apps.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_bindRemoteDisplay">bind to a remote display</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_bindRemoteDisplay">Allows the holder to bind to the top-level diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml index 92cce25..a40835c 100644 --- a/core/res/res/values/styles_material.xml +++ b/core/res/res/values/styles_material.xml @@ -345,6 +345,9 @@ please see styles_device_defaults.xml. </style> <style name="Widget.StatusBar.Material.ProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal"> + </style> + + <style name="Widget.StatusBar.Material.ProgressBar.Media"> <item name="android:progressDrawable">@drawable/notification_material_media_progress</item> </style> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 2ea7421..f2550ab 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1509,7 +1509,7 @@ <java-symbol type="integer" name="config_defaultNotificationLedOn" /> <java-symbol type="integer" name="config_deskDockKeepsScreenOn" /> <java-symbol type="integer" name="config_lightSensorWarmupTime" /> - <java-symbol type="integer" name="config_lowBatteryCloseWarningLevel" /> + <java-symbol type="integer" name="config_lowBatteryCloseWarningBump" /> <java-symbol type="integer" name="config_lowBatteryWarningLevel" /> <java-symbol type="integer" name="config_networkPolicyDefaultWarning" /> <java-symbol type="integer" name="config_networkTransitionTimeout" /> @@ -1518,6 +1518,7 @@ <java-symbol type="integer" name="config_notificationsBatteryLedOn" /> <java-symbol type="integer" name="config_notificationsBatteryLowARGB" /> <java-symbol type="integer" name="config_notificationsBatteryMediumARGB" /> + <java-symbol type="integer" name="config_notificationServiceArchiveSize" /> <java-symbol type="integer" name="config_radioScanningTimeout" /> <java-symbol type="integer" name="config_screenBrightnessSettingMinimum" /> <java-symbol type="integer" name="config_screenBrightnessSettingMaximum" /> @@ -1678,7 +1679,6 @@ <java-symbol type="drawable" name="notification_material_bg" /> <java-symbol type="drawable" name="notification_material_media_progress" /> <java-symbol type="color" name="notification_media_action_bg" /> - <java-symbol type="color" name="notification_media_info_bg" /> <java-symbol type="color" name="notification_media_progress" /> <java-symbol type="id" name="media_action_area" /> diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk index c6bccfe..abb960c 100644 --- a/data/fonts/Android.mk +++ b/data/fonts/Android.mk @@ -106,6 +106,7 @@ font_src_files := \ ifeq ($(MINIMAL_FONT_FOOTPRINT),true) $(eval $(call create-font-symlink,Roboto-Black.ttf,Roboto-Bold.ttf)) +$(eval $(call create-font-symlink,Roboto-BlackItalic.ttf,Roboto-BoldItalic.ttf)) $(eval $(call create-font-symlink,Roboto-Light.ttf,Roboto-Regular.ttf)) $(eval $(call create-font-symlink,Roboto-LightItalic.ttf,Roboto-Italic.ttf)) $(eval $(call create-font-symlink,Roboto-Medium.ttf,Roboto-Regular.ttf)) @@ -120,6 +121,7 @@ $(eval $(call create-font-symlink,RobotoCondensed-BoldItalic.ttf,Roboto-BoldItal else # !MINIMAL_FONT font_src_files += \ Roboto-Black.ttf \ + Roboto-BlackItalic.ttf \ Roboto-Light.ttf \ Roboto-LightItalic.ttf \ Roboto-Medium.ttf \ diff --git a/data/fonts/Roboto-BlackItalic.ttf b/data/fonts/Roboto-BlackItalic.ttf Binary files differnew file mode 100644 index 0000000..3ebdc7d --- /dev/null +++ b/data/fonts/Roboto-BlackItalic.ttf diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk index e5573bb..70fc6a2 100644 --- a/data/fonts/fonts.mk +++ b/data/fonts/fonts.mk @@ -25,6 +25,7 @@ PRODUCT_PACKAGES := \ Roboto-Italic.ttf \ Roboto-BoldItalic.ttf \ Roboto-Black.ttf \ + Roboto-BlackItalic.ttf \ Roboto-Light.ttf \ Roboto-LightItalic.ttf \ Roboto-Medium.ttf \ diff --git a/data/fonts/system_fonts.xml b/data/fonts/system_fonts.xml index 646b33b..8c59fea 100644 --- a/data/fonts/system_fonts.xml +++ b/data/fonts/system_fonts.xml @@ -82,6 +82,7 @@ </nameset> <fileset> <file>Roboto-Black.ttf</file> + <file>Roboto-BlackItalic.ttf</file> </fileset> </family> diff --git a/docs/html/distribute/engage/analytics.jd b/docs/html/distribute/engage/analytics.jd new file mode 100644 index 0000000..5f7cade --- /dev/null +++ b/docs/html/distribute/engage/analytics.jd @@ -0,0 +1,50 @@ +page.title=Understand User Behavior +page.metaDescription=Use Google Analytics to learn what your users like and what keeps them coming back. +page.tags="analytics, user behavior" +page.image=/images/gp-analytics.jpg + +@jd:body + +<div class="figure"> + <img src="{@docRoot}images/gp-analytics.jpg" style="width:320px"> +</div> + + +<p> + Link your Google Play Developer Console with Google Analytics to learn much + more about how users interact with your app — before and after they + download it. +</p> + +<p> + Start by discovering how many people visit your Google Play listing page, + where they come from, and how many go on to install your app. Once the app is + launched, use Google Analytics to see which of your features are most + popular, where power users spend their time, who tends to make in-app + purchases, and more. +</p> + +<p> + Google Analytics delivers the numbers in real time, so you can act fast to + update your landing page and your app. <a href= + "http://www.google.com/analytics/mobile/">Learn more</a>. +</p> + +<p> + If you have a Google Analytics account already, linking it to Google Play + takes just a few moments. You can also link your Google Analytics account to + Admob to start gaining more user insights to improve your in-app marketing. +</p> + + <div class="headerLine clearfloat"> + <h2 id="related-resources"> + Related Resources + </h2> +</div> + +<div class="resource-widget resource-flow-layout col-13" + data-query="collection:distribute/engage/analytics" + data-sortorder="-timestamp" + data-cardsizes="9x3" + data-maxresults="6"> +</div>
\ No newline at end of file diff --git a/docs/html/distribute/engage/deep-linking.jd b/docs/html/distribute/engage/deep-linking.jd index 0417ba1..a25c3c6 100644 --- a/docs/html/distribute/engage/deep-linking.jd +++ b/docs/html/distribute/engage/deep-linking.jd @@ -1,13 +1,13 @@ page.title=Deep Link to Bring Users Back -page.metaDescription=Use deep links to bring your users into your apps from social posts or search. +page.metaDescription=Use deep links to bring your users into your apps from social posts, search, or ads. page.tags="app indexing, google+ signin" page.image=/images/gp-listing-4.jpg @jd:body <p> - Use deep links to bring your users into your apps from social posts or - search. + Use deep links to bring your users into your apps from social posts, + search, or ads. </p> <div class="headerLine"> @@ -47,6 +47,11 @@ page.image=/images/gp-listing-4.jpg <h2>Deep Linking from Google Search — App Indexing</h2> </div> + +<div style="float:right;"> + <img src="/images/gp-listing-4.jpg" style="padding-top:1em;padding-left:2em;"> +</div> + <p> Another way to bring users back to your apps is to apply for app indexing. </p> @@ -60,11 +65,29 @@ page.image=/images/gp-listing-4.jpg content</a>. </p> -<div> - <img src="{@docRoot}images/gp-listing-4.jpg" style="padding-top:1em;"> +<div class="clearfloat" style="margin-top:2em;"></div> + +<div style="float:right;width:340px;padding-left:2em;"> + <img src="/images/gp-ads-linking2.jpg" style="padding-top:1em;"> </div> -<div class="headerLine"> +<div class="headerLine "> +<h2>Deep Linking from Google Ads</h2> +</div> +<p> + Ads can remind users about the apps they already have. +</p> + +<p> + As with deep links from Google's organic search results, AdWords deep links + send users directly to the relevant pages in apps they already have on their + mobile device. A mobile search for "flights to London," for instance, could + take a user straight to the London page in a travel app. <a href= + "http://www.thinkwithgoogle.com/products/ads-apps.html" + class="external-link">Learn more</a>. +</p> + +<div class="headerLine clearfloat"> <h2 id="related-resources"> Related Resources </h2> diff --git a/docs/html/distribute/engage/engage_toc.cs b/docs/html/distribute/engage/engage_toc.cs index 0314f8c..596051a 100644 --- a/docs/html/distribute/engage/engage_toc.cs +++ b/docs/html/distribute/engage/engage_toc.cs @@ -37,6 +37,12 @@ </li> <li class="nav-section"> <div class="nav-section empty" style="font-weight:normal"><a href="<?cs + var:toroot?>distribute/engage/analytics.html"> + <span class="en">Understand User Behavior</span></a> + </div> + </li> + <li class="nav-section"> + <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/engage/app-updates.html"> <span class="en">Update Regularly</span></a> </div> diff --git a/docs/html/distribute/engage/index.jd b/docs/html/distribute/engage/index.jd index f8cd1ee..2b103c3 100644 --- a/docs/html/distribute/engage/index.jd +++ b/docs/html/distribute/engage/index.jd @@ -15,8 +15,8 @@ nonavpage=true <div class="resource-widget resource-flow-layout landing col-16" data-query="collection:distribute/engagelanding" - data-cardSizes="6x6" - data-maxResults="9"> + data-cardSizes="9x6,9x6,6x6,6x6,6x6,9x6,9x6,6x6,6x6,6x6" + data-maxResults="10"> </div> <h3>Related Resources</h3> diff --git a/docs/html/distribute/engage/widgets.jd b/docs/html/distribute/engage/widgets.jd index 286adea..6adb55c 100644 --- a/docs/html/distribute/engage/widgets.jd +++ b/docs/html/distribute/engage/widgets.jd @@ -1,5 +1,5 @@ page.title=Build Useful Widgets -page.metaDescription=Use widgets to remind users about important information in your apps and games, even when your apps are closed. +page.metaDescription=Use home screen widgets to remind users about important information in your apps and games, even when your apps are closed. page.tags="" page.image=/images/gp-engage-0.jpg diff --git a/docs/html/distribute/monetize/ads.jd b/docs/html/distribute/monetize/ads.jd index bcb1e52..9a847ff 100644 --- a/docs/html/distribute/monetize/ads.jd +++ b/docs/html/distribute/monetize/ads.jd @@ -10,17 +10,32 @@ page.image=/distribute/images/advertising.png </div> <p> - In-app advertising offers a quick and easy way to incorporate a monetization - option into both your <a href= + Ads can be a quick and easy way to earn more from your <a href= "{@docRoot}distribute/monetize/freemium.html">freemium</a>, <a href= "{@docRoot}distribute/monetize/premium.html">premium</a>, and <a href= - "{@docRoot}distribute/monetize/subscriptions.html">subscription</a> apps. </p> + "{@docRoot}distribute/monetize/subscriptions.html">subscription</a> apps. + AdMob and the Google Mobile Ads SDK let you add advertising to your apps with + just a few lines of code. +</p> + +<p> + The question is: which model gets the best results for your app? Google's ad + tools are made to help you figure out what combination works best for both + your audience and your bottom line. </p> -<p>Using <a href= +<p>Start by linking your AdMob and Google + Analytics accounts to get better insights and more earning power: for + instance, AdMob can promote in-app purchases to the people who buy them most + often, while showing income-generating ads to those less likely to buy right + now. +</p> + +<p> + Using <a href= "http://www.google.com/ads/admob/monetize.html#subid=us-en-et-dac">AdMob</a> - and the <a href="{@docRoot}google/play-services/ads.html">Google - Mobile Ads SDK</a> included in Google Play Services, you’re able to add - advertising into your apps, with just a few lines of code. + and the <a href="{@docRoot}google/play-services/ads.html">Google Mobile Ads + SDK</a> included in Google Play Services, you’re able to add advertising into + your apps, with just a few lines of code. </p> <p> @@ -30,36 +45,33 @@ page.image=/distribute/images/advertising.png <ul> <li> <p> - <strong>Placement within your apps</strong> — Well placed ads will - optimize your revenue by making it more likely that users will ‘click - through’. Poorly placed ads can result in low click-through rates, and in - the worse cases poor rating and users rapidly abandoning your apps. You - can get advice on how to best place ads from the developer training on - <a href= - "{@docRoot}training/monetization/ads-and-ux.html">using - ads</a>. + <strong>Placement within your apps</strong> — Well-placed ads make + it more likely that users will click through and convert. Poorly-placed + ads lead to lower click-through rates, and even poor ratings and users + abandoning your apps. Our <a href= + "{@docRoot}training/monetization/ads-and-ux.html">developer training</a> + on using ads shows some of the best ways to place ads. </p> </li> <li> <p> <strong>Ad formats</strong> — Every app offers a different type of - experience for users, so it’s important to consider the format of ads - you’re using to ensure it’s compatible with the experience. While banner - ads may work well for a flashlight utility app, an immersive gaming app - may benefit more from a video interstitial. Mismatched ad formats may - negatively affect your users’ experience and ad revenue, so try to select - formats that fit well with the content and flow of your apps. + experience for users, so it’s important that your ad formats match that + experience. While banner ads may work well for a flashlight utility app, + an immersive gaming app may benefit more from a video interstitial. + Mismatched ad formats can make users unhappy and leave money on the + table. </p> </li> <li> <p> - <strong>Maximizing your performance</strong> — Ensure you’re optimizing - your advertising revenue by maximizing your CPMs <em>and</em> fill rate. - Often ad providers will cite very high CPMs but will have a low fill rate - that can severely decrease your effective CPM, so look at both of these - figures. Also consider using a <a href= + <strong>Maximizing your performance</strong> — Make sure you’re + optimizing your advertising revenue by maximizing your CPMs and fill + rate. Ad providers often cite their very high CPMs but don't mention low + fill rates that can severely decrease your effective CPM. Be sure to look + at both of these figures. Consider using a <a href= "https://support.google.com/admob/v2/answer/3063564?hl=en&ref_topic=3063091#subid=us-en-et-dac"> mediation</a> solution if you’d like to use multiple ad providers in your apps. Look for solutions that offer yield management or <a href= @@ -71,30 +83,43 @@ page.image=/distribute/images/advertising.png <li> <p> - <strong>Exercising control options</strong> — A variety of ads promoting a - broad selection of other services or apps may show up within you apps. - Depending on your goals and the type of experience you want to provide - your users, it may make sense to <a href= + <strong>Exercising control options</strong> — A variety of ads may + show up within your app. It may make sense to <a href= "https://support.google.com/admob/v2/answer/3150235?hl=enl#subid=us-en-et-dac"> - block</a> certain advertisements from appearing. Some developers don’t - want apps in a similar category showing to their users, but some don’t - mind. + block</a> certain of those advertisements from appearing, depending on + your goals and the type of experience you want to provide. Some + developers, for instance, don’t want ads for apps in their same category + showing to their users, while others don’t mind at all. </p> </li> <li> <p> - <strong>Cross promoting your other apps</strong> — Ads can be used for - more than just earning revenue. Consider using <a href= + <strong>Cross promoting your other apps</strong> — Ads can do more + than earn revenue. Consider running <a href= "https://support.google.com/admob/v2/answer/3210452?hl=en#subid=us-en-et-dac"> - house ads</a> within your apps to create awareness and promote your - entire portfolio of apps. When launching new apps, an easy way to quickly - attract users is to promote directly to your existing customers. + house ads</a> within your apps to promote other apps in your portfolio. + When you launch a new app, this kind of promotion is a free and easy way + to attract new users quickly. </p> </li> </ul> <p> + Don't forget that paid channels like AdWords and YouTube can help you cast a + wider net by reaching targeted audiences outside the app ecosystem. They're a + great way to find new users at a price that you control. <a href= + "https://support.google.com/adwords/answer/2549053">Learn more</a>. +</p> + +<p> + To start monetizing with ads, sign up for AdMob and integrate the Google + Mobile Ads SDK into your apps. If you also need to manage direct deals with + advertisers, consider using DoubleClick for Publishers Small Business. +</p> + + +<p> To start monetizing with ads sign up for <a href= "http://www.google.com/ads/admob/#subid=us-en-et-dac">AdMob</a> and integrate the <a href="https://developers.google.com/mobile-ads-sdk/download">Google diff --git a/docs/html/distribute/users/index.jd b/docs/html/distribute/users/index.jd index 77ef609..a810f36 100644 --- a/docs/html/distribute/users/index.jd +++ b/docs/html/distribute/users/index.jd @@ -14,8 +14,8 @@ nonavpage=true <div class="resource-widget resource-flow-layout landing col-16" data-query="collection:distribute/users" - data-cardSizes="6x6, 6x6, 6x6, 9x6, 9x6, 6x6, 6x6, 6x6" - data-maxResults="8"> + data-cardSizes="6x6" + data-maxResults="6"> </div> <h3>Related resources</h3> diff --git a/docs/html/distribute/users/promote-with-ads.jd b/docs/html/distribute/users/promote-with-ads.jd new file mode 100644 index 0000000..1e28ae1 --- /dev/null +++ b/docs/html/distribute/users/promote-with-ads.jd @@ -0,0 +1,45 @@ +page.title=Promote Your App with Ads +page.metaDescription=Promote your app through AdMob, AdWords, and YouTube to find new users at the right moment. +page.image=/images/gp-ads-console.jpg +page.tags="users, ads, analytics" + +@jd:body + +<p> + AdMob is Google's advertising platform for mobile apps. You can use it to + monetize your app and promote your apps, and you can link your Google + Analytics account to AdMob so you can analyze your apps — all in one + place. +</p> + +<p> + <a href="http://www.google.com/ads/admob/">AdMob</a> is the largest mobile ad + app network. But you get more than just massive scale: AdMob will soon help + you find the right users in related apps. If your app is for bicycling, AdMob + can promote your app on other fitness and cycling-related apps worldwide. + <a href= + "https://apps.admob.com/admob/signup?subid=us-en-et-dac&_adc=ww-ww-et-admob2&hl=en"> + Sign up for AdMob</a>. +</p> + +<p> + AdMob also offers new solutions to help you achieve app-related goals such as + downloads, re-engagement and in-app purchases using Google search and the + Google Display Network. These solutions include streamlined campaign creation + flows and tools to track performance across the entire app lifecycle. + <a href="https://support.google.com/adwords/answer/2549053?hl=en">Learn + More</a>. +</p> +<div style="margin-top:2em;"> + <img src="{@docRoot}images/gp-ads-console.jpg"> +</div> + +<div class="headerLine"> +<h2 id="related-resources">Related Resources</h2> +</div> + +<div class="resource-widget resource-flow-layout col-13" + data-query="collection:distribute/users/promotewithads" + data-sortOrder="-timestamp" + data-cardSizes="9x3" + data-maxResults="6"></div>
\ No newline at end of file diff --git a/docs/html/distribute/users/users_toc.cs b/docs/html/distribute/users/users_toc.cs index a2437a6..1f173cb 100644 --- a/docs/html/distribute/users/users_toc.cs +++ b/docs/html/distribute/users/users_toc.cs @@ -28,7 +28,12 @@ </a> </div> </li> - + <li class="nav-section"> + <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/users/promote-with-ads.html"> + <span class="en">Promote with Ads</span> + </a> + </div> + </li> </ul> diff --git a/docs/html/images/gp-ads-console.jpg b/docs/html/images/gp-ads-console.jpg Binary files differnew file mode 100644 index 0000000..158e31d --- /dev/null +++ b/docs/html/images/gp-ads-console.jpg diff --git a/docs/html/images/gp-ads-linking2.jpg b/docs/html/images/gp-ads-linking2.jpg Binary files differnew file mode 100644 index 0000000..0c2f731 --- /dev/null +++ b/docs/html/images/gp-ads-linking2.jpg diff --git a/docs/html/images/gp-analytics.jpg b/docs/html/images/gp-analytics.jpg Binary files differnew file mode 100644 index 0000000..e1a92c7 --- /dev/null +++ b/docs/html/images/gp-analytics.jpg diff --git a/docs/html/jd_collections.js b/docs/html/jd_collections.js index 8a4ac47..5ee01f3 100644 --- a/docs/html/jd_collections.js +++ b/docs/html/jd_collections.js @@ -49,7 +49,8 @@ var RESOURCE_COLLECTIONS = { "distribute/users/your-listing.html", "distribute/users/build-buzz.html", "distribute/users/build-community.html", - "distribute/users/expand-to-new-markets.html" + "distribute/users/expand-to-new-markets.html", + "distribute/users/promote-with-ads.html" ] }, "distribute/engagelanding": { @@ -60,6 +61,7 @@ var RESOURCE_COLLECTIONS = { "distribute/engage/easy-signin.html", "distribute/engage/deep-linking.html", "distribute/engage/game-services.html", + "distribute/engage/analytics.html", "distribute/engage/app-updates.html", "distribute/engage/community.html", "distribute/engage/video.html" @@ -214,6 +216,13 @@ var RESOURCE_COLLECTIONS = { "distribute/stories/localization.html" ] }, + "distribute/users/promotewithads": { + "title": "", + "resources": [ + "http://www.google.com/ads/admob/#subid=us-en-et-dac", + "distribute/essentials/optimizing-your-app.html" + ] + }, "distribute/users/buildbuzz": { "title": "", "resources": [ @@ -394,6 +403,14 @@ var RESOURCE_COLLECTIONS = { "http://play.google.com/about/developer-content-policy.html" ] }, + "distribute/engage/analytics": { + "title": "", + "resources": [ + "http://www.google.com/analytics/mobile/", + "http://android-developers.blogspot.com/2013/10/improved-app-insight-by-linking-google.html", + "https://developers.google.com/analytics/devguides/collection/android/" + ] + }, "distribute/engage/widgets": { "title": "", "resources": [ diff --git a/docs/html/jd_extras.js b/docs/html/jd_extras.js index f26b747..d8db5bf 100644 --- a/docs/html/jd_extras.js +++ b/docs/html/jd_extras.js @@ -726,6 +726,21 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([ "tags": [ "#engagement", ], + "url": "http://www.google.com/analytics/mobile/", + "timestamp": 1383243492000, + "image": "http://www.google.com//analytics/images/heros/mobile-index.jpg", + "title": "Google Mobile App Analytics", + "summary": "Mobile App Analytics measures what matters most at all key stages: from first discovery and download to in-app purchases. ", + "keywords": ["analytics,user behavior"], + "type": "guide", + "titleFriendly": "" + }, + { + "lang": "en", + "group": "", + "tags": [ + "#engagement", + ], "url": "https://developers.google.com/app-indexing/", "timestamp": 1383243492000, "image": "https://developers.google.com/app-indexing/images/allthecooks_srp.png", @@ -1101,4 +1116,17 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([ "type": "Google+", "titleFriendly": "" }, + { + "lang": "en", + "group": "", + "tags": ["analytics"], + "url": "https://developers.google.com/analytics/devguides/collection/android/", + "timestamp": null, + "image": "https://developers.google.com/analytics/images/home/gear-logo.png", + "title": "Google Mobile App Analytics SDK", + "summary": "The Google Analytics for Mobile Apps SDKs make it easy for you to implement Google Analytics in your mobile application.", + "keywords": ["analytics, user behavior"], + "type": "sdk", + "titleFriendly": "" + } ]);
\ No newline at end of file diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 06cf253..ef0a411 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -194,6 +194,11 @@ public final class Bitmap implements Parcelable { * while {@link #getAllocationByteCount()} will reflect that of the initial * configuration.</p> * + * <p>Note: This may change this result of hasAlpha(). When converting to 565, + * the new bitmap will always be considered opaque. When converting from 565, + * the new bitmap will be considered non-opaque, and will respect the value + * set by setPremultiplied().</p> + * * <p>WARNING: This method should NOT be called on a bitmap currently used * by the view system. It does not make guarantees about how the underlying * pixel buffer is remapped to the new config, just that the allocation is @@ -217,7 +222,8 @@ public final class Bitmap implements Parcelable { throw new IllegalStateException("native-backed bitmaps may not be reconfigured"); } - nativeReconfigure(mNativeBitmap, width, height, config.nativeInt, mBuffer.length); + nativeReconfigure(mNativeBitmap, width, height, config.nativeInt, mBuffer.length, + mIsPremultiplied); mWidth = width; mHeight = height; } @@ -1586,7 +1592,8 @@ public final class Bitmap implements Parcelable { private static native void nativeDestructor(long nativeBitmap); private static native boolean nativeRecycle(long nativeBitmap); private static native void nativeReconfigure(long nativeBitmap, int width, int height, - int config, int allocSize); + int config, int allocSize, + boolean isPremultiplied); private static native boolean nativeCompress(long nativeBitmap, int format, int quality, OutputStream stream, diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java index de458af..a021165 100644 --- a/graphics/java/android/graphics/Picture.java +++ b/graphics/java/android/graphics/Picture.java @@ -53,7 +53,7 @@ public class Picture { public Picture(Picture src) { this(nativeConstructor(src != null ? src.mNativePicture : 0), false); } - + /** * To record a picture, call beginRecording() and then draw into the Canvas * that is returned. Nothing we appear on screen, but all of the draw @@ -67,7 +67,7 @@ public class Picture { mRecordingCanvas = new RecordingCanvas(this, ni); return mRecordingCanvas; } - + /** * Call endRecording when the picture is built. After this call, the picture * may be drawn, but the canvas that was returned by beginRecording must not @@ -92,16 +92,19 @@ public class Picture { * does not reflect (per se) the content of the picture. */ public native int getHeight(); - + /** - * Draw this picture on the canvas. The picture may have the side effect - * of changing the matrix and clip of the canvas. - * + * Draw this picture on the canvas. + * <p> + * Prior to {@link android.os.Build.VERSION_CODES#L}, this call could + * have the side effect of changing the matrix and clip of the canvas + * if this picture had imbalanced saves/restores. + * * <p> * <strong>Note:</strong> This forces the picture to internally call * {@link Picture#endRecording()} in order to prepare for playback. * - * @param canvas The picture is drawn to this canvas + * @param canvas The picture is drawn to this canvas */ public void draw(Canvas canvas) { if (mRecordingCanvas != null) { @@ -119,7 +122,7 @@ public class Picture { * <p> * <strong>Note:</strong> a picture created from an input stream cannot be * replayed on a hardware accelerated canvas. - * + * * @see #writeToStream(java.io.OutputStream) * @deprecated The recommended alternative is to not use writeToStream and * instead draw the picture into a Bitmap from which you can persist it as @@ -167,7 +170,7 @@ public class Picture { final long ni() { return mNativePicture; } - + private Picture(long nativePicture, boolean fromStream) { if (nativePicture == 0) { throw new RuntimeException(); @@ -187,7 +190,7 @@ public class Picture { private static native boolean nativeWriteToStream(long nativePicture, OutputStream stream, byte[] storage); private static native void nativeDestructor(long nativePicture); - + private static class RecordingCanvas extends Canvas { private final Picture mPicture; @@ -195,7 +198,7 @@ public class Picture { super(nativeCanvas); mPicture = pict; } - + @Override public void setBitmap(Bitmap bitmap) { throw new RuntimeException( diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp index 41cc9d2..79fe4d3 100644 --- a/libs/hwui/TessellationCache.cpp +++ b/libs/hwui/TessellationCache.cpp @@ -165,12 +165,12 @@ public: ShadowTask(const Matrix4* drawTransform, const Rect& localClip, bool opaque, const SkPath* casterPerimeter, const Matrix4* transformXY, const Matrix4* transformZ, const Vector3& lightCenter, float lightRadius) - : drawTransform(drawTransform) + : drawTransform(*drawTransform) , localClip(localClip) , opaque(opaque) - , casterPerimeter(casterPerimeter) - , transformXY(transformXY) - , transformZ(transformZ) + , casterPerimeter(*casterPerimeter) + , transformXY(*transformXY) + , transformZ(*transformZ) , lightCenter(lightCenter) , lightRadius(lightRadius) { } @@ -182,14 +182,19 @@ public: delete bufferPair; } - // Note - only the localClip is deep copied, since other pointers point at Allocator controlled - // objects, which are safe for the entire frame - const Matrix4* drawTransform; + /* Note - we deep copy all task parameters, because *even though* pointers into Allocator + * controlled objects (like the SkPath and Matrix4s) should be safe for the entire frame, + * certain Allocators are destroyed before trim() is called to flush incomplete tasks. + * + * These deep copies could be avoided, long term, by cancelling or flushing outstanding tasks + * before tearning down single-frame LinearAllocators. + */ + const Matrix4 drawTransform; const Rect localClip; bool opaque; - const SkPath* casterPerimeter; - const Matrix4* transformXY; - const Matrix4* transformZ; + const SkPath casterPerimeter; + const Matrix4 transformXY; + const Matrix4 transformZ; const Vector3 lightCenter; const float lightRadius; }; @@ -281,8 +286,8 @@ public: VertexBuffer* ambientBuffer = new VertexBuffer; VertexBuffer* spotBuffer = new VertexBuffer; - tessellateShadows(t->drawTransform, &t->localClip, t->opaque, t->casterPerimeter, - t->transformXY, t->transformZ, t->lightCenter, t->lightRadius, + tessellateShadows(&t->drawTransform, &t->localClip, t->opaque, &t->casterPerimeter, + &t->transformXY, &t->transformZ, t->lightCenter, t->lightRadius, *ambientBuffer, *spotBuffer); t->setResult(new TessellationCache::vertexBuffer_pair_t(ambientBuffer, spotBuffer)); diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index ded10a1..4988f19 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -47,7 +47,7 @@ namespace renderthread { #define SETUP_TASK(method) \ LOG_ALWAYS_FATAL_IF( METHOD_INVOKE_PAYLOAD_SIZE < sizeof(ARGS(method)), \ - "METHOD_INVOKE_PAYLOAD_SIZE %d is smaller than sizeof(" #method "Args) %d", \ + "METHOD_INVOKE_PAYLOAD_SIZE %zu is smaller than sizeof(" #method "Args) %zu", \ METHOD_INVOKE_PAYLOAD_SIZE, sizeof(ARGS(method))); \ MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \ ARGS(method) *args = (ARGS(method) *) task->payload() diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index c3d5d94..ba6b214 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1653,6 +1653,25 @@ public class AudioManager { } } + + /** + * Return a new audio session identifier not associated with any player or effect. + * It can for instance be used to create one of the {@link android.media.audiofx.AudioEffect} + * objects. + * @return a new unclaimed and unused audio session identifier, or {@link #ERROR} when the + * system failed to allocate a new session. + */ + public int allocateAudioSessionId() { + int session = AudioSystem.newAudioSessionId(); + if (session > 0) { + return session; + } else { + Log.e(TAG, "Failure to allocate a new audio session ID"); + return ERROR; + } + } + + /* * Sets a generic audio configuration parameter. The use of these parameters * are platform dependant, see libaudio @@ -2998,7 +3017,8 @@ public class AudioManager { /** @hide */ public static final int SUCCESS = AudioSystem.SUCCESS; - /** @hide + /** + * A default error code. */ public static final int ERROR = AudioSystem.ERROR; /** @hide diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index c8d64ce..9fbcd18 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -130,6 +130,11 @@ public class AudioSystem public static native boolean isSourceActive(int source); /* + * Returns a new unused audio session ID + */ + public static native int newAudioSessionId(); + + /* * Sets a group generic audio configuration parameters. The use of these parameters * are platform dependent, see libaudio * diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java index 52045d3..7ff2c2e 100644 --- a/media/java/android/media/tv/TvContract.java +++ b/media/java/android/media/tv/TvContract.java @@ -272,6 +272,15 @@ public final class TvContract { /** A generic channel type. */ public static final int TYPE_OTHER = 0x0; + /** The channel type for NTSC. */ + public static final int TYPE_NTSC = 0x1; + + /** The channel type for PAL. */ + public static final int TYPE_PAL = 0x2; + + /** The channel type for SECAM. */ + public static final int TYPE_SECAM = 0x3; + /** The special channel type used for pass-through inputs such as HDMI. */ public static final int TYPE_PASSTHROUGH = 0x00010000; diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp index 157dd49..fbe5340 100644 --- a/media/jni/android_media_MediaMetadataRetriever.cpp +++ b/media/jni/android_media_MediaMetadataRetriever.cpp @@ -24,6 +24,7 @@ #include <SkBitmap.h> #include <media/IMediaHTTPService.h> #include <media/mediametadataretriever.h> +#include <media/mediascanner.h> #include <private/media/VideoFrame.h> #include "jni.h" @@ -337,17 +338,13 @@ static jbyteArray android_media_MediaMetadataRetriever_getEmbeddedPicture( return NULL; } - unsigned int len = mediaAlbumArt->mSize; - char* data = (char*) mediaAlbumArt + sizeof(MediaAlbumArt); - jbyteArray array = env->NewByteArray(len); + jbyteArray array = env->NewByteArray(mediaAlbumArt->size()); if (!array) { // OutOfMemoryError exception has already been thrown. ALOGE("getEmbeddedPicture: OutOfMemoryError is thrown."); } else { - jbyte* bytes = env->GetByteArrayElements(array, NULL); - if (bytes != NULL) { - memcpy(bytes, data, len); - env->ReleaseByteArrayElements(array, bytes, 0); - } + const jbyte* data = + reinterpret_cast<const jbyte*>(mediaAlbumArt->data()); + env->SetByteArrayRegion(array, 0, mediaAlbumArt->size(), data); } // No need to delete mediaAlbumArt here diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp index b520440..321c2e3 100644 --- a/media/jni/android_media_MediaScanner.cpp +++ b/media/jni/android_media_MediaScanner.cpp @@ -348,17 +348,16 @@ android_media_MediaScanner_extractAlbumArt( } int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); - MediaAlbumArt* mediaAlbumArt = - reinterpret_cast<MediaAlbumArt*>(mp->extractAlbumArt(fd)); + MediaAlbumArt* mediaAlbumArt = mp->extractAlbumArt(fd); if (mediaAlbumArt == NULL) { return NULL; } - jbyteArray array = env->NewByteArray(mediaAlbumArt->mSize); + jbyteArray array = env->NewByteArray(mediaAlbumArt->size()); if (array != NULL) { - jbyte* bytes = env->GetByteArrayElements(array, NULL); - memcpy(bytes, &mediaAlbumArt->mData[0], mediaAlbumArt->mSize); - env->ReleaseByteArrayElements(array, bytes, 0); + const jbyte* data = + reinterpret_cast<const jbyte*>(mediaAlbumArt->data()); + env->SetByteArrayRegion(array, 0, mediaAlbumArt->size(), data); } done: diff --git a/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png b/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png Binary files differdeleted file mode 100644 index 5ce8708..0000000 --- a/packages/SystemUI/res/drawable-hdpi/stat_sys_no_sim.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png b/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png Binary files differdeleted file mode 100644 index bdf0f67..0000000 --- a/packages/SystemUI/res/drawable-mdpi/stat_sys_no_sim.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xhdpi/stat_sys_no_sim.png b/packages/SystemUI/res/drawable-xhdpi/stat_sys_no_sim.png Binary files differdeleted file mode 100644 index 461535c..0000000 --- a/packages/SystemUI/res/drawable-xhdpi/stat_sys_no_sim.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxhdpi/stat_sys_no_sim.png b/packages/SystemUI/res/drawable-xxhdpi/stat_sys_no_sim.png Binary files differdeleted file mode 100644 index 7b03a11..0000000 --- a/packages/SystemUI/res/drawable-xxhdpi/stat_sys_no_sim.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable/ic_qs_location_01.xml b/packages/SystemUI/res/drawable/ic_notify_zen.xml index ff37d9a..c46455b 100644 --- a/packages/SystemUI/res/drawable/ic_qs_location_01.xml +++ b/packages/SystemUI/res/drawable/ic_notify_zen.xml @@ -15,14 +15,14 @@ Copyright (C) 2014 The Android Open Source Project --> <vector xmlns:android="http://schemas.android.com/apk/res/android" > <size - android:width="64dp" - android:height="64dp"/> + android:width="24dp" + android:height="24dp"/> <viewport - android:viewportWidth="24.0" - android:viewportHeight="24.0"/> + android:viewportWidth="48.0" + android:viewportHeight="48.0"/> <path android:fill="#FFFFFFFF" - android:pathData="M12.0,2.0C8.13,2.0 5.0,5.13 5.0,9.0c0.0,5.25 7.0,13.0 7.0,13.0s7.0,-7.75 7.0,-13.0C19.0,5.13 15.87,2.0 12.0,2.0zM12.0,11.5c-1.38,0.0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5c1.38,0.0 2.5,1.12 2.5,2.5S13.38,11.5 12.0,11.5z"/> + android:pathData="M4.0,24.0c0.0,11.0 9.0,20.0 20.0,20.0s20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0S4.0,13.0 4.0,24.0zM36.6,33.8L14.2,11.4C16.9,9.3 20.3,8.0 24.0,8.0c8.8,0.0 16.0,7.2 16.0,16.0C40.0,27.7 38.7,31.1 36.6,33.8zM8.0,24.0c0.0,-3.7 1.3,-7.1 3.4,-9.8L33.8,36.6C31.1,38.7 27.7,40.0 24.0,40.0C15.2,40.0 8.0,32.8 8.0,24.0z"/> </vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_color_inversion.xml b/packages/SystemUI/res/drawable/ic_qs_color_inversion.xml deleted file mode 100644 index dc30a53..0000000 --- a/packages/SystemUI/res/drawable/ic_qs_color_inversion.xml +++ /dev/null @@ -1,28 +0,0 @@ -<!-- -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. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" > - <size - android:width="64dp" - android:height="64dp"/> - - <viewport - android:viewportWidth="24.0" - android:viewportHeight="24.0"/> - - <path - android:fill="#FFFFFFFF" - android:pathData="M18.939,7.244c-5.887,-5.885 -6.214,-6.214 -6.222,-6.222l-0.707,-0.737L5.088,7.207c-2.914,2.915 -3.74,6.629 -2.266,10.19c1.541,3.719 5.312,6.316 9.174,6.317l0.0,0.0c3.861,-0.001 7.636,-2.603 9.179,-6.328C22.646,13.834 21.832,10.138 18.939,7.244zM4.67,16.632c-1.149,-2.776 -0.481,-5.696 1.832,-8.011l5.494,-5.492c0.0,0.002 0.002,0.003 0.003,0.004l0.0,18.582c-0.001,0.0 -0.002,0.0 -0.003,0.0C8.922,21.714 5.91,19.624 4.67,16.632z"/> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_location_02.xml b/packages/SystemUI/res/drawable/ic_qs_inversion_off.xml index bb4465f..b6a5cad 100644 --- a/packages/SystemUI/res/drawable/ic_qs_location_02.xml +++ b/packages/SystemUI/res/drawable/ic_qs_inversion_off.xml @@ -19,10 +19,10 @@ Copyright (C) 2014 The Android Open Source Project android:height="64dp"/> <viewport - android:viewportWidth="24.0" - android:viewportHeight="24.0"/> + android:viewportWidth="48.0" + android:viewportHeight="48.0"/> <path - android:fill="#FFFFFFFF" - android:pathData="M12.0,4.0c-3.48,0.0 -6.3,2.82 -6.3,6.3C5.7,15.02 12.0,22.0 12.0,22.0s6.3,-6.98 6.3,-11.7C18.3,6.82 15.48,4.0 12.0,4.0zM12.0,12.55c-1.24,0.0 -2.25,-1.01 -2.25,-2.25S10.76,8.05 12.0,8.05c1.24,0.0 2.25,1.01 2.25,2.25S13.24,12.55 12.0,12.55z"/> + android:fill="#4DFFFFFF" + android:pathData="M41.3,41.7L36.6,37.0L24.0,24.5l-7.1,-7.1L14.0,14.5L8.5,9.0L6.0,11.5l5.6,5.6c-5.1,6.3 -4.7,15.5 1.1,21.4c3.1,3.1 7.2,4.7 11.3,4.7c3.6,0.0 7.1,-1.2 10.1,-3.6l5.4,5.4l2.5,-2.5L41.3,41.7zM24.0,39.2c-3.2,0.0 -6.2,-1.2 -8.5,-3.5c-2.3,-2.3 -3.5,-5.3 -3.5,-8.5c0.0,-2.6 0.9,-5.1 2.4,-7.2l9.6,9.6L24.0,39.2zM24.0,10.2l0.0,9.2l14.5,14.5c2.7,-5.9 1.7,-13.1 -3.2,-18.0L24.0,4.5l0.0,0.0l0.0,0.0L16.6,12.0l2.8,2.8L24.0,10.2z"/> </vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_location_04.xml b/packages/SystemUI/res/drawable/ic_qs_inversion_on.xml index 0c0fb3b..e8d59e0 100644 --- a/packages/SystemUI/res/drawable/ic_qs_location_04.xml +++ b/packages/SystemUI/res/drawable/ic_qs_inversion_on.xml @@ -19,10 +19,10 @@ Copyright (C) 2014 The Android Open Source Project android:height="64dp"/> <viewport - android:viewportWidth="24.0" - android:viewportHeight="24.0"/> + android:viewportWidth="48.0" + android:viewportHeight="48.0"/> <path android:fill="#FFFFFFFF" - android:pathData="M12.0,10.0c-2.32,0.0 -4.2,1.88 -4.2,4.2C7.8,17.35 12.0,22.0 12.0,22.0s4.2,-4.65 4.2,-7.8C16.2,11.88 14.32,10.0 12.0,10.0zM12.0,15.7c-0.83,0.0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5c0.83,0.0 1.5,0.67 1.5,1.5S12.83,15.7 12.0,15.7z"/> + android:pathData="M35.3,15.9L24.0,4.5l0.0,0.0l0.0,0.0L12.7,15.9c-6.2,6.2 -6.2,16.4 0.0,22.6c3.1,3.1 7.2,4.7 11.3,4.7s8.2,-1.6 11.3,-4.7C41.6,32.2 41.6,22.1 35.3,15.9zM24.0,39.2L24.0,39.2c-3.2,0.0 -6.2,-1.2 -8.5,-3.5c-2.3,-2.3 -3.5,-5.3 -3.5,-8.5s1.2,-6.2 3.5,-8.5l8.5,-8.5L24.0,39.2z"/> </vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_location_03.xml b/packages/SystemUI/res/drawable/ic_qs_location_03.xml deleted file mode 100644 index 956a8c3..0000000 --- a/packages/SystemUI/res/drawable/ic_qs_location_03.xml +++ /dev/null @@ -1,28 +0,0 @@ -<!-- -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. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" > - <size - android:width="64dp" - android:height="64dp"/> - - <viewport - android:viewportWidth="24.0" - android:viewportHeight="24.0"/> - - <path - android:fill="#FFFFFFFF" - android:pathData="M12.0,7.0c-2.9,0.0 -5.25,2.35 -5.25,5.25C6.75,16.19 12.0,22.0 12.0,22.0s5.25,-5.81 5.25,-9.75C17.25,9.35 14.9,7.0 12.0,7.0zM12.0,14.12c-1.04,0.0 -1.88,-0.84 -1.88,-1.88s0.84,-1.88 1.88,-1.88c1.04,0.0 1.87,0.84 1.87,1.88S13.04,14.12 12.0,14.12z"/> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_location_05.xml b/packages/SystemUI/res/drawable/ic_qs_location_05.xml deleted file mode 100644 index 1a21e2f..0000000 --- a/packages/SystemUI/res/drawable/ic_qs_location_05.xml +++ /dev/null @@ -1,28 +0,0 @@ -<!-- -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. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" > - <size - android:width="64dp" - android:height="64dp"/> - - <viewport - android:viewportWidth="24.0" - android:viewportHeight="24.0"/> - - <path - android:fill="#FFFFFFFF" - android:pathData="M12.0,13.0c-1.74,0.0 -3.15,1.41 -3.15,3.15C8.85,18.51 12.0,22.0 12.0,22.0s3.15,-3.49 3.15,-5.85C15.15,14.41 13.74,13.0 12.0,13.0zM12.0,17.27c-0.62,0.0 -1.13,-0.5 -1.13,-1.12c0.0,-0.62 0.5,-1.12 1.13,-1.12c0.62,0.0 1.12,0.5 1.12,1.12C13.12,16.77 12.62,17.27 12.0,17.27z"/> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_location_06.xml b/packages/SystemUI/res/drawable/ic_qs_location_06.xml deleted file mode 100644 index 5642a8a..0000000 --- a/packages/SystemUI/res/drawable/ic_qs_location_06.xml +++ /dev/null @@ -1,31 +0,0 @@ -<!-- -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. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" > - <size - android:width="64dp" - android:height="64dp"/> - - <viewport - android:viewportWidth="24.0" - android:viewportHeight="24.0"/> - - <path - android:fill="#FFFFFFFF" - android:pathData="M12.0,16.0c-1.16,0.0 -2.1,0.94 -2.1,2.1C9.9,19.67 12.0,22.0 12.0,22.0s2.1,-2.33 2.1,-3.9C14.1,16.94 13.16,16.0 12.0,16.0zM12.0,18.85c-0.41,0.0 -0.75,-0.34 -0.75,-0.75s0.34,-0.75 0.75,-0.75c0.41,0.0 0.75,0.34 0.75,0.75S12.41,18.85 12.0,18.85z"/> - <path - android:pathData="M11.99,15c-1.35,0,-2.45,1.1,-2.45,2.45 c0,1.84,2.45,4.55,2.45,4.55s2.45,-2.71,2.45,-4.55C14.44,16.1,13.34,15,11.99,15z M11.99,18.33c-0.48,0,-0.88,-0.39,-0.88,-0.88 s0.39,-0.88,0.88,-0.88c0.48,0,0.87,0.39,0.87,0.88S12.47,18.33,11.99,18.33z" - android:fill="#4DFFFFFF"/> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_location_07.xml b/packages/SystemUI/res/drawable/ic_qs_location_07.xml deleted file mode 100644 index 1ad2ebc..0000000 --- a/packages/SystemUI/res/drawable/ic_qs_location_07.xml +++ /dev/null @@ -1,28 +0,0 @@ -<!-- -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. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" > - <size - android:width="64dp" - android:height="64dp"/> - - <viewport - android:viewportWidth="24.0" - android:viewportHeight="24.0"/> - - <path - android:pathData="M12,9c-2.51,0,-4.55,2.04,-4.55,4.55 C7.45,16.96,12,22,12,22s4.55,-5.04,4.55,-8.45C16.55,11.04,14.51,9,12,9z M12,15.18c-0.9,0,-1.63,-0.73,-1.63,-1.62 s0.73,-1.62,1.63,-1.62c0.9,0,1.62,0.73,1.62,1.62S12.9,15.18,12,15.18z" - android:fill="#4DFFFFFF"/> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_location_08.xml b/packages/SystemUI/res/drawable/ic_qs_location_08.xml deleted file mode 100644 index 179bc66..0000000 --- a/packages/SystemUI/res/drawable/ic_qs_location_08.xml +++ /dev/null @@ -1,28 +0,0 @@ -<!-- -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. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" > - <size - android:width="64dp" - android:height="64dp"/> - - <viewport - android:viewportWidth="24.0" - android:viewportHeight="24.0"/> - - <path - android:pathData="M12,6c-3.09,0,-5.6,2.51,-5.6,5.6 C6.4,15.8,12,22,12,22s5.6,-6.2,5.6,-10.4C17.6,8.51,15.09,6,12,6z M12,13.6c-1.1,0,-2,-0.9,-2,-2s0.9,-2,2,-2c1.1,0,2,0.9,2,2 S13.1,13.6,12,13.6z" - android:fill="#4DFFFFFF"/> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_location_09.xml b/packages/SystemUI/res/drawable/ic_qs_location_09.xml deleted file mode 100644 index 6169af5..0000000 --- a/packages/SystemUI/res/drawable/ic_qs_location_09.xml +++ /dev/null @@ -1,28 +0,0 @@ -<!-- -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. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" > - <size - android:width="64dp" - android:height="64dp"/> - - <viewport - android:viewportWidth="24.0" - android:viewportHeight="24.0"/> - - <path - android:pathData="M12,4c-3.48,0,-6.3,2.82,-6.3,6.3 C5.7,15.02,12,22,12,22s6.3,-6.98,6.3,-11.7C18.3,6.82,15.48,4,12,4z M12,12.55c-1.24,0,-2.25,-1.01,-2.25,-2.25S10.76,8.05,12,8.05 c1.24,0,2.25,1.01,2.25,2.25S13.24,12.55,12,12.55z" - android:fill="#4DFFFFFF"/> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_location_10.xml b/packages/SystemUI/res/drawable/ic_qs_location_10.xml deleted file mode 100644 index 93e2eb4..0000000 --- a/packages/SystemUI/res/drawable/ic_qs_location_10.xml +++ /dev/null @@ -1,28 +0,0 @@ -<!-- -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. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" > - <size - android:width="64dp" - android:height="64dp"/> - - <viewport - android:viewportWidth="24.0" - android:viewportHeight="24.0"/> - - <path - android:pathData="M12,3C8.33,3,5.35,5.98,5.35,9.65 C5.35,14.64,12,22,12,22s6.65,-7.36,6.65,-12.35C18.65,5.98,15.67,3,12,3z M12,12.02c-1.31,0,-2.38,-1.06,-2.38,-2.38 S10.69,7.28,12,7.28c1.31,0,2.37,1.06,2.37,2.37S13.31,12.02,12,12.02z" - android:fill="#4DFFFFFF"/> -</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_location_off.xml b/packages/SystemUI/res/drawable/ic_qs_location_off.xml index d28d347..26ebfbf 100644 --- a/packages/SystemUI/res/drawable/ic_qs_location_off.xml +++ b/packages/SystemUI/res/drawable/ic_qs_location_off.xml @@ -1,4 +1,3 @@ -<?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2014 The Android Open Source Project @@ -14,18 +13,19 @@ Copyright (C) 2014 The Android Open Source Project See the License for the specific language governing permissions and limitations under the License. --> -<animation-list - xmlns:android="http://schemas.android.com/apk/res/android" - android:oneshot="true"> - <item android:drawable="@drawable/ic_qs_location_01" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_02" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_03" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_04" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_05" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_06" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_07" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_08" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_09" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_10" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_11" android:duration="16" /> -</animation-list> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="48.0" + android:viewportHeight="48.0"/> + + <path + android:fill="#4DFFFFFF" + android:pathData="M24.0,13.0c2.8,0.0 5.0,2.2 5.0,5.0c0.0,1.5 -0.7,2.8 -1.7,3.7l7.3,7.3c2.0,-3.7 3.4,-7.6 3.4,-11.0c0.0,-7.7 -6.3,-14.0 -14.0,-14.0c-4.0,0.0 -7.5,1.6 -10.1,4.3l6.4,6.4C21.2,13.6 22.5,13.0 24.0,13.0zM32.7,32.2l-9.3,-9.3l-0.2,-0.2L6.5,6.0L4.0,8.5l6.4,6.4c-0.2,1.0 -0.4,2.0 -0.4,3.1c0.0,10.5 14.0,26.0 14.0,26.0s3.3,-3.7 6.8,-8.7l6.7,6.7l2.5,-2.5L32.7,32.2z"/> + <path + android:pathData="M23.5,22.9l0.0,0.0 -0.20000076,-0.19999886z" + android:fill="#4DFFFFFF"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_location_on.xml b/packages/SystemUI/res/drawable/ic_qs_location_on.xml index 72512ac..bc73005 100644 --- a/packages/SystemUI/res/drawable/ic_qs_location_on.xml +++ b/packages/SystemUI/res/drawable/ic_qs_location_on.xml @@ -1,4 +1,3 @@ -<?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2014 The Android Open Source Project @@ -14,18 +13,16 @@ Copyright (C) 2014 The Android Open Source Project See the License for the specific language governing permissions and limitations under the License. --> -<animation-list - xmlns:android="http://schemas.android.com/apk/res/android" - android:oneshot="true"> - <item android:drawable="@drawable/ic_qs_location_11" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_10" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_09" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_08" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_07" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_06" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_05" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_04" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_03" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_02" android:duration="16" /> - <item android:drawable="@drawable/ic_qs_location_01" android:duration="16" /> -</animation-list> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64dp" + android:height="64dp"/> + + <viewport + android:viewportWidth="48.0" + android:viewportHeight="48.0"/> + + <path + android:fill="#FFFFFFFF" + android:pathData="M24.0,4.0c-7.7,0.0 -14.0,6.3 -14.0,14.0c0.0,10.5 14.0,26.0 14.0,26.0s14.0,-15.5 14.0,-26.0C38.0,10.3 31.7,4.0 24.0,4.0zM24.0,23.0c-2.8,0.0 -5.0,-2.2 -5.0,-5.0s2.2,-5.0 5.0,-5.0c2.8,0.0 5.0,2.2 5.0,5.0S26.8,23.0 24.0,23.0z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_qs_location_11.xml b/packages/SystemUI/res/drawable/stat_sys_no_sim.xml index 09a3e63..70948b7 100644 --- a/packages/SystemUI/res/drawable/ic_qs_location_11.xml +++ b/packages/SystemUI/res/drawable/stat_sys_no_sim.xml @@ -15,14 +15,14 @@ Copyright (C) 2014 The Android Open Source Project --> <vector xmlns:android="http://schemas.android.com/apk/res/android" > <size - android:width="64dp" - android:height="64dp"/> + android:width="18dp" + android:height="18dp"/> <viewport android:viewportWidth="24.0" android:viewportHeight="24.0"/> <path - android:pathData="M12,2C8.13,2,5,5.13,5,9c0,5.25,7,13,7,13s7,-7.75,7,-13 C19,5.13,15.87,2,12,2z M12,11.5c-1.38,0,-2.5,-1.12,-2.5,-2.5s1.12,-2.5,2.5,-2.5c1.38,0,2.5,1.12,2.5,2.5S13.38,11.5,12,11.5z" - android:fill="#4DFFFFFF"/> + android:fill="#4DFFFFFF" + android:pathData="M19.0,5.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0l-7.0,0.0L7.7,5.3L19.0,16.7L19.0,5.0zM3.7,3.9L2.4,5.2L5.0,7.8L5.0,19.0c0.0,1.1 0.9,2.0 2.0,2.0l10.0,0.0c0.4,0.0 0.7,-0.1 1.0,-0.3l1.9,1.9l1.3,-1.3L3.7,3.9z"/> </vector> diff --git a/packages/SystemUI/res/layout/status_bar_notification_speed_bump.xml b/packages/SystemUI/res/layout/status_bar_notification_speed_bump.xml index 84d64b9..e220a16 100644 --- a/packages/SystemUI/res/layout/status_bar_notification_speed_bump.xml +++ b/packages/SystemUI/res/layout/status_bar_notification_speed_bump.xml @@ -18,48 +18,13 @@ <com.android.systemui.statusbar.SpeedBumpView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="match_parent" - android:focusable="true" - android:clickable="true" + android:layout_height="@dimen/speed_bump_height" android:visibility="gone" > - <LinearLayout - android:layout_width="match_parent" - android:layout_height="@dimen/speed_bump_height_collapsed" - android:layout_gravity="top" - android:orientation="horizontal"> - <com.android.systemui.statusbar.AlphaOptimizedView - android:id="@+id/speedbump_line_left" - android:layout_width="0dp" - android:layout_height="1dp" - android:layout_weight="1" - android:background="#6fdddddd" - android:layout_gravity="center_vertical"/> - <com.android.systemui.statusbar.SpeedBumpDotsLayout - android:id="@+id/speed_bump_dots_layout" - android:layout_width="34dp" - android:layout_marginStart="8dp" - android:layout_marginEnd="8dp" - android:layout_height="match_parent" - android:layout_weight="0"/> - <com.android.systemui.statusbar.AlphaOptimizedView - android:id="@+id/speedbump_line_right" - android:layout_width="0dp" - android:layout_height="1dp" - android:layout_weight="1" - android:background="#6fdddddd" - android:layout_gravity="center_vertical"/> - </LinearLayout> - <TextView - android:id="@+id/speed_bump_text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="top|center_horizontal" - android:fontFamily="sans-serif-condensed" - android:textSize="15sp" - android:singleLine="true" - android:textColor="#eeeeee" - android:visibility="invisible" - android:text="@string/speed_bump_explanation" - android:paddingTop="4dp" /> + <com.android.systemui.statusbar.AlphaOptimizedView + android:id="@+id/speedbump_line" + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="#6fdddddd" + android:layout_gravity="center_vertical"/> </com.android.systemui.statusbar.SpeedBumpView> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index bb6f1a9..8c1a9c7 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -45,18 +45,6 @@ <!-- Tint color for the content on the notification overflow card. --> <color name="keyguard_overflow_content_color">#ff686868</color> - <!-- The color of the red speed bump dot --> - <color name="speed_bump_dot_red">#ffd50000</color> - - <!-- The color of the blue speed bump dot --> - <color name="speed_bump_dot_blue">#ff2962ff</color> - - <!-- The color of the yellow speed bump dot --> - <color name="speed_bump_dot_yellow">#ffffd600</color> - - <!-- The color of the green speed bump dot --> - <color name="speed_bump_dot_green">#ff00c853</color> - <!-- The default recents task bar background color. --> <color name="recents_task_bar_default_background_color">#e6444444</color> <!-- The default recents task bar text color. --> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 35bc7e3..1664c8a 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -275,14 +275,8 @@ <!-- The minimum amount of top overscroll to go to the quick settings. --> <dimen name="min_top_overscroll_to_qs">36dp</dimen> - <!-- The height of the collapsed speed bump view. --> - <dimen name="speed_bump_height_collapsed">24dp</dimen> - - <!-- The padding inset the explanation text needs compared to the collapsed height --> - <dimen name="speed_bump_text_padding_inset">10dp</dimen> - - <!-- The height of the speed bump dots. --> - <dimen name="speed_bump_dots_height">5dp</dimen> + <!-- The height of the speed bump view. --> + <dimen name="speed_bump_height">16dp</dimen> <!-- The total height of the stack in its collapsed size (i.e. when quick settings is open) --> <dimen name="collapsed_stack_height">94dp</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java index 0fb0f8b..192ba57 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java @@ -17,13 +17,16 @@ package com.android.systemui.power; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.database.ContentObserver; import android.os.BatteryManager; import android.os.Handler; import android.os.PowerManager; import android.os.SystemClock; +import android.os.UserHandle; import android.provider.Settings; import android.util.Slog; @@ -54,17 +57,22 @@ public class PowerUI extends SystemUI { public void start() { - mLowBatteryAlertCloseLevel = mContext.getResources().getInteger( - com.android.internal.R.integer.config_lowBatteryCloseWarningLevel); - mLowBatteryReminderLevels[0] = mContext.getResources().getInteger( - com.android.internal.R.integer.config_lowBatteryWarningLevel); - mLowBatteryReminderLevels[1] = mContext.getResources().getInteger( - com.android.internal.R.integer.config_criticalBatteryWarningLevel); - final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mScreenOffTime = pm.isScreenOn() ? -1 : SystemClock.elapsedRealtime(); mWarnings = new PowerDialogWarnings(mContext); + ContentObserver obs = new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange) { + updateBatteryWarningLevels(); + } + }; + final ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL), + false, obs, UserHandle.USER_ALL); + updateBatteryWarningLevels(); + // Register for Intent broadcasts for... IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_BATTERY_CHANGED); @@ -73,6 +81,29 @@ public class PowerUI extends SystemUI { mContext.registerReceiver(mIntentReceiver, filter, null, mHandler); } + void updateBatteryWarningLevels() { + int critLevel = mContext.getResources().getInteger( + com.android.internal.R.integer.config_criticalBatteryWarningLevel); + + final ContentResolver resolver = mContext.getContentResolver(); + int defWarnLevel = mContext.getResources().getInteger( + com.android.internal.R.integer.config_lowBatteryWarningLevel); + int warnLevel = Settings.Global.getInt(resolver, + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel); + if (warnLevel == 0) { + warnLevel = defWarnLevel; + } + if (warnLevel < critLevel) { + warnLevel = critLevel; + } + + mLowBatteryReminderLevels[0] = warnLevel; + mLowBatteryReminderLevels[1] = critLevel; + mLowBatteryAlertCloseLevel = mLowBatteryReminderLevels[0] + + mContext.getResources().getInteger( + com.android.internal.R.integer.config_lowBatteryCloseWarningBump); + } + /** * Buckets the battery level. * @@ -87,7 +118,7 @@ public class PowerUI extends SystemUI { if (level >= mLowBatteryAlertCloseLevel) { return 1; } - if (level >= mLowBatteryReminderLevels[0]) { + if (level > mLowBatteryReminderLevels[0]) { return 0; } final int N = mLowBatteryReminderLevels.length; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java index c76ee8c..786cd9e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java @@ -287,6 +287,7 @@ public abstract class QSTile<TState extends State> implements Listenable { public boolean activityIn; public boolean activityOut; public int overlayIconId; + public boolean filter; @Override public boolean copyTo(State other) { @@ -300,6 +301,7 @@ public abstract class QSTile<TState extends State> implements Listenable { o.activityIn = activityIn; o.activityOut = activityOut; o.overlayIconId = overlayIconId; + o.filter = filter; return super.copyTo(other) || changed; } @@ -311,6 +313,7 @@ public abstract class QSTile<TState extends State> implements Listenable { rt.insert(rt.length() - 1, ",activityIn=" + activityIn); rt.insert(rt.length() - 1, ",activityOut=" + activityOut); rt.insert(rt.length() - 1, ",overlayIconId=" + overlayIconId); + rt.insert(rt.length() - 1, ",filter=" + filter); return rt; } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java index 901cc10..d5fe033 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/SignalTileView.java @@ -93,12 +93,12 @@ public final class SignalTileView extends QSTileView { final SignalState s = (SignalState) state; mSignal.setImageDrawable(null); // force refresh mSignal.setImageResource(s.iconId); - mSignal.setColorFilter(FILTER); + mSignal.setColorFilter(s.filter ? FILTER : null); if (s.overlayIconId > 0) { mOverlay.setVisibility(VISIBLE); mOverlay.setImageDrawable(null); // force refresh mOverlay.setImageResource(s.overlayIconId); - mOverlay.setColorFilter(FILTER); + mOverlay.setColorFilter(s.filter ? FILTER : null); } else { mOverlay.setVisibility(GONE); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java index 182a0ce..6d91d33 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java @@ -72,12 +72,15 @@ public class CellularTile extends QSTile<QSTile.SignalState> { if (cb == null) return; final Resources r = mContext.getResources(); - state.iconId = cb.enabled && (cb.mobileSignalIconId > 0) + state.iconId = cb.noSim + ? R.drawable.stat_sys_no_sim + : cb.enabled && (cb.mobileSignalIconId > 0) ? cb.mobileSignalIconId : R.drawable.ic_qs_signal_no_signal; state.overlayIconId = cb.enabled && (cb.dataTypeIconId > 0) && !cb.wifiEnabled ? cb.dataTypeIconId : 0; + state.filter = state.iconId != R.drawable.stat_sys_no_sim; state.activityIn = cb.enabled && cb.activityIn; state.activityOut = cb.enabled && cb.activityOut; @@ -117,6 +120,7 @@ public class CellularTile extends QSTile<QSTile.SignalState> { boolean activityIn; boolean activityOut; String enabledDesc; + boolean noSim; } private final NetworkSignalChangedCallback mCallback = new NetworkSignalChangedCallback() { @@ -134,7 +138,7 @@ public class CellularTile extends QSTile<QSTile.SignalState> { int mobileSignalIconId, String mobileSignalContentDescriptionId, int dataTypeIconId, boolean activityIn, boolean activityOut, - String dataTypeContentDescriptionId, String description) { + String dataTypeContentDescriptionId, String description, boolean noSim) { final CallbackInfo info = new CallbackInfo(); // TODO pool? info.enabled = enabled; info.wifiEnabled = mWifiEnabled; @@ -145,6 +149,7 @@ public class CellularTile extends QSTile<QSTile.SignalState> { info.activityIn = activityIn; info.activityOut = activityOut; info.enabledDesc = description; + info.noSim = noSim; refreshState(info); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java index 5301362..7c2c7c3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java @@ -71,6 +71,6 @@ public class ColorInversionTile extends QSTile<QSTile.BooleanState> { state.visible = mVisible; state.value = enabled; state.label = mContext.getString(R.string.quick_settings_inversion_label); - state.iconId = R.drawable.ic_qs_color_inversion; + state.iconId = enabled ? R.drawable.ic_qs_inversion_on : R.drawable.ic_qs_inversion_off; } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java index db9b054..04f1eb5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java @@ -16,9 +16,6 @@ package com.android.systemui.qs.tiles; -import android.content.res.Resources; -import android.graphics.drawable.AnimationDrawable; - import com.android.systemui.R; import com.android.systemui.qs.QSTile; import com.android.systemui.statusbar.policy.LocationController; @@ -63,28 +60,15 @@ public class LocationTile extends QSTile<QSTile.BooleanState> { protected void handleUpdateState(BooleanState state, Object arg) { final boolean locationEnabled = mController.isLocationEnabled(); state.visible = true; - if (state.value != locationEnabled) { - state.value = locationEnabled; - final Resources res = mContext.getResources(); - final AnimationDrawable d = (AnimationDrawable) res.getDrawable(locationEnabled - ? R.drawable.ic_qs_location_on - : R.drawable.ic_qs_location_off); - state.icon = d; - mUiHandler.post(new Runnable() { - @Override - public void run() { - d.start(); - } - }); - } + state.value = locationEnabled; if (locationEnabled) { - if (state.icon == null) state.iconId = R.drawable.ic_qs_location_01; + state.iconId = R.drawable.ic_qs_location_on; state.label = mContext.getString(R.string.quick_settings_location_label); state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_location, mContext.getString(R.string.accessibility_desc_on)); } else { - if (state.icon == null) state.iconId = R.drawable.ic_qs_location_11; + state.iconId = R.drawable.ic_qs_location_off; state.label = mContext.getString(R.string.quick_settings_location_label); state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_location, diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java index 6b73002..a236497 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java @@ -87,6 +87,7 @@ public class WifiTile extends QSTile<QSTile.SignalState> { state.connected = wifiConnected; state.activityIn = cb.enabled && cb.activityIn; state.activityOut = cb.enabled && cb.activityOut; + state.filter = true; final String signalContentDescription; final Resources r = mContext.getResources(); if (wifiConnected) { @@ -159,7 +160,7 @@ public class WifiTile extends QSTile<QSTile.SignalState> { int mobileSignalIconId, String mobileSignalContentDescriptionId, int dataTypeIconId, boolean activityIn, boolean activityOut, - String dataTypeContentDescriptionId, String description) { + String dataTypeContentDescriptionId, String description, boolean noSim) { // noop } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java index b91e129..8d19f50 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java @@ -158,7 +158,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private final Runnable mTapTimeoutRunnable = new Runnable() { @Override public void run() { - makeInactive(); + makeInactive(true /* animate */); } }; @@ -183,7 +183,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView break; case MotionEvent.ACTION_MOVE: if (!isWithinTouchSlop(event)) { - makeInactive(); + makeInactive(true /* animate */); return false; } break; @@ -193,14 +193,17 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView makeActive(); postDelayed(mTapTimeoutRunnable, DOUBLETAP_TIMEOUT_MS); } else { - performClick(); + boolean performed = performClick(); + if (performed) { + removeCallbacks(mTapTimeoutRunnable); + } } } else { - makeInactive(); + makeInactive(true /* animate */); } break; case MotionEvent.ACTION_CANCEL: - makeInactive(); + makeInactive(true /* animate */); break; default: break; @@ -257,10 +260,14 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView /** * Cancels the hotspot and makes the notification inactive. */ - private void makeInactive() { + public void makeInactive(boolean animate) { if (mActivated) { if (mDimmed) { - startActivateAnimation(true /* reverse */); + if (animate) { + startActivateAnimation(true /* reverse */); + } else { + mBackgroundNormal.setVisibility(View.INVISIBLE); + } } mActivated = false; } @@ -351,6 +358,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView mBackgroundDimmed.setVisibility(View.INVISIBLE); mBackgroundNormal.setVisibility(View.VISIBLE); mBackgroundNormal.setAlpha(1f); + removeCallbacks(mTapTimeoutRunnable); } } @@ -581,7 +589,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } public interface OnActivatedListener { - void onActivated(View view); - void onActivationReset(View view); + void onActivated(ActivatableNotificationView view); + void onActivationReset(ActivatableNotificationView view); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 508c34e..5a9fe91 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -48,7 +48,7 @@ import android.provider.Settings; import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; import android.service.notification.NotificationListenerService; -import android.service.notification.NotificationListenerService.Ranking; +import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.util.Log; @@ -80,11 +80,10 @@ import com.android.systemui.SearchPanelView; import com.android.systemui.SystemUI; import com.android.systemui.statusbar.NotificationData.Entry; import com.android.systemui.statusbar.phone.KeyguardTouchDelegate; +import com.android.systemui.statusbar.policy.HeadsUpNotificationView; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.Locale; import static com.android.keyguard.KeyguardHostView.OnDismissAction; @@ -106,6 +105,7 @@ public abstract class BaseStatusBar extends SystemUI implements protected static final int MSG_SHOW_HEADS_UP = 1026; protected static final int MSG_HIDE_HEADS_UP = 1027; protected static final int MSG_ESCALATE_HEADS_UP = 1028; + protected static final int MSG_DECAY_HEADS_UP = 1029; protected static final boolean ENABLE_HEADS_UP = true; // scores above this threshold should be displayed in heads up mode. @@ -129,7 +129,9 @@ public abstract class BaseStatusBar extends SystemUI implements protected NotificationData mNotificationData = new NotificationData(); protected NotificationStackScrollLayout mStackScroller; - protected NotificationData.Entry mInterruptingNotificationEntry; + // for heads up notifications + protected HeadsUpNotificationView mHeadsUpNotificationView; + protected int mHeadsUpNotificationDecay; protected long mInterruptingNotificationTime; // used to notify status bar for suppressing notification LED @@ -291,7 +293,7 @@ public abstract class BaseStatusBar extends SystemUI implements public void onListenerConnected() { if (DEBUG) Log.d(TAG, "onListenerConnected"); final StatusBarNotification[] notifications = getActiveNotifications(); - final Ranking currentRanking = getCurrentRanking(); + final RankingMap currentRanking = getCurrentRanking(); mHandler.post(new Runnable() { @Override public void run() { @@ -303,41 +305,40 @@ public abstract class BaseStatusBar extends SystemUI implements } @Override - public void onNotificationPosted(final StatusBarNotification sbn) { + public void onNotificationPosted(final StatusBarNotification sbn, + final RankingMap rankingMap) { if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn); - final Ranking currentRanking = getCurrentRanking(); mHandler.post(new Runnable() { @Override public void run() { if (mNotificationData.findByKey(sbn.getKey()) != null) { - updateNotificationInternal(sbn, currentRanking); + updateNotificationInternal(sbn, rankingMap); } else { - addNotificationInternal(sbn, currentRanking); + addNotificationInternal(sbn, rankingMap); } } }); } @Override - public void onNotificationRemoved(final StatusBarNotification sbn) { + public void onNotificationRemoved(final StatusBarNotification sbn, + final RankingMap rankingMap) { if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn); - final Ranking currentRanking = getCurrentRanking(); mHandler.post(new Runnable() { @Override public void run() { - removeNotificationInternal(sbn.getKey(), currentRanking); + removeNotificationInternal(sbn.getKey(), rankingMap); } }); } @Override - public void onNotificationRankingUpdate() { + public void onNotificationRankingUpdate(final RankingMap rankingMap) { if (DEBUG) Log.d(TAG, "onRankingUpdate"); - final Ranking currentRanking = getCurrentRanking(); mHandler.post(new Runnable() { @Override public void run() { - updateRankingInternal(currentRanking); + updateRankingInternal(rankingMap); } }); } @@ -505,8 +506,8 @@ public abstract class BaseStatusBar extends SystemUI implements protected View updateNotificationVetoButton(View row, StatusBarNotification n) { View vetoButton = row.findViewById(R.id.veto); - if (n.isClearable() || (mInterruptingNotificationEntry != null - && mInterruptingNotificationEntry.row == row)) { + if (n.isClearable() || (mHeadsUpNotificationView.getEntry() != null + && mHeadsUpNotificationView.getEntry().row == row)) { final String _pkg = n.getPackageName(); final String _tag = n.getTag(); final int _id = n.getId(); @@ -765,6 +766,12 @@ public abstract class BaseStatusBar extends SystemUI implements public abstract void resetHeadsUpDecayTimer(); + public abstract void scheduleHeadsUpOpen(); + + public abstract void scheduleHeadsUpClose(); + + public abstract void scheduleHeadsUpEscalation(); + /** * Save the current "public" (locked and secure) state of the lockscreen. */ @@ -796,6 +803,18 @@ public abstract class BaseStatusBar extends SystemUI implements return mUsersAllowingPrivateNotifications.get(userHandle); } + public void onNotificationClear(StatusBarNotification notification) { + try { + mBarService.onNotificationClear( + notification.getPackageName(), + notification.getTag(), + notification.getId(), + notification.getUserId()); + } catch (android.os.RemoteException ex) { + // oh well + } + } + protected class H extends Handler { public void handleMessage(Message m) { Intent intent; @@ -1101,7 +1120,7 @@ public abstract class BaseStatusBar extends SystemUI implements try { if (mIsHeadsUp) { - mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP); + mHeadsUpNotificationView.clear(); } mBarService.onNotificationClick(mNotificationKey); } catch (RemoteException ex) { @@ -1157,7 +1176,7 @@ public abstract class BaseStatusBar extends SystemUI implements } } - protected StatusBarNotification removeNotificationViews(String key, Ranking ranking) { + protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) { NotificationData.Entry entry = mNotificationData.remove(key, ranking); if (entry == null) { Log.w(TAG, "removeNotification for unknown key: " + key); @@ -1197,7 +1216,7 @@ public abstract class BaseStatusBar extends SystemUI implements return entry; } - protected void addNotificationViews(Entry entry, Ranking ranking) { + protected void addNotificationViews(Entry entry, RankingMap ranking) { if (entry == null) { return; } @@ -1206,7 +1225,7 @@ public abstract class BaseStatusBar extends SystemUI implements updateNotifications(); } - private void addNotificationViews(StatusBarNotification notification, Ranking ranking) { + private void addNotificationViews(StatusBarNotification notification, RankingMap ranking) { addNotificationViews(createNotificationViews(notification), ranking); } @@ -1292,9 +1311,9 @@ public abstract class BaseStatusBar extends SystemUI implements } public abstract void addNotificationInternal(StatusBarNotification notification, - Ranking ranking); + RankingMap ranking); - protected abstract void updateRankingInternal(Ranking ranking); + protected abstract void updateRankingInternal(RankingMap ranking); @Override public void removeNotification(String key) { @@ -1303,7 +1322,7 @@ public abstract class BaseStatusBar extends SystemUI implements } } - public abstract void removeNotificationInternal(String key, Ranking ranking); + public abstract void removeNotificationInternal(String key, RankingMap ranking); public void updateNotification(StatusBarNotification notification) { if (!USE_NOTIFICATION_LISTENER) { @@ -1311,7 +1330,7 @@ public abstract class BaseStatusBar extends SystemUI implements } } - public void updateNotificationInternal(StatusBarNotification notification, Ranking ranking) { + public void updateNotificationInternal(StatusBarNotification notification, RankingMap ranking) { if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")"); final NotificationData.Entry oldEntry = mNotificationData.findByKey(notification.getKey()); @@ -1394,15 +1413,15 @@ public abstract class BaseStatusBar extends SystemUI implements try { updateNotificationViews(oldEntry, notification); - if (ENABLE_HEADS_UP && mInterruptingNotificationEntry != null - && oldNotification == mInterruptingNotificationEntry.notification) { + if (ENABLE_HEADS_UP && mHeadsUpNotificationView.getEntry() != null + && oldNotification == mHeadsUpNotificationView.getEntry().notification) { if (!shouldInterrupt(notification)) { if (DEBUG) Log.d(TAG, "no longer interrupts!"); - mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP); + scheduleHeadsUpClose(); } else { if (DEBUG) Log.d(TAG, "updating the current heads up:" + notification); - mInterruptingNotificationEntry.notification = notification; - updateHeadsUpViews(mInterruptingNotificationEntry, notification); + mHeadsUpNotificationView.getEntry().notification = notification; + updateHeadsUpViews(mHeadsUpNotificationView.getEntry(), notification); } } @@ -1511,8 +1530,8 @@ public abstract class BaseStatusBar extends SystemUI implements } protected void notifyHeadsUpScreenOn(boolean screenOn) { - if (!screenOn && mInterruptingNotificationEntry != null) { - mHandler.sendEmptyMessage(MSG_ESCALATE_HEADS_UP); + if (!screenOn) { + scheduleHeadsUpEscalation(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java index 4233ab8..bfa74fa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java @@ -21,6 +21,7 @@ import android.content.Context; import android.os.Process; import android.provider.Settings; import android.service.notification.NotificationListenerService.Ranking; +import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.StatusBarNotification; import android.util.ArrayMap; import android.util.ArraySet; @@ -58,17 +59,18 @@ public class InterceptedNotifications { updateSyntheticNotification(); } - public boolean tryIntercept(StatusBarNotification notification, Ranking ranking) { - if (ranking == null) return false; + public boolean tryIntercept(StatusBarNotification notification, RankingMap rankingMap) { + if (rankingMap == null) return false; if (shouldDisplayIntercepted()) return false; if (mReleased.contains(notification.getKey())) return false; - if (!ranking.isInterceptedByDoNotDisturb(notification.getKey())) return false; + Ranking ranking = rankingMap.getRanking(notification.getKey()); + if (!ranking.isInterceptedByDoNotDisturb()) return false; mIntercepted.put(notification.getKey(), notification); updateSyntheticNotification(); return true; } - public void retryIntercepts(Ranking ranking) { + public void retryIntercepts(RankingMap ranking) { if (ranking == null) return; final int N = mIntercepted.size(); @@ -111,7 +113,7 @@ public class InterceptedNotifications { return; } final Notification n = new Notification.Builder(mContext) - .setSmallIcon(R.drawable.ic_qs_zen_on) + .setSmallIcon(R.drawable.ic_notify_zen) .setContentTitle(mContext.getResources().getQuantityString( R.plurals.zen_mode_notification_title, mIntercepted.size(), mIntercepted.size())) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java index 631e19c..c313c58 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar; import android.app.Notification; import android.service.notification.NotificationListenerService.Ranking; +import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.StatusBarNotification; import android.view.View; @@ -70,12 +71,16 @@ public class NotificationData { } private final ArrayList<Entry> mEntries = new ArrayList<Entry>(); - private Ranking mRanking; + private RankingMap mRanking; private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() { @Override public int compare(Entry a, Entry b) { if (mRanking != null) { - return mRanking.getRank(a.key) - mRanking.getRank(b.key); + Ranking aRanking = mRanking.getRanking(a.key); + Ranking bRanking = mRanking.getRanking(b.key); + int aRank = aRanking != null ? aRanking.getRank() : -1; + int bRank = bRanking != null ? bRanking.getRank() : -1; + return aRank - bRank; } final StatusBarNotification na = a.notification; @@ -108,12 +113,12 @@ public class NotificationData { return null; } - public void add(Entry entry, Ranking ranking) { + public void add(Entry entry, RankingMap ranking) { mEntries.add(entry); updateRankingAndSort(ranking); } - public Entry remove(String key, Ranking ranking) { + public Entry remove(String key, RankingMap ranking) { Entry e = findByKey(key); if (e == null) { return null; @@ -123,7 +128,7 @@ public class NotificationData { return e; } - public void updateRanking(Ranking ranking) { + public void updateRanking(RankingMap ranking) { updateRankingAndSort(ranking); } @@ -137,12 +142,13 @@ public class NotificationData { } } } else { - return mRanking.isAmbient(key); + Ranking ranking = mRanking.getRanking(key); + return ranking != null && ranking.isAmbient(); } return false; } - private void updateRankingAndSort(Ranking ranking) { + private void updateRankingAndSort(RankingMap ranking) { if (ranking != null) { mRanking = ranking; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotView.java deleted file mode 100644 index 1503072..0000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotView.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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 com.android.systemui.statusbar; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Outline; -import android.graphics.Paint; -import android.util.AttributeSet; -import android.view.View; - -/** - * An single dot of the {@link com.android.systemui.statusbar.SpeedBumpDotsLayout} - */ -public class SpeedBumpDotView extends View { - - private final Paint mPaint = new Paint(); - - public SpeedBumpDotView(Context context, AttributeSet attrs) { - super(context, attrs); - mPaint.setAntiAlias(true); - } - - @Override - protected void onDraw(Canvas canvas) { - float radius = getWidth() / 2.0f; - canvas.drawCircle(radius, radius, radius, mPaint); - } - - @Override - public boolean hasOverlappingRendering() { - return false; - } - - public void setColor(int color) { - mPaint.setColor(color); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsAlgorithm.java deleted file mode 100644 index cac6327..0000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsAlgorithm.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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 com.android.systemui.statusbar; - -import android.content.Context; -import android.view.View; -import com.android.systemui.R; - -/** - * The Algorithm of the {@link com.android.systemui.statusbar.SpeedBumpDotsLayout} which can be - * queried for {@link * com.android.systemui.statusbar.SpeedBumpDotsState} - */ -public class SpeedBumpDotsAlgorithm { - - private final float mDotRadius; - - public SpeedBumpDotsAlgorithm(Context context) { - mDotRadius = context.getResources().getDimensionPixelSize(R.dimen.speed_bump_dots_height) - / 2.0f; - } - - public void getState(SpeedBumpDotsState resultState) { - - // First reset the current state and ensure that every View has a ViewState - resultState.resetViewStates(); - - SpeedBumpDotsLayout hostView = resultState.getHostView(); - boolean currentlyVisible = hostView.isCurrentlyVisible(); - resultState.setActiveState(currentlyVisible - ? SpeedBumpDotsState.SHOWN - : SpeedBumpDotsState.HIDDEN); - int hostWidth = hostView.getWidth(); - float layoutWidth = hostWidth - 2 * mDotRadius; - int childCount = hostView.getChildCount(); - float paddingBetween = layoutWidth / (childCount - 1); - float centerY = hostView.getHeight() / 2.0f; - for (int i = 0; i < childCount; i++) { - View child = hostView.getChildAt(i); - SpeedBumpDotsState.ViewState viewState = resultState.getViewStateForView(child); - if (currentlyVisible) { - float xTranslation = i * paddingBetween; - viewState.xTranslation = xTranslation; - viewState.yTranslation = calculateYTranslation(hostView, centerY, xTranslation, - layoutWidth); - } else { - viewState.xTranslation = layoutWidth / 2; - viewState.yTranslation = centerY - mDotRadius; - } - viewState.alpha = currentlyVisible ? 1.0f : 0.0f; - viewState.scale = currentlyVisible ? 1.0f : 0.5f; - } - } - - private float calculateYTranslation(SpeedBumpDotsLayout hostView, float centerY, - float xTranslation, float layoutWidth) { - float t = hostView.getAnimationProgress(); - if (t == 0.0f || t == 1.0f) { - return centerY - mDotRadius; - } - float damping = (0.5f -Math.abs(0.5f - t)) * 1.3f; - float partialOffset = xTranslation / layoutWidth; - float indentFactor = (float) (Math.sin((t + partialOffset * 1.5f) * - Math.PI) * damping); - return (1.0f - indentFactor) * centerY - mDotRadius; - } - -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsLayout.java deleted file mode 100644 index ddf5215..0000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsLayout.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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 com.android.systemui.statusbar; - -import android.animation.TimeAnimator; -import android.animation.ValueAnimator; -import android.content.Context; -import android.graphics.Canvas; -import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.AccelerateDecelerateInterpolator; -import com.android.systemui.R; - -/** - * A layout with a certain number of dots which are integrated in the - * {@link com.android.systemui.statusbar.SpeedBumpView} - */ -public class SpeedBumpDotsLayout extends ViewGroup { - - private static final float DOT_CLICK_ANIMATION_LENGTH = 300; - private final int mDotSize; - private final SpeedBumpDotsAlgorithm mAlgorithm = new SpeedBumpDotsAlgorithm(getContext()); - private final SpeedBumpDotsState mCurrentState = new SpeedBumpDotsState(this); - private boolean mIsCurrentlyVisible = true; - private final ValueAnimator mClickAnimator; - private float mAnimationProgress; - private ValueAnimator.AnimatorUpdateListener mClickUpdateListener - = new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - mAnimationProgress = animation.getAnimatedFraction(); - updateChildren(); - } - }; - - public SpeedBumpDotsLayout(Context context, AttributeSet attrs) { - super(context, attrs); - mDotSize = getResources().getDimensionPixelSize(R.dimen.speed_bump_dots_height); - createDots(context, attrs); - mClickAnimator = TimeAnimator.ofFloat(0, DOT_CLICK_ANIMATION_LENGTH); - mClickAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); - mClickAnimator.addUpdateListener(mClickUpdateListener); - } - - private void createDots(Context context, AttributeSet attrs) { - SpeedBumpDotView blueDot = new SpeedBumpDotView(context, attrs); - blueDot.setColor(getResources().getColor(R.color.speed_bump_dot_blue)); - addView(blueDot); - - SpeedBumpDotView redDot = new SpeedBumpDotView(context, attrs); - redDot.setColor(getResources().getColor(R.color.speed_bump_dot_red)); - addView(redDot); - - SpeedBumpDotView yellowDot = new SpeedBumpDotView(context, attrs); - yellowDot.setColor(getResources().getColor(R.color.speed_bump_dot_yellow)); - addView(yellowDot); - - SpeedBumpDotView greenDot = new SpeedBumpDotView(context, attrs); - greenDot.setColor(getResources().getColor(R.color.speed_bump_dot_green)); - addView(greenDot); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - int childWidthSpec = MeasureSpec.makeMeasureSpec(mDotSize, - MeasureSpec.getMode(widthMeasureSpec)); - int childHeightSpec = MeasureSpec.makeMeasureSpec(mDotSize, - MeasureSpec.getMode(heightMeasureSpec)); - measureChildren(childWidthSpec, childHeightSpec); - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = getChildAt(i); - child.layout(0, 0, mDotSize, mDotSize); - } - if (changed) { - updateChildren(); - } - } - - private void updateChildren() { - mAlgorithm.getState(mCurrentState); - mCurrentState.apply(); - } - - public void performVisibilityAnimation(boolean visible) { - if (mClickAnimator.isRunning()) { - mClickAnimator.cancel(); - } - mIsCurrentlyVisible = visible; - mAlgorithm.getState(mCurrentState); - mCurrentState.animateToState(); - } - - public void setInvisible() { - mIsCurrentlyVisible = false; - mAlgorithm.getState(mCurrentState); - mCurrentState.apply(); - } - - public boolean isCurrentlyVisible() { - return mIsCurrentlyVisible; - } - - public void performDotClickAnimation() { - if (mClickAnimator.isRunning()) { - // don't perform an animation if it's running already - return; - } - mClickAnimator.start(); - } - - - public float getAnimationProgress() { - return mAnimationProgress; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsState.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsState.java deleted file mode 100644 index 4febab1..0000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpDotsState.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * 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 com.android.systemui.statusbar; - -import android.view.View; -import android.view.ViewPropertyAnimator; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; - -import java.util.HashMap; -import java.util.Map; - -/** - * A state of a {@link com.android.systemui.statusbar.SpeedBumpDotsLayout} - */ -public class SpeedBumpDotsState { - - public static final int HIDDEN = 1; - public static final int SHOWN = 2; - private static final int VISIBILITY_ANIMATION_DELAY_PER_ELEMENT = 80; - - private final SpeedBumpDotsLayout mHostView; - private final HashMap<View, ViewState> mStateMap = new HashMap<View, ViewState>(); - private final Interpolator mFastOutSlowInInterpolator; - private int mActiveState = 0; - - public SpeedBumpDotsState(SpeedBumpDotsLayout hostLayout) { - mHostView = hostLayout; - mFastOutSlowInInterpolator = AnimationUtils - .loadInterpolator(hostLayout.getContext(), - android.R.interpolator.fast_out_slow_in); - } - - public SpeedBumpDotsLayout getHostView() { - return mHostView; - } - - public void resetViewStates() { - int numChildren = mHostView.getChildCount(); - for (int i = 0; i < numChildren; i++) { - View child = mHostView.getChildAt(i); - ViewState viewState = mStateMap.get(child); - if (viewState == null) { - viewState = new ViewState(); - mStateMap.put(child, viewState); - } - } - } - - public ViewState getViewStateForView(View requestedView) { - return mStateMap.get(requestedView); - } - - public void apply() { - int childCount = mHostView.getChildCount(); - for (int i = 0; i < childCount; i++) { - View child = mHostView.getChildAt(i); - ViewState viewState = mStateMap.get(child); - - child.setTranslationX(viewState.xTranslation); - child.setTranslationY(viewState.yTranslation); - child.setScaleX(viewState.scale); - child.setScaleY(viewState.scale); - child.setAlpha(viewState.alpha); - } - } - - public void animateToState() { - int childCount = mHostView.getChildCount(); - int middleIndex = (childCount - 1) / 2; - long delayPerElement = VISIBILITY_ANIMATION_DELAY_PER_ELEMENT; - boolean isAppearing = getActiveState() == SHOWN; - boolean isDisappearing = getActiveState() == HIDDEN; - for (int i = 0; i < childCount; i++) { - int delayIndex; - if (i <= middleIndex) { - delayIndex = i * 2; - } else { - int distToMiddle = i - middleIndex; - delayIndex = (childCount - 1) - (distToMiddle - 1) * 2; - } - long startDelay = 0; - if (isAppearing || isDisappearing) { - if (isDisappearing) { - delayIndex = childCount - 1 - delayIndex; - } - startDelay = delayIndex * delayPerElement; - } - View child = mHostView.getChildAt(i); - ViewState viewState = mStateMap.get(child); - child.animate().setInterpolator(mFastOutSlowInInterpolator) - .setStartDelay(startDelay) - .alpha(viewState.alpha) - .translationX(viewState.xTranslation) - .translationY(viewState.yTranslation) - .scaleX(viewState.scale).scaleY(viewState.scale); - } - } - - public int getActiveState() { - return mActiveState; - } - - public void setActiveState(int mActiveState) { - this.mActiveState = mActiveState; - } - - public static class ViewState { - float xTranslation; - float yTranslation; - float alpha; - float scale; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java index 689d0e9..f80f0fd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java @@ -16,71 +16,26 @@ package com.android.systemui.statusbar; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; import android.content.Context; -import android.graphics.Outline; import android.util.AttributeSet; -import android.view.View; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; -import android.widget.TextView; import com.android.systemui.R; /** * The view representing the separation between important and less important notifications */ -public class SpeedBumpView extends ExpandableView implements View.OnClickListener { +public class SpeedBumpView extends ExpandableView { - private final int mCollapsedHeight; - private final int mDotsHeight; - private final int mTextPaddingInset; - private SpeedBumpDotsLayout mDots; - private AlphaOptimizedView mLineLeft; - private AlphaOptimizedView mLineRight; - private boolean mIsExpanded; - private boolean mDividerVisible = true; - private ValueAnimator mCurrentAnimator; + private final int mSpeedBumpHeight; + private AlphaOptimizedView mLine; + private boolean mIsVisible = true; private final Interpolator mFastOutSlowInInterpolator; - private float mCenterX; - private TextView mExplanationText; - private boolean mExplanationTextVisible = false; - private AnimatorListenerAdapter mHideExplanationListener = new AnimatorListenerAdapter() { - private boolean mCancelled; - - @Override - public void onAnimationEnd(Animator animation) { - if (!mCancelled) { - mExplanationText.setVisibility(View.INVISIBLE); - } - } - - @Override - public void onAnimationCancel(Animator animation) { - mCancelled = true; - } - - @Override - public void onAnimationStart(Animator animation) { - mCancelled = false; - } - }; - private Animator.AnimatorListener mAnimationFinishedListener = new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mCurrentAnimator = null; - } - }; public SpeedBumpView(Context context, AttributeSet attrs) { super(context, attrs); - mCollapsedHeight = getResources() - .getDimensionPixelSize(R.dimen.speed_bump_height_collapsed); - mTextPaddingInset = getResources().getDimensionPixelSize( - R.dimen.speed_bump_text_padding_inset); - mDotsHeight = getResources().getDimensionPixelSize(R.dimen.speed_bump_dots_height); - setOnClickListener(this); + mSpeedBumpHeight = getResources() + .getDimensionPixelSize(R.dimen.speed_bump_height); mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(), android.R.interpolator.fast_out_slow_in); } @@ -88,111 +43,41 @@ public class SpeedBumpView extends ExpandableView implements View.OnClickListene @Override protected void onFinishInflate() { super.onFinishInflate(); - mDots = (SpeedBumpDotsLayout) findViewById(R.id.speed_bump_dots_layout); - mLineLeft = (AlphaOptimizedView) findViewById(R.id.speedbump_line_left); - mLineRight = (AlphaOptimizedView) findViewById(R.id.speedbump_line_right); - mExplanationText = (TextView) findViewById(R.id.speed_bump_text); - resetExplanationText(); - + mLine = (AlphaOptimizedView) findViewById(R.id.speedbump_line); } @Override protected int getInitialHeight() { - return mCollapsedHeight; + return mSpeedBumpHeight; } @Override public int getIntrinsicHeight() { - if (mCurrentAnimator != null) { - // expand animation is running - return getActualHeight(); - } - return mIsExpanded ? getHeight() : mCollapsedHeight; + return mSpeedBumpHeight; } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - Outline outline = new Outline(); - mCenterX = getWidth() / 2; - float centerY = getHeight() / 2; - // TODO: hide outline better - // Temporary workaround to hide outline on a transparent view - int outlineLeft = (int) (mCenterX - getResources().getDisplayMetrics().densityDpi * 8); - int outlineTop = (int) (centerY - mDotsHeight / 2); - outline.setOval(outlineLeft, outlineTop, outlineLeft + mDotsHeight, - outlineTop + mDotsHeight); - setOutline(outline); - mLineLeft.setPivotX(mLineLeft.getWidth()); - mLineLeft.setPivotY(mLineLeft.getHeight() / 2); - mLineRight.setPivotX(0); - mLineRight.setPivotY(mLineRight.getHeight() / 2); + mLine.setPivotX(mLine.getWidth() / 2); + mLine.setPivotY(mLine.getHeight() / 2); + setOutline(null); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { measureChildren(widthMeasureSpec, heightMeasureSpec); - int height = mCollapsedHeight + mExplanationText.getMeasuredHeight() - mTextPaddingInset; + int height = mSpeedBumpHeight; setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), height); } @Override - public void onClick(View v) { - if (mCurrentAnimator != null) { - return; - } - int startValue = mIsExpanded ? getMaxHeight() : mCollapsedHeight; - int endValue = mIsExpanded ? mCollapsedHeight : getMaxHeight(); - mCurrentAnimator = ValueAnimator.ofInt(startValue, endValue); - mCurrentAnimator.setInterpolator(mFastOutSlowInInterpolator); - mCurrentAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - setActualHeight((int) animation.getAnimatedValue()); - } - }); - mCurrentAnimator.addListener(mAnimationFinishedListener); - mCurrentAnimator.start(); - mIsExpanded = !mIsExpanded; - mDots.performDotClickAnimation(); - animateExplanationTextInternal(mIsExpanded); - } - - private void animateExplanationTextInternal(boolean visible) { - if (mExplanationTextVisible != visible) { - float translationY = 0.0f; - float scale = 0.5f; - float alpha = 0.0f; - boolean needsHideListener = true; - if (visible) { - mExplanationText.setVisibility(VISIBLE); - translationY = mDots.getBottom() - mTextPaddingInset; - scale = 1.0f; - alpha = 1.0f; - needsHideListener = false; - } - mExplanationText.animate().setInterpolator(mFastOutSlowInInterpolator) - .alpha(alpha) - .scaleX(scale) - .scaleY(scale) - .translationY(translationY) - .setListener(needsHideListener ? mHideExplanationListener : null); - mExplanationTextVisible = visible; - } - } - - @Override public boolean isTransparent() { return true; } public void performVisibilityAnimation(boolean nowVisible) { animateDivider(nowVisible, null /* onFinishedRunnable */); - - // Animate explanation Text - if (mIsExpanded) { - animateExplanationTextInternal(nowVisible); - } } /** @@ -203,28 +88,16 @@ public class SpeedBumpView extends ExpandableView implements View.OnClickListene * finished. */ public void animateDivider(boolean nowVisible, Runnable onFinishedRunnable) { - if (nowVisible != mDividerVisible) { + if (nowVisible != mIsVisible) { // Animate dividers float endValue = nowVisible ? 1.0f : 0.0f; - float endTranslationXLeft = nowVisible ? 0.0f : mCenterX - mLineLeft.getRight(); - float endTranslationXRight = nowVisible ? 0.0f : mCenterX - mLineRight.getLeft(); - mLineLeft.animate() + mLine.animate() .alpha(endValue) .scaleX(endValue) .scaleY(endValue) - .translationX(endTranslationXLeft) .setInterpolator(mFastOutSlowInInterpolator) .withEndAction(onFinishedRunnable); - mLineRight.animate() - .alpha(endValue) - .scaleX(endValue) - .scaleY(endValue) - .translationX(endTranslationXRight) - .setInterpolator(mFastOutSlowInInterpolator); - - // Animate dots - mDots.performVisibilityAnimation(nowVisible); - mDividerVisible = nowVisible; + mIsVisible = nowVisible; } else { if (onFinishedRunnable != null) { onFinishedRunnable.run(); @@ -233,34 +106,10 @@ public class SpeedBumpView extends ExpandableView implements View.OnClickListene } public void setInvisible() { - float endTranslationXLeft = mCenterX - mLineLeft.getRight(); - float endTranslationXRight = mCenterX - mLineRight.getLeft(); - mLineLeft.setAlpha(0.0f); - mLineLeft.setScaleX(0.0f); - mLineLeft.setScaleY(0.0f); - mLineLeft.setTranslationX(endTranslationXLeft); - mLineRight.setAlpha(0.0f); - mLineRight.setScaleX(0.0f); - mLineRight.setScaleY(0.0f); - mLineRight.setTranslationX(endTranslationXRight); - mDots.setInvisible(); - resetExplanationText(); - - mDividerVisible = false; - } - - public void collapse() { - if (mIsExpanded) { - setActualHeight(mCollapsedHeight); - mIsExpanded = false; - } - resetExplanationText(); - } - - public void animateExplanationText(boolean nowVisible) { - if (mIsExpanded) { - animateExplanationTextInternal(nowVisible); - } + mLine.setAlpha(0.0f); + mLine.setScaleX(0.0f); + mLine.setScaleY(0.0f); + mIsVisible = false; } @Override @@ -272,17 +121,4 @@ public class SpeedBumpView extends ExpandableView implements View.OnClickListene public void performAddAnimation(long delay) { performVisibilityAnimation(true); } - - private void resetExplanationText() { - mExplanationText.setTranslationY(0); - mExplanationText.setVisibility(INVISIBLE); - mExplanationText.setAlpha(0.0f); - mExplanationText.setScaleX(0.5f); - mExplanationText.setScaleY(0.5f); - mExplanationTextVisible = false; - } - - public boolean isExpanded() { - return mIsExpanded; - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java index 086a266..e312d58 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java @@ -423,6 +423,7 @@ public class KeyguardPageSwipeHelper { return; } if (!animate) { + view.animate().cancel(); view.setAlpha(alpha); view.setScaleX(scale); view.setScaleY(scale); @@ -465,6 +466,13 @@ public class KeyguardPageSwipeHelper { } public void reset() { + if (mSwipeAnimator != null) { + mSwipeAnimator.cancel(); + } + ArrayList<View> targetViews = mCallback.getTranslationViews(); + for (View view : targetViews) { + view.animate().cancel(); + } setTranslation(0.0f, true); mSwipingInProgress = false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index 772d0e7..1f3098d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -27,6 +27,7 @@ import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.ViewConfiguration; +import android.view.ViewTreeObserver; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.FrameLayout; @@ -67,6 +68,11 @@ public abstract class PanelView extends FrameLayout { private VelocityTrackerInterface mVelocityTracker; private FlingAnimationUtils mFlingAnimationUtils; + /** + * Whether an instant expand request is currently pending and we are just waiting for layout. + */ + private boolean mInstantExpanding; + PanelBar mBar; protected int mMaxPanelHeight = -1; @@ -128,6 +134,9 @@ public abstract class PanelView extends FrameLayout { @Override public boolean onTouchEvent(MotionEvent event) { + if (mInstantExpanding) { + return false; + } /* * We capture touch events here and update the expand height here in case according to @@ -263,6 +272,9 @@ public abstract class PanelView extends FrameLayout { @Override public boolean onInterceptTouchEvent(MotionEvent event) { + if (mInstantExpanding) { + return false; + } /* * If the user drags anywhere inside the panel we intercept it if he moves his finger @@ -556,6 +568,41 @@ public abstract class PanelView extends FrameLayout { } } + public void instantExpand() { + mInstantExpanding = true; + abortAnimations(); + if (mTracking) { + onTrackingStopped(true /* expands */); // The panel is expanded after this call. + onExpandingFinished(); + } + setVisibility(VISIBLE); + + // Wait for window manager to pickup the change, so we know the maximum height of the panel + // then. + getViewTreeObserver().addOnGlobalLayoutListener( + new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + if (mStatusBar.getStatusBarWindow().getHeight() + != mStatusBar.getStatusBarHeight()) { + getViewTreeObserver().removeOnGlobalLayoutListener(this); + setExpandedFraction(1f); + mInstantExpanding = false; + } + } + }); + + // Make sure a layout really happens. + requestLayout(); + } + + private void abortAnimations() { + cancelPeek(); + if (mHeightAnimator != null) { + mHeightAnimator.cancel(); + } + } + protected void startUnlockHintAnimation() { // We don't need to hint the user if an animation is already running or the user is changing 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 a8f61f3..4ed1888 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -64,7 +64,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.provider.Settings.Global; -import android.service.notification.NotificationListenerService.Ranking; +import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.StatusBarNotification; import android.util.ArraySet; import android.util.DisplayMetrics; @@ -103,6 +103,7 @@ import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.qs.CircularClipper; import com.android.systemui.qs.QSPanel; import com.android.systemui.qs.QSTile; +import com.android.systemui.statusbar.ActivatableNotificationView; import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DragDownHelper; @@ -277,10 +278,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // the date view DateView mDateView; - // for heads up notifications - private HeadsUpNotificationView mHeadsUpNotificationView; - private int mHeadsUpNotificationDecay; - // on-screen navigation buttons private NavigationBarView mNavigationBarView = null; private int mNavigationBarWindowState = WINDOW_STATE_SHOWING; @@ -366,7 +363,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, if (!mUseHeadsUp) { Log.d(TAG, "dismissing any existing heads up notification on disable event"); setHeadsUpVisibility(false); - mHeadsUpNotificationView.setNotification(null); + mHeadsUpNotificationView.release(); removeHeadsUpView(); } else { addHeadsUpView(); @@ -818,6 +815,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, return mStatusBarView; } + public StatusBarWindowView getStatusBarWindow() { + return mStatusBarWindow; + } + @Override protected WindowManager.LayoutParams getSearchLayoutParams(LayoutParams layoutParams) { boolean opaque = false; @@ -1050,7 +1051,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } @Override - public void addNotificationInternal(StatusBarNotification notification, Ranking ranking) { + public void addNotificationInternal(StatusBarNotification notification, RankingMap ranking) { if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey()); if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(notification, ranking)) { // Forward the ranking so we can sort the new notification. @@ -1061,31 +1062,28 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, displayNotification(notification, ranking); } - public void displayNotification(StatusBarNotification notification, - Ranking ranking) { - Entry shadeEntry = createNotificationViews(notification); - if (shadeEntry == null) { - return; - } + public void displayNotification(StatusBarNotification notification, RankingMap ranking) { if (mUseHeadsUp && shouldInterrupt(notification)) { if (DEBUG) Log.d(TAG, "launching notification in heads up mode"); Entry interruptionCandidate = new Entry(notification, null); ViewGroup holder = mHeadsUpNotificationView.getHolder(); if (inflateViewsForHeadsUp(interruptionCandidate, holder)) { mInterruptingNotificationTime = System.currentTimeMillis(); - mInterruptingNotificationEntry = interruptionCandidate; - shadeEntry.setInterruption(); // 1. Populate mHeadsUpNotificationView - mHeadsUpNotificationView.setNotification(mInterruptingNotificationEntry); + mHeadsUpNotificationView.showNotification(interruptionCandidate); - // 2. Animate mHeadsUpNotificationView in - mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP); - - // 3. Set alarm to age the notification off - resetHeadsUpDecayTimer(); + // do not show the notification in the shade, yet. + return; } - } else if (notification.getNotification().fullScreenIntent != null) { + } + + Entry shadeEntry = createNotificationViews(notification); + if (shadeEntry == null) { + return; + } + + if (notification.getNotification().fullScreenIntent != null) { // Stop screensaver if the notification has a full-screen intent. // (like an incoming phone call) awakenDreams(); @@ -1100,7 +1098,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // usual case: status bar visible & not immersive // show the ticker if there isn't already a heads up - if (mInterruptingNotificationEntry == null) { + if (mHeadsUpNotificationView.getEntry() == null) { tick(notification, true); } } @@ -1110,31 +1108,64 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, updateExpandedViewPos(EXPANDED_LEAVE_ALONE); } + public void displayNotificationFromHeadsUp(StatusBarNotification notification) { + NotificationData.Entry shadeEntry = createNotificationViews(notification); + if (shadeEntry == null) { + return; + } + shadeEntry.setInterruption(); + + addNotificationViews(shadeEntry, null); + // Recalculate the position of the sliding windows and the titles. + setAreThereNotifications(); + updateExpandedViewPos(EXPANDED_LEAVE_ALONE); + } + @Override public void resetHeadsUpDecayTimer() { - mHandler.removeMessages(MSG_HIDE_HEADS_UP); + mHandler.removeMessages(MSG_DECAY_HEADS_UP); if (mUseHeadsUp && mHeadsUpNotificationDecay > 0 && mHeadsUpNotificationView.isClearable()) { - mHandler.sendEmptyMessageDelayed(MSG_HIDE_HEADS_UP, mHeadsUpNotificationDecay); + mHandler.sendEmptyMessageDelayed(MSG_DECAY_HEADS_UP, mHeadsUpNotificationDecay); } } @Override - public void updateNotificationInternal(StatusBarNotification notification, Ranking ranking) { + public void scheduleHeadsUpOpen() { + mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP); + } + + @Override + public void scheduleHeadsUpClose() { + mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP); + } + + @Override + public void scheduleHeadsUpEscalation() { + mHandler.sendEmptyMessage(MSG_ESCALATE_HEADS_UP); + } + + @Override + public void updateNotificationInternal(StatusBarNotification notification, RankingMap ranking) { super.updateNotificationInternal(notification, ranking); // if we're here, then the notification is already in the shade mIntercepted.remove(notification.getKey()); } @Override - protected void updateRankingInternal(Ranking ranking) { + protected void updateRankingInternal(RankingMap ranking) { mNotificationData.updateRanking(ranking); mIntercepted.retryIntercepts(ranking); updateNotifications(); } @Override - public void removeNotificationInternal(String key, Ranking ranking) { + public void removeNotificationInternal(String key, RankingMap ranking) { + if (ENABLE_HEADS_UP && mHeadsUpNotificationView.getEntry() != null + && key.equals(mHeadsUpNotificationView.getEntry().notification.getKey())) { + mHeadsUpNotificationView.clear(); + } + StatusBarNotification old = removeNotificationViews(key, ranking); if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old); @@ -1147,11 +1178,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // Recalculate the position of the sliding windows and the titles. updateExpandedViewPos(EXPANDED_LEAVE_ALONE); - if (ENABLE_HEADS_UP && mInterruptingNotificationEntry != null - && old == mInterruptingNotificationEntry.notification) { - mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP); - } - if (CLOSE_PANEL_WHEN_EMPTIED && mNotificationData.size() == 0 && !mNotificationPanel.isTracking() && mState != StatusBarState.KEYGUARD) { animateCollapsePanels(); @@ -1574,7 +1600,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, case MSG_SHOW_HEADS_UP: setHeadsUpVisibility(true); break; + case MSG_DECAY_HEADS_UP: + mHeadsUpNotificationView.release(); + setHeadsUpVisibility(false); + break; case MSG_HIDE_HEADS_UP: + mHeadsUpNotificationView.release(); setHeadsUpVisibility(false); break; case MSG_ESCALATE_HEADS_UP: @@ -1587,8 +1618,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, /** if the interrupting notification had a fullscreen intent, fire it now. */ private void escalateHeadsUp() { - if (mInterruptingNotificationEntry != null) { - final StatusBarNotification sbn = mInterruptingNotificationEntry.notification; + if (mHeadsUpNotificationView.getEntry() != null) { + final StatusBarNotification sbn = mHeadsUpNotificationView.getEntry().notification; + mHeadsUpNotificationView.release(); final Notification notification = sbn.getNotification(); if (notification.fullScreenIntent != null) { if (DEBUG) @@ -2243,7 +2275,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, pw.print(" mUseHeadsUp="); pw.println(mUseHeadsUp); pw.print(" interrupting package: "); - pw.println(hunStateToString(mInterruptingNotificationEntry)); + pw.println(hunStateToString(mHeadsUpNotificationView.getEntry())); dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions()); if (mNavigationBarView != null) { pw.print(" mNavigationBarWindowState="); @@ -2517,26 +2549,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, if (!ENABLE_HEADS_UP) return; if (DEBUG) Log.v(TAG, (vis ? "showing" : "hiding") + " heads up window"); mHeadsUpNotificationView.setVisibility(vis ? View.VISIBLE : View.GONE); - if (!vis) { - if (DEBUG) Log.d(TAG, "setting heads up entry to null"); - mInterruptingNotificationEntry = null; - } } public void onHeadsUpDismissed() { - if (mInterruptingNotificationEntry == null) return; - mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP); - if (mHeadsUpNotificationView.isClearable()) { - try { - mBarService.onNotificationClear( - mInterruptingNotificationEntry.notification.getPackageName(), - mInterruptingNotificationEntry.notification.getTag(), - mInterruptingNotificationEntry.notification.getId(), - mInterruptingNotificationEntry.notification.getUserId()); - } catch (android.os.RemoteException ex) { - // oh well - } - } + mHeadsUpNotificationView.dismiss(); } /** @@ -2905,7 +2921,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mNotificationPanel.setKeyguardShowing(false); mScrimController.setKeyguardShowing(false); } - updateStackScrollerState(); updatePublicMode(); updateNotifications(); @@ -2921,6 +2936,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, ? View.INVISIBLE : View.VISIBLE); mStackScroller.setScrollingEnabled(!onKeyguard); mStackScroller.setExpandingEnabled(!onKeyguard); + ActivatableNotificationView activatedChild = mStackScroller.getActivatedChild(); + mStackScroller.setActivatedChild(null); + if (activatedChild != null) { + activatedChild.makeInactive(false /* animate */); + } } public void userActivity() { @@ -2954,22 +2974,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private void instantExpandNotificationsPanel() { - // Make our window larger and the panel visible. + // Make our window larger and the panel expanded. makeExpandedVisible(true); - mNotificationPanel.setVisibility(View.VISIBLE); - - // Wait for window manager to pickup the change, so we know the maximum height of the panel - // then. - mNotificationPanel.getViewTreeObserver().addOnGlobalLayoutListener( - new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - if (mStatusBarWindow.getHeight() != getStatusBarHeight()) { - mNotificationPanel.getViewTreeObserver().removeOnGlobalLayoutListener(this); - mNotificationPanel.setExpandedFraction(1); - } - } - }); + mNotificationPanel.instantExpand(); } private void instantCollapseNotificationPanel() { @@ -2977,9 +2984,13 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } @Override - public void onActivated(View view) { + public void onActivated(ActivatableNotificationView view) { userActivity(); mKeyguardIndicationController.showTransientIndication(R.string.notification_tap_again); + ActivatableNotificationView previousView = mStackScroller.getActivatedChild(); + if (previousView != null) { + previousView.makeInactive(true /* animate */); + } mStackScroller.setActivatedChild(view); } @@ -2992,7 +3003,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } @Override - public void onActivationReset(View view) { + public void onActivationReset(ActivatableNotificationView view) { if (view == mStackScroller.getActivatedChild()) { mKeyguardIndicationController.hideTransientIndication(); mStackScroller.setActivatedChild(null); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java index df01c12..d778ccb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java @@ -33,9 +33,9 @@ import com.android.systemui.ExpandHelper; import com.android.systemui.Gefingerpoken; import com.android.systemui.R; import com.android.systemui.SwipeHelper; -import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.statusbar.ExpandableView; import com.android.systemui.statusbar.NotificationData; +import com.android.systemui.statusbar.phone.PhoneStatusBar; public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper.Callback, ExpandHelper.Callback, ViewTreeObserver.OnComputeInternalInsetsListener { @@ -51,7 +51,7 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper. private SwipeHelper mSwipeHelper; private EdgeSwipeHelper mEdgeSwipeHelper; - private BaseStatusBar mBar; + private PhoneStatusBar mBar; private ExpandHelper mExpandHelper; private long mStartTouchTime; @@ -69,7 +69,7 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper. if (DEBUG) Log.v(TAG, "create() " + mTouchSensitivityDelay); } - public void setBar(BaseStatusBar bar) { + public void setBar(PhoneStatusBar bar) { mBar = bar; } @@ -77,7 +77,10 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper. return mContentHolder; } - public boolean setNotification(NotificationData.Entry headsUp) { + public boolean showNotification(NotificationData.Entry headsUp) { + // bump any previous heads up back to the shade + release(); + mHeadsUp = headsUp; if (mContentHolder != null) { mContentHolder.removeAllViews(); @@ -97,10 +100,46 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper. mSwipeHelper.snapChild(mContentHolder, 1f); mStartTouchTime = System.currentTimeMillis() + mTouchSensitivityDelay; + + // 2. Animate mHeadsUpNotificationView in + mBar.scheduleHeadsUpOpen(); + + // 3. Set alarm to age the notification off + mBar.resetHeadsUpDecayTimer(); } return true; } + /** Discard the Heads Up notification. */ + public void clear() { + mHeadsUp = null; + mBar.scheduleHeadsUpClose(); + } + + /** Respond to dismissal of the Heads Up window. */ + public void dismiss() { + if (mHeadsUp == null) return; + if (mHeadsUp.notification.isClearable()) { + mBar.onNotificationClear(mHeadsUp.notification); + } else { + release(); + } + mHeadsUp = null; + mBar.scheduleHeadsUpClose(); + } + + /** Push any current Heads Up notification down into the shade. */ + public void release() { + if (mHeadsUp != null) { + mBar.displayNotificationFromHeadsUp(mHeadsUp.notification); + } + mHeadsUp = null; + } + + public NotificationData.Entry getEntry() { + return mHeadsUp; + } + public boolean isClearable() { return mHeadsUp == null || mHeadsUp.notification.isClearable(); } @@ -125,7 +164,7 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper. if (mHeadsUp != null) { // whoops, we're on already! - setNotification(mHeadsUp); + showNotification(mHeadsUp); } getViewTreeObserver().addOnComputeInternalInsetsListener(this); @@ -282,6 +321,10 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper. mTmpTwoArray[1] + mContentHolder.getHeight()); } + public void escalate() { + mBar.scheduleHeadsUpEscalation(); + } + private class EdgeSwipeHelper implements Gefingerpoken { private static final boolean DEBUG_EDGE_SWIPE = false; private final float mTouchSlop; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index dc8f315..1f68860 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -30,7 +30,7 @@ public interface NetworkController { void onMobileDataSignalChanged(boolean enabled, int mobileSignalIconId, String mobileSignalContentDescriptionId, int dataTypeIconId, boolean activityIn, boolean activityOut, - String dataTypeContentDescriptionId, String description); + String dataTypeContentDescriptionId, String description, boolean noSim); void onAirplaneModeChanged(boolean enabled); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index bf908fe..4e54e41 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -88,6 +88,7 @@ public class NetworkControllerImpl extends BroadcastReceiver int mQSDataTypeIconId; int mAirplaneIconId; boolean mDataActive; + boolean mNoSim; int mLastSignalLevel; boolean mShowPhoneRSSIForData = false; boolean mShowAtLeastThreeGees = false; @@ -350,18 +351,18 @@ public class NetworkControllerImpl extends BroadcastReceiver if (isEmergencyOnly()) { cb.onMobileDataSignalChanged(false, mQSPhoneSignalIconId, mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut, - mContentDescriptionDataType, null); + mContentDescriptionDataType, null, mNoSim); } else { if (mIsWimaxEnabled && mWimaxConnected) { // Wimax is special cb.onMobileDataSignalChanged(true, mQSPhoneSignalIconId, mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut, - mContentDescriptionDataType, mNetworkName); + mContentDescriptionDataType, mNetworkName, mNoSim); } else { // Normal mobile data cb.onMobileDataSignalChanged(mHasMobileDataFeature, mQSPhoneSignalIconId, mContentDescriptionPhoneSignal, mQSDataTypeIconId, mobileIn, mobileOut, - mContentDescriptionDataType, mNetworkName); + mContentDescriptionDataType, mNetworkName, mNoSim); } } cb.onAirplaneModeChanged(mAirplaneMode); @@ -737,6 +738,7 @@ public class NetworkControllerImpl extends BroadcastReceiver // GSM case, we have to check also the sim state if (mSimState == IccCardConstants.State.READY || mSimState == IccCardConstants.State.UNKNOWN) { + mNoSim = false; if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) { switch (mDataActivity) { case TelephonyManager.DATA_ACTIVITY_IN: @@ -759,6 +761,7 @@ public class NetworkControllerImpl extends BroadcastReceiver } } else { iconId = R.drawable.stat_sys_no_sim; + mNoSim = true; visible = false; // no SIM? no data } } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java index b21e12c..6d92b05 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.stack; import android.view.View; +import com.android.systemui.statusbar.ActivatableNotificationView; import java.util.ArrayList; @@ -27,7 +28,7 @@ public class AmbientState { private ArrayList<View> mDraggedViews = new ArrayList<View>(); private int mScrollY; private boolean mDimmed; - private View mActivatedChild; + private ActivatableNotificationView mActivatedChild; private float mOverScrollTopAmount; private float mOverScrollBottomAmount; private int mSpeedBumpIndex = -1; @@ -64,7 +65,7 @@ public class AmbientState { * In dimmed mode, a child can be activated, which happens on the first tap of the double-tap * interaction. This child is then scaled normally and its background is fully opaque. */ - public void setActivatedChild(View activatedChild) { + public void setActivatedChild(ActivatableNotificationView activatedChild) { mActivatedChild = activatedChild; } @@ -72,7 +73,7 @@ public class AmbientState { return mDimmed; } - public View getActivatedChild() { + public ActivatableNotificationView getActivatedChild() { return mActivatedChild; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 5ace89f..94472a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -33,6 +33,7 @@ import android.widget.OverScroller; import com.android.systemui.ExpandHelper; import com.android.systemui.R; import com.android.systemui.SwipeHelper; +import com.android.systemui.statusbar.ActivatableNotificationView; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.ExpandableView; import com.android.systemui.statusbar.SpeedBumpView; @@ -1722,7 +1723,6 @@ public class NotificationStackScrollLayout extends ViewGroup mStackScrollAlgorithm.setIsExpanded(isExpanded); if (!isExpanded) { mOwnScrollY = 0; - mSpeedBumpView.collapse(); } } @@ -1760,7 +1760,7 @@ public class NotificationStackScrollLayout extends ViewGroup /** * See {@link AmbientState#setActivatedChild}. */ - public void setActivatedChild(View activatedChild) { + public void setActivatedChild(ActivatableNotificationView activatedChild) { mAmbientState.setActivatedChild(activatedChild); if (mAnimationsEnabled) { mActivateNeedsAnimation = true; @@ -1769,7 +1769,7 @@ public class NotificationStackScrollLayout extends ViewGroup requestChildrenUpdate(); } - public View getActivatedChild() { + public ActivatableNotificationView getActivatedChild() { return mAmbientState.getActivatedChild(); } @@ -1791,7 +1791,6 @@ public class NotificationStackScrollLayout extends ViewGroup int newVisibility = visible ? VISIBLE : GONE; mSpeedBumpView.setVisibility(newVisibility); if (visible) { - mSpeedBumpView.collapse(); // Make invisible to ensure that the appear animation is played. mSpeedBumpView.setInvisible(); if (!mIsExpansionChanging) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java index 94cb16d..1ad4acc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java @@ -160,9 +160,8 @@ public class StackScrollState { } if(child instanceof SpeedBumpView) { - float speedBumpEnd = newYTranslation + newHeight; - performSpeedBumpAnimation(i, (SpeedBumpView) child, speedBumpEnd, - newYTranslation); + float lineEnd = newYTranslation + newHeight / 2; + performSpeedBumpAnimation(i, (SpeedBumpView) child, lineEnd); } } } @@ -183,20 +182,12 @@ public class StackScrollState { child.setClipBounds(mClipRect); } - private void performSpeedBumpAnimation(int i, SpeedBumpView speedBump, float speedBumpEnd, - float speedBumpStart) { + private void performSpeedBumpAnimation(int i, SpeedBumpView speedBump, float speedBumpEnd) { View nextChild = getNextChildNotGone(i); if (nextChild != null) { ViewState nextState = getViewStateForView(nextChild); - boolean startIsAboveNext = nextState.yTranslation > speedBumpStart; + boolean startIsAboveNext = nextState.yTranslation > speedBumpEnd; speedBump.animateDivider(startIsAboveNext, null /* onFinishedRunnable */); - - // handle expanded case - if (speedBump.isExpanded()) { - boolean endIsAboveNext = nextState.yTranslation > speedBumpEnd; - speedBump.animateExplanationText(endIsAboveNext); - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java index faea8de..9260aac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java @@ -17,14 +17,14 @@ package com.android.systemui.statusbar.tv; import android.os.IBinder; -import android.service.notification.NotificationListenerService; -import android.service.notification.NotificationListenerService.Ranking; +import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.StatusBarNotification; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.view.WindowManager; import com.android.internal.statusbar.StatusBarIcon; +import com.android.systemui.statusbar.ActivatableNotificationView; import com.android.systemui.statusbar.BaseStatusBar; /* @@ -51,11 +51,11 @@ public class TvStatusBar extends BaseStatusBar { } @Override - public void addNotificationInternal(StatusBarNotification notification, Ranking ranking) { + public void addNotificationInternal(StatusBarNotification notification, RankingMap ranking) { } @Override - protected void updateRankingInternal(Ranking ranking) { + protected void updateRankingInternal(RankingMap ranking) { } @Override @@ -63,7 +63,7 @@ public class TvStatusBar extends BaseStatusBar { } @Override - public void removeNotificationInternal(String key, Ranking ranking) { + public void removeNotificationInternal(String key, RankingMap ranking) { } @Override @@ -147,6 +147,18 @@ public class TvStatusBar extends BaseStatusBar { } @Override + public void scheduleHeadsUpOpen() { + } + + @Override + public void scheduleHeadsUpEscalation() { + } + + @Override + public void scheduleHeadsUpClose() { + } + + @Override protected int getMaxKeyguardNotifications() { return 0; } @@ -164,10 +176,10 @@ public class TvStatusBar extends BaseStatusBar { } @Override - public void onActivated(View view) { + public void onActivated(ActivatableNotificationView view) { } @Override - public void onActivationReset(View view) { + public void onActivationReset(ActivatableNotificationView view) { } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index eca1bc1..faa0c57 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -1640,6 +1640,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { pw.println("}]"); pw.println(); } + final int windowCount = mSecurityPolicy.mWindows.size(); + for (int j = 0; j < windowCount; j++) { + if (j > 0) { + pw.append(','); + pw.println(); + } + pw.append("Window["); + AccessibilityWindowInfo window = mSecurityPolicy.mWindows.get(j); + pw.append(window.toString()); + pw.append(']'); + } } } @@ -3283,7 +3294,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } if (mTouchInteractionInProgress && activeWindowGone) { - mActiveWindowId = INVALID_WINDOW_ID; + mActiveWindowId = mFocusedWindowId; } // Focused window may change the active one, so set the @@ -3336,10 +3347,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { // The active window also determined events from which // windows are delivered. synchronized (mLock) { - mFocusedWindowId = getFocusedWindowId(); - if (mWindowsForAccessibilityCallback == null - && windowId == mFocusedWindowId) { - mActiveWindowId = windowId; + if (mWindowsForAccessibilityCallback == null) { + mFocusedWindowId = getFocusedWindowId(); + if (windowId == mFocusedWindowId) { + mActiveWindowId = windowId; + } } } } break; diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index fe5c2ef..aeb195f 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -16,6 +16,7 @@ package com.android.server; +import android.database.ContentObserver; import android.os.BatteryStats; import com.android.internal.app.IBatteryStats; import com.android.server.am.BatteryStatsService; @@ -149,8 +150,8 @@ public final class BatteryService extends Binder { com.android.internal.R.integer.config_criticalBatteryWarningLevel); mLowBatteryWarningLevel = mContext.getResources().getInteger( com.android.internal.R.integer.config_lowBatteryWarningLevel); - mLowBatteryCloseWarningLevel = mContext.getResources().getInteger( - com.android.internal.R.integer.config_lowBatteryCloseWarningLevel); + mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger( + com.android.internal.R.integer.config_lowBatteryCloseWarningBump); mShutdownBatteryTemperature = mContext.getResources().getInteger( com.android.internal.R.integer.config_shutdownBatteryTemperature); @@ -173,9 +174,37 @@ public final class BatteryService extends Binder { void systemReady() { // check our power situation now that it is safe to display the shutdown dialog. synchronized (mLock) { - shutdownIfNoPowerLocked(); - shutdownIfOverTempLocked(); + ContentObserver obs = new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange) { + synchronized (mLock) { + updateBatteryWarningLevelLocked(); + } + } + }; + final ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL), + false, obs, UserHandle.USER_ALL); + updateBatteryWarningLevelLocked(); + } + } + + void updateBatteryWarningLevelLocked() { + final ContentResolver resolver = mContext.getContentResolver(); + int defWarnLevel = mContext.getResources().getInteger( + com.android.internal.R.integer.config_lowBatteryWarningLevel); + mLowBatteryWarningLevel = Settings.Global.getInt(resolver, + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel); + if (mLowBatteryWarningLevel == 0) { + mLowBatteryWarningLevel = defWarnLevel; + } + if (mLowBatteryWarningLevel < mCriticalBatteryLevel) { + mLowBatteryWarningLevel = mCriticalBatteryLevel; } + mLowBatteryCloseWarningLevel = mLowBatteryWarningLevel + mContext.getResources().getInteger( + com.android.internal.R.integer.config_lowBatteryCloseWarningBump); + processValuesLocked(true); } /** @@ -232,7 +261,7 @@ public final class BatteryService extends Binder { } } - public boolean isBatteryLowLocked() { + public boolean shouldSendBatteryLowLocked() { final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE; final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE; @@ -299,14 +328,14 @@ public final class BatteryService extends Binder { if (!mUpdatesStopped) { mBatteryProps = props; // Process the new values. - processValuesLocked(); + processValuesLocked(false); } else { mLastBatteryProps.set(props); } } } - private void processValuesLocked() { + private void processValuesLocked(boolean force) { boolean logOutlier = false; long dischargeDuration = 0; @@ -349,14 +378,14 @@ public final class BatteryService extends Binder { shutdownIfNoPowerLocked(); shutdownIfOverTempLocked(); - if (mBatteryProps.batteryStatus != mLastBatteryStatus || + if (force || (mBatteryProps.batteryStatus != mLastBatteryStatus || mBatteryProps.batteryHealth != mLastBatteryHealth || mBatteryProps.batteryPresent != mLastBatteryPresent || mBatteryProps.batteryLevel != mLastBatteryLevel || mPlugType != mLastPlugType || mBatteryProps.batteryVoltage != mLastBatteryVoltage || mBatteryProps.batteryTemperature != mLastBatteryTemperature || - mInvalidCharger != mLastInvalidCharger) { + mInvalidCharger != mLastInvalidCharger)) { if (mPlugType != mLastPlugType) { if (mLastPlugType == BATTERY_PLUGGED_NONE) { @@ -400,7 +429,24 @@ public final class BatteryService extends Binder { logOutlier = true; } - mBatteryLevelLow = isBatteryLowLocked(); + if (!mBatteryLevelLow) { + // Should we now switch in to low battery mode? + if (mPlugType == BATTERY_PLUGGED_NONE + && mBatteryProps.batteryLevel <= mLowBatteryWarningLevel) { + mBatteryLevelLow = true; + } + } else { + // Should we now switch out of low battery mode? + if (mPlugType != BATTERY_PLUGGED_NONE) { + mBatteryLevelLow = false; + } else if (mBatteryProps.batteryLevel >= mLowBatteryCloseWarningLevel) { + mBatteryLevelLow = false; + } else if (force && mBatteryProps.batteryLevel >= mLowBatteryWarningLevel) { + // If being forced, the previous state doesn't matter, we will just + // absolutely check to see if we are now above the warning level. + mBatteryLevelLow = false; + } + } sendIntentLocked(); @@ -428,7 +474,7 @@ public final class BatteryService extends Binder { }); } - if (mBatteryLevelLow) { + if (shouldSendBatteryLowLocked()) { mSentLowBatteryBroadcast = true; mHandler.post(new Runnable() { @Override @@ -650,7 +696,7 @@ public final class BatteryService extends Binder { long ident = Binder.clearCallingIdentity(); try { mUpdatesStopped = true; - processValuesLocked(); + processValuesLocked(false); } finally { Binder.restoreCallingIdentity(ident); } @@ -664,7 +710,7 @@ public final class BatteryService extends Binder { if (mUpdatesStopped) { mUpdatesStopped = false; mBatteryProps.set(mLastBatteryProps); - processValuesLocked(); + processValuesLocked(false); } } finally { Binder.restoreCallingIdentity(ident); diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 32722bc..11f855e 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -906,6 +906,9 @@ final class ActivityRecord { } startTime = 0; finishLaunchTickingLocked(); + if (task != null) { + task.hasBeenVisible = true; + } } } diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 39800f2..03ce530 100755 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -32,12 +32,12 @@ import static com.android.server.am.ActivityManagerService.VALIDATE_TOKENS; import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE; import static com.android.server.am.ActivityStackSupervisor.DEBUG_APP; +import static com.android.server.am.ActivityStackSupervisor.DEBUG_CONTAINERS; import static com.android.server.am.ActivityStackSupervisor.DEBUG_SAVED_STATE; import static com.android.server.am.ActivityStackSupervisor.DEBUG_SCREENSHOTS; import static com.android.server.am.ActivityStackSupervisor.DEBUG_STATES; import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID; -import android.service.voice.IVoiceInteractionSession; import com.android.internal.app.IVoiceInteractor; import com.android.internal.os.BatteryStatsImpl; import com.android.server.Watchdog; @@ -74,6 +74,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; +import android.service.voice.IVoiceInteractionSession; import android.util.EventLog; import android.util.Slog; import android.view.Display; @@ -1405,7 +1406,7 @@ final class ActivityStack { ActivityRecord parent = mActivityContainer.mParentActivity; if ((parent != null && parent.state != ActivityState.RESUMED) || - !mActivityContainer.isAttached()) { + !mActivityContainer.isAttachedLocked()) { // Do not resume this stack if its parent is not resumed. // TODO: If in a loop, make sure that parent stack resumeTopActivity is called 1st. return false; @@ -2677,11 +2678,14 @@ final class ActivityStack { || prevState == ActivityState.INITIALIZING) { // If this activity is already stopped, we can just finish // it right now. - boolean activityRemoved = destroyActivityLocked(r, true, - oomAdj, "finish-imm"); + r.makeFinishing(); + boolean activityRemoved = destroyActivityLocked(r, true, oomAdj, "finish-imm"); if (activityRemoved) { mStackSupervisor.resumeTopActivitiesLocked(); } + if (DEBUG_CONTAINERS) Slog.d(TAG, + "destroyActivityLocked: finishCurrentActivityLocked r=" + r + + " destroy returned removed=" + activityRemoved); return activityRemoved ? null : r; } @@ -3044,6 +3048,7 @@ final class ActivityStack { if (r != null) { mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r); } + if (DEBUG_CONTAINERS) Slog.d(TAG, "activityDestroyedLocked: r=" + r); if (isInStackLocked(token) != null) { if (r.state == ActivityState.DESTROYING) { @@ -3803,7 +3808,7 @@ final class ActivityStack { mStacks.remove(this); mStacks.add(0, this); } - mActivityContainer.onTaskListEmpty(); + mActivityContainer.onTaskListEmptyLocked(); } } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index dcdc610..545a9f7 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -111,6 +111,7 @@ public final class ActivityStackSupervisor implements DisplayListener { static final boolean DEBUG_STATES = DEBUG || false; static final boolean DEBUG_IDLE = DEBUG || false; static final boolean DEBUG_SCREENSHOTS = DEBUG || false; + static final boolean DEBUG_CONTAINERS = DEBUG || false; public static final int HOME_STACK_ID = 0; @@ -135,6 +136,7 @@ public final class ActivityStackSupervisor implements DisplayListener { static final int LOCK_TASK_START_MSG = FIRST_SUPERVISOR_STACK_MSG + 9; static final int LOCK_TASK_END_MSG = FIRST_SUPERVISOR_STACK_MSG + 10; static final int CONTAINER_CALLBACK_TASK_LIST_EMPTY = FIRST_SUPERVISOR_STACK_MSG + 11; + static final int CONTAINER_TASK_LIST_EMPTY_TIMEOUT = FIRST_SUPERVISOR_STACK_MSG + 12; private final static String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay"; @@ -239,7 +241,7 @@ public final class ActivityStackSupervisor implements DisplayListener { // TODO: Add listener for removal of references. /** Mapping from (ActivityStack/TaskStack).mStackId to their current state */ - SparseArray<ActivityContainer> mActivityContainers = new SparseArray<ActivityContainer>(); + private SparseArray<ActivityContainer> mActivityContainers = new SparseArray<ActivityContainer>(); /** Mapping from displayId to display current state */ private final SparseArray<ActivityDisplay> mActivityDisplays = @@ -2255,8 +2257,10 @@ public final class ActivityStackSupervisor implements DisplayListener { ActivityContainer createActivityContainer(ActivityRecord parentActivity, IActivityContainerCallback callback) { - ActivityContainer activityContainer = new VirtualActivityContainer(parentActivity, callback); + ActivityContainer activityContainer = + new VirtualActivityContainer(parentActivity, callback); mActivityContainers.put(activityContainer.mStackId, activityContainer); + if (DEBUG_CONTAINERS) Slog.d(TAG, "createActivityContainer: " + activityContainer); parentActivity.mChildContainers.add(activityContainer); return activityContainer; } @@ -2265,6 +2269,8 @@ public final class ActivityStackSupervisor implements DisplayListener { final ArrayList<ActivityContainer> childStacks = parentActivity.mChildContainers; for (int containerNdx = childStacks.size() - 1; containerNdx >= 0; --containerNdx) { ActivityContainer container = childStacks.remove(containerNdx); + if (DEBUG_CONTAINERS) Slog.d(TAG, "removeChildActivityContainers: removing " + + container); container.release(); } } @@ -2272,11 +2278,8 @@ public final class ActivityStackSupervisor implements DisplayListener { void deleteActivityContainer(IActivityContainer container) { ActivityContainer activityContainer = (ActivityContainer)container; if (activityContainer != null) { - activityContainer.mStack.finishAllActivitiesLocked(); - final ActivityRecord parent = activityContainer.mParentActivity; - if (parent != null) { - parent.mChildContainers.remove(activityContainer); - } + if (DEBUG_CONTAINERS) Slog.d(TAG, "deleteActivityContainer: ", + new RuntimeException("here").fillInStackTrace()); final int stackId = activityContainer.mStackId; mActivityContainers.remove(stackId); mWindowManager.removeStack(stackId); @@ -3128,13 +3131,20 @@ public final class ActivityStackSupervisor implements DisplayListener { } } } break; + case CONTAINER_TASK_LIST_EMPTY_TIMEOUT: { + synchronized (mService) { + Slog.w(TAG, "Timeout waiting for all activities in task to finish. " + + msg.obj); + ((ActivityContainer) msg.obj).onTaskListEmptyLocked(); + } + } break; } } } class ActivityContainer extends android.app.IActivityContainer.Stub { final static int FORCE_NEW_TASK_FLAGS = Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_MULTIPLE_TASK; + Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION; final int mStackId; IActivityContainerCallback mCallback = null; final ActivityStack mStack; @@ -3184,8 +3194,10 @@ public final class ActivityStackSupervisor implements DisplayListener { @Override public int getDisplayId() { - if (mActivityDisplay != null) { - return mActivityDisplay.mDisplayId; + synchronized (mService) { + if (mActivityDisplay != null) { + return mActivityDisplay.mDisplayId; + } } return -1; } @@ -3194,10 +3206,12 @@ public final class ActivityStackSupervisor implements DisplayListener { public boolean injectEvent(InputEvent event) { final long origId = Binder.clearCallingIdentity(); try { - if (mActivityDisplay != null) { - return mInputManagerInternal.injectInputEvent(event, - mActivityDisplay.mDisplayId, - InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + synchronized (mService) { + if (mActivityDisplay != null) { + return mInputManagerInternal.injectInputEvent(event, + mActivityDisplay.mDisplayId, + InputManager.INJECT_INPUT_EVENT_MODE_ASYNC); + } } return false; } finally { @@ -3207,10 +3221,16 @@ public final class ActivityStackSupervisor implements DisplayListener { @Override public void release() { - mContainerState = CONTAINER_STATE_FINISHING; - mStack.finishAllActivitiesLocked(); - detachLocked(); - mWindowManager.removeStack(mStackId); + synchronized (mService) { + if (mContainerState == CONTAINER_STATE_FINISHING) { + return; + } + mContainerState = CONTAINER_STATE_FINISHING; + final Message msg = + mHandler.obtainMessage(CONTAINER_TASK_LIST_EMPTY_TIMEOUT, this); + mHandler.sendMessageDelayed(msg, 1000); + mStack.finishAllActivitiesLocked(); + } } private void detachLocked() { @@ -3301,15 +3321,17 @@ public final class ActivityStackSupervisor implements DisplayListener { return ActivityStackSupervisor.this; } - boolean isAttached() { + boolean isAttachedLocked() { return mActivityDisplay != null; } void getBounds(Point outBounds) { - if (mActivityDisplay != null) { - mActivityDisplay.getBounds(outBounds); - } else { - outBounds.set(0, 0); + synchronized (mService) { + if (mActivityDisplay != null) { + mActivityDisplay.getBounds(outBounds); + } else { + outBounds.set(0, 0); + } } } @@ -3332,7 +3354,12 @@ public final class ActivityStackSupervisor implements DisplayListener { return true; } - void onTaskListEmpty() { + void onTaskListEmptyLocked() { + mHandler.removeMessages(CONTAINER_TASK_LIST_EMPTY_TIMEOUT, this); + if (!mStack.isHomeStack()) { + detachLocked(); + deleteActivityContainer(this); + } mHandler.obtainMessage(CONTAINER_CALLBACK_TASK_LIST_EMPTY, this).sendToTarget(); } @@ -3351,7 +3378,7 @@ public final class ActivityStackSupervisor implements DisplayListener { mParentActivity = parent; mCallback = callback; mContainerState = CONTAINER_STATE_NO_SURFACE; - mIdString = "VirtualActivtyContainer{" + mStackId + ", parent=" + mParentActivity + "}"; + mIdString = "VirtualActivityContainer{" + mStackId + ", parent=" + mParentActivity + "}"; } @Override @@ -3397,22 +3424,22 @@ public final class ActivityStackSupervisor implements DisplayListener { } } - setSurfaceIfReady(); + setSurfaceIfReadyLocked(); if (DEBUG_STACK) Slog.d(TAG, "setSurface: " + this + " to display=" + virtualActivityDisplay); } @Override - boolean isAttached() { - return mSurface != null && super.isAttached(); + boolean isAttachedLocked() { + return mSurface != null && super.isAttachedLocked(); } @Override void setDrawn() { synchronized (mService) { mDrawn = true; - setSurfaceIfReady(); + setSurfaceIfReadyLocked(); } } @@ -3422,8 +3449,8 @@ public final class ActivityStackSupervisor implements DisplayListener { return false; } - private void setSurfaceIfReady() { - if (DEBUG_STACK) Slog.v(TAG, "setSurfaceIfReady: mDrawn=" + mDrawn + + private void setSurfaceIfReadyLocked() { + if (DEBUG_STACK) Slog.v(TAG, "setSurfaceIfReadyLocked: mDrawn=" + mDrawn + " mContainerState=" + mContainerState + " mSurface=" + mSurface); if (mDrawn && mSurface != null && mContainerState == CONTAINER_STATE_NO_SURFACE) { ((VirtualActivityDisplay) mActivityDisplay).setSurface(mSurface); diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 79e2d9d..81a0b36 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -73,6 +73,7 @@ final class TaskRecord extends ThumbnailHolder { boolean rootWasReset; // True if the intent at the root of the task had // the FLAG_ACTIVITY_RESET_TASK_IF_NEEDED flag. boolean askedCompatMode;// Have asked the user about compat mode for this task. + boolean hasBeenVisible; // Set if any activities in the task have been visible to the user. String stringName; // caching of toString() result. int userId; // user for which this task was created @@ -328,8 +329,12 @@ final class TaskRecord extends ThumbnailHolder { } boolean autoRemoveFromRecents() { - return intent != null && - (intent.getFlags() & Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS) != 0; + // We will automatically remove the task either if it has explicitly asked for + // this, or it is empty and has never contained an activity that got shown to + // the user. + return (intent != null && + (intent.getFlags() & Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS) != 0) || + (mActivities.isEmpty() && !hasBeenVisible); } /** @@ -800,7 +805,8 @@ final class TaskRecord extends ThumbnailHolder { } pw.print(prefix); pw.print("lastThumbnail="); pw.print(lastThumbnail); pw.print(" lastDescription="); pw.println(lastDescription); - pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime); + pw.print(prefix); pw.print("hasBeenVisible="); pw.print(hasBeenVisible); + pw.print(" lastActiveTime="); pw.print(lastActiveTime); pw.print(" (inactive for "); pw.print((getInactiveDuration()/1000)); pw.println("s)"); } diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java index 6f42c8b..d36fc2c 100644 --- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java +++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java @@ -259,7 +259,7 @@ final class DeviceDiscoveryAction extends FeatureAction { byte params[] = cmd.getParams(); if (params.length == 3) { - current.mPhysicalAddress = ((params[0] & 0xFF) << 8) | (params[1] & 0xFF); + current.mPhysicalAddress = HdmiUtils.twoBytesToInt(params); current.mDeviceType = params[2] & 0xFF; increaseProcessedDeviceCount(); @@ -307,9 +307,7 @@ final class DeviceDiscoveryAction extends FeatureAction { byte[] params = cmd.getParams(); if (params.length == 3) { - int vendorId = ((params[0] & 0xFF) << 16) - | ((params[1] & 0xFF) << 8) - | (params[2] & 0xFF); + int vendorId = HdmiUtils.threeBytesToInt(params); current.mVendorId = vendorId; } else { Slog.w(TAG, "Invalid vendor id: " + cmd.toString()); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java index 5c420d7..f869424 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecController.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java @@ -387,6 +387,41 @@ final class HdmiCecController { } /** + * Pass a option to CEC HAL. + * + * @param flag a key of option. For more details, look at + * {@link HdmiConstants#FLAG_HDMI_OPTION_WAKEUP} to + * {@link HdmiConstants#FLAG_HDMI_OPTION_SYSTEM_CEC_CONTROL} + * @param value a value of option. Actual value varies flag. For more + * details, look at description of flags + */ + void setOption(int flag, int value) { + assertRunOnServiceThread(); + nativeSetOption(mNativePtr, flag, value); + } + + /** + * Configure ARC circuit in the hardware logic to start or stop the feature. + * + * @param enabled whether to enable/disable ARC + */ + void setAudioReturnChannel(boolean enabled) { + assertRunOnServiceThread(); + nativeSetAudioReturnChannel(mNativePtr, enabled); + } + + /** + * Return the connection status of the specified port + * + * @param port port number to check connection status + * @return true if connected; otherwise, return false + */ + boolean isConnected(int port) { + assertRunOnServiceThread(); + return nativeIsConnected(mNativePtr, port); + } + + /** * Poll all remote devices. It sends <Polling Message> to all remote * devices. * @@ -606,4 +641,8 @@ final class HdmiCecController { private static native int nativeGetPhysicalAddress(long controllerPtr); private static native int nativeGetVersion(long controllerPtr); private static native int nativeGetVendorId(long controllerPtr); + private static native void nativeSetOption(long controllerPtr, int flag, int value); + private static native void nativeSetAudioReturnChannel(long controllerPtr, boolean flag); + private static native boolean nativeIsConnected(long controllerPtr, int port); + } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java index 23454ad..6697a53 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java @@ -18,12 +18,15 @@ package com.android.server.hdmi; import android.hardware.hdmi.HdmiCec; import android.hardware.hdmi.HdmiCecDeviceInfo; +import android.hardware.hdmi.HdmiCecMessage; +import android.util.Slog; /** * Class that models a logical CEC device hosted in this system. Handles initialization, * CEC commands that call for actions customized per device type. */ abstract class HdmiCecLocalDevice { + private static final String TAG = "HdmiCecLocalDevice"; protected final HdmiControlService mService; protected final int mDeviceType; @@ -59,6 +62,83 @@ abstract class HdmiCecLocalDevice { */ protected abstract void onAddressAllocated(int logicalAddress); + /** + * Dispatch incoming message. + * + * @param message incoming message + * @return true if consumed a message; otherwise, return false. + */ + final boolean dispatchMessage(HdmiCecMessage message) { + int dest = message.getDestination(); + if (dest != mAddress && dest != HdmiCec.ADDR_BROADCAST) { + return false; + } + return onMessage(message); + } + + protected boolean onMessage(HdmiCecMessage message) { + switch (message.getOpcode()) { + case HdmiCec.MESSAGE_GET_MENU_LANGUAGE: + return handleGetMenuLanguage(message); + case HdmiCec.MESSAGE_GIVE_PHYSICAL_ADDRESS: + return handleGivePhysicalAddress(); + case HdmiCec.MESSAGE_GIVE_OSD_NAME: + return handleGiveOsdName(message); + case HdmiCec.MESSAGE_GIVE_DEVICE_VENDOR_ID: + return handleGiveDeviceVendorId(); + case HdmiCec.MESSAGE_GET_CEC_VERSION: + return handleGetCecVersion(message); + default: + return false; + } + } + + protected boolean handleGivePhysicalAddress() { + int physicalAddress = mService.getPhysicalAddress(); + HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( + mAddress, physicalAddress, mDeviceType); + mService.sendCecCommand(cecMessage); + return true; + } + + protected boolean handleGiveDeviceVendorId() { + int vendorId = mService.getVendorId(); + HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand( + mAddress, vendorId); + mService.sendCecCommand(cecMessage); + return true; + } + + protected boolean handleGetCecVersion(HdmiCecMessage message) { + int version = mService.getCecVersion(); + HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(), + message.getSource(), version); + mService.sendCecCommand(cecMessage); + return true; + } + + protected boolean handleGetMenuLanguage(HdmiCecMessage message) { + Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString()); + mService.sendCecCommand( + HdmiCecMessageBuilder.buildFeatureAbortCommand(mAddress, + message.getSource(), HdmiCec.MESSAGE_GET_MENU_LANGUAGE, + HdmiConstants.ABORT_UNRECOGNIZED_MODE)); + return true; + } + + protected boolean handleGiveOsdName(HdmiCecMessage message) { + // Note that since this method is called after logical address allocation is done, + // mDeviceInfo should not be null. + HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildSetOsdNameCommand( + mAddress, message.getSource(), mDeviceInfo.getDisplayName()); + if (cecMessage != null) { + mService.sendCecCommand(cecMessage); + } else { + Slog.w(TAG, "Failed to build <Get Osd Name>:" + mDeviceInfo.getDisplayName()); + } + return true; + } + final void handleAddressAllocated(int logicalAddress) { mAddress = mPreferredAddress = logicalAddress; onAddressAllocated(logicalAddress); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java index 72d7f2d..8bd81ea 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java @@ -17,11 +17,16 @@ package com.android.server.hdmi; import android.hardware.hdmi.HdmiCec; +import android.hardware.hdmi.HdmiCecMessage; +import android.util.Slog; + +import java.util.Locale; /** * Represent a logical device of type TV residing in Android system. */ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { + private static final String TAG = "HdmiCecLocalDeviceTv"; HdmiCecLocalDeviceTv(HdmiControlService service) { super(service, HdmiCec.DEVICE_TV); @@ -39,4 +44,42 @@ final class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice { mService.launchDeviceDiscovery(mAddress); // TODO: Start routing control action, device discovery action. } + + @Override + protected boolean onMessage(HdmiCecMessage message) { + switch (message.getOpcode()) { + case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS: + return handleReportPhysicalAddress(message); + default: + return super.onMessage(message); + } + } + + @Override + protected boolean handleGetMenuLanguage(HdmiCecMessage message) { + HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand( + mAddress, Locale.getDefault().getISO3Language()); + // TODO: figure out how to handle failed to get language code. + if (command != null) { + mService.sendCecCommand(command); + } else { + Slog.w(TAG, "Failed to respond to <Get Menu Language>: " + message.toString()); + } + return true; + } + + private boolean handleReportPhysicalAddress(HdmiCecMessage message) { + // Ignore if [Device Discovery Action] is going on. + if (mService.hasAction(DeviceDiscoveryAction.class)) { + Slog.i(TAG, "Ignore unrecognizable <Report Physical Address> " + + "because Device Discovery Action is on-going:" + message); + return true; + } + + int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams()); + mService.addAndStartAction(new NewDeviceAction(mService, + mAddress, message.getSource(), physicalAddress)); + + return true; + } } diff --git a/services/core/java/com/android/server/hdmi/HdmiConstants.java b/services/core/java/com/android/server/hdmi/HdmiConstants.java index a83d1ed..54b5dcb 100644 --- a/services/core/java/com/android/server/hdmi/HdmiConstants.java +++ b/services/core/java/com/android/server/hdmi/HdmiConstants.java @@ -43,5 +43,31 @@ final class HdmiConstants { static final int UI_COMMAND_MUTE_FUNCTION = 0x65; static final int UI_COMMAND_RESTORE_VOLUME_FUNCTION = 0x66; + // Flags used for setOption to CEC HAL. + /** + * When set to false, HAL does not wake up the system upon receiving + * <Image View On> or <Text View On>. Used when user changes the TV + * settings to disable the auto TV on functionality. + * True by default. + */ + static final int FLAG_HDMI_OPTION_WAKEUP = 1; + /** + * When set to false, all the CEC commands are discarded. Used when + * user changes the TV settings to disable CEC functionality. + * True by default. + */ + static final int FLAG_HDMI_OPTION_ENABLE_CEC = 2; + /** + * Setting this flag to false means Android system will stop handling + * CEC service and yield the control over to the microprocessor that is + * powered on through the standby mode. When set to true, the system + * will gain the control over, hence telling the microprocessor to stop + * handling the cec commands. This is called when system goes + * in and out of standby mode to notify the microprocessor that it should + * start/stop handling CEC commands on behalf of the system. + * False by default. + */ + static final int FLAG_HDMI_OPTION_SYSTEM_CEC_CONTROL = 3; + private HdmiConstants() { /* cannot be instantiated */ } } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 58bd5f1..d41da30 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -24,6 +24,7 @@ import android.hardware.hdmi.HdmiCecMessage; import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.hdmi.IHdmiControlService; import android.hardware.hdmi.IHdmiHotplugEventListener; +import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -42,7 +43,6 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; -import java.util.Locale; /** * Provides a service for sending and processing HDMI control messages, @@ -239,6 +239,13 @@ public final class HdmiControlService extends SystemService { } /** + * Returns version of CEC. + */ + int getCecVersion() { + return mCecController.getVersion(); + } + + /** * Returns a list of {@link HdmiCecDeviceInfo}. * * @param includeLocalDevice whether to include local devices @@ -277,7 +284,7 @@ public final class HdmiControlService extends SystemService { } // See if we have an action of a given type in progress. - private <T extends FeatureAction> boolean hasAction(final Class<T> clazz) { + <T extends FeatureAction> boolean hasAction(final Class<T> clazz) { for (FeatureAction action : mActions) { if (action.getClass().equals(clazz)) { return true; @@ -335,10 +342,13 @@ public final class HdmiControlService extends SystemService { * @return {@code true} if ARC was in "Enabled" status */ boolean setArcStatus(boolean enabled) { + assertRunOnServiceThread(); synchronized (mLock) { boolean oldStatus = mArcStatusEnabled; // 1. Enable/disable ARC circuit. - // TODO: call set_audio_return_channel of hal interface. + mCecController.setAudioReturnChannel(enabled); + + // TODO: notify arc mode change to AudioManager. // 2. Update arc status; mArcStatusEnabled = enabled; @@ -366,31 +376,14 @@ public final class HdmiControlService extends SystemService { // Commands that queries system information replies directly instead // of creating FeatureAction because they are state-less. + // TODO: move the leftover message to local device. switch (message.getOpcode()) { - case HdmiCec.MESSAGE_GET_MENU_LANGUAGE: - handleGetMenuLanguage(message); - return true; - case HdmiCec.MESSAGE_GIVE_OSD_NAME: - handleGiveOsdName(message); - return true; - case HdmiCec.MESSAGE_GIVE_PHYSICAL_ADDRESS: - handleGivePhysicalAddress(message); - return true; - case HdmiCec.MESSAGE_GIVE_DEVICE_VENDOR_ID: - handleGiveDeviceVendorId(message); - return true; - case HdmiCec.MESSAGE_GET_CEC_VERSION: - handleGetCecVersion(message); - return true; case HdmiCec.MESSAGE_INITIATE_ARC: handleInitiateArc(message); return true; case HdmiCec.MESSAGE_TERMINATE_ARC: handleTerminateArc(message); return true; - case HdmiCec.MESSAGE_REPORT_PHYSICAL_ADDRESS: - handleReportPhysicalAddress(message); - return true; case HdmiCec.MESSAGE_SET_SYSTEM_AUDIO_MODE: handleSetSystemAudioMode(message); return true; @@ -398,8 +391,22 @@ public final class HdmiControlService extends SystemService { handleSystemAudioModeStatus(message); return true; default: - return dispatchMessageToAction(message); + if (dispatchMessageToAction(message)) { + return true; + } + break; } + + return dispatchMessageToLocalDevice(message); + } + + private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) { + for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) { + if (device.dispatchMessage(message)) { + return true; + } + } + return false; } /** @@ -446,9 +453,8 @@ public final class HdmiControlService extends SystemService { void launchDeviceDiscovery(final int sourceAddress) { // At first, clear all existing device infos. mCecController.clearDeviceInfoList(); - mCecMessageCache.flushAll(); + // TODO: flush cec message cache when CEC is turned off. - // TODO: check whether TV is one of local devices. DeviceDiscoveryAction action = new DeviceDiscoveryAction(this, sourceAddress, new DeviceDiscoveryCallback() { @Override @@ -470,28 +476,12 @@ public final class HdmiControlService extends SystemService { } private HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) { - // TODO: get device name read from system configuration. - String displayName = HdmiCec.getDefaultDeviceName(logicalAddress); + // TODO: find better name instead of model name. + String displayName = Build.MODEL; return new HdmiCecDeviceInfo(logicalAddress, getPhysicalAddress(), deviceType, getVendorId(), displayName); } - private void handleReportPhysicalAddress(HdmiCecMessage message) { - // At first, try to consume it. - if (dispatchMessageToAction(message)) { - return; - } - - // Ignore if [Device Discovery Action] is going on. - if (hasAction(DeviceDiscoveryAction.class)) { - Slog.i(TAG, "Ignore unrecognizable <Report Physical Address> " - + "because Device Discovery Action is on-going:" + message); - return; - } - - // TODO: start new device action. - } - private void handleInitiateArc(HdmiCecMessage message){ // In case where <Initiate Arc> is started by <Request ARC Initiation> // need to clean up RequestArcInitiationAction. @@ -512,64 +502,6 @@ public final class HdmiControlService extends SystemService { addAndStartAction(action); } - private void handleGetCecVersion(HdmiCecMessage message) { - int version = mCecController.getVersion(); - HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(), - message.getSource(), - version); - sendCecCommand(cecMessage); - } - - private void handleGiveDeviceVendorId(HdmiCecMessage message) { - int vendorId = mCecController.getVendorId(); - HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand( - message.getDestination(), vendorId); - sendCecCommand(cecMessage); - } - - private void handleGivePhysicalAddress(HdmiCecMessage message) { - int physicalAddress = mCecController.getPhysicalAddress(); - int deviceType = HdmiCec.getTypeFromAddress(message.getDestination()); - HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildReportPhysicalAddressCommand( - message.getDestination(), physicalAddress, deviceType); - sendCecCommand(cecMessage); - } - - private void handleGiveOsdName(HdmiCecMessage message) { - // TODO: read device name from settings or property. - String name = HdmiCec.getDefaultDeviceName(message.getDestination()); - HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildSetOsdNameCommand( - message.getDestination(), message.getSource(), name); - if (cecMessage != null) { - sendCecCommand(cecMessage); - } else { - Slog.w(TAG, "Failed to build <Get Osd Name>:" + name); - } - } - - private void handleGetMenuLanguage(HdmiCecMessage message) { - // Only 0 (TV), 14 (specific use) can answer. - if (message.getDestination() != HdmiCec.ADDR_TV - && message.getDestination() != HdmiCec.ADDR_SPECIFIC_USE) { - Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString()); - sendCecCommand( - HdmiCecMessageBuilder.buildFeatureAbortCommand(message.getDestination(), - message.getSource(), HdmiCec.MESSAGE_GET_MENU_LANGUAGE, - HdmiConstants.ABORT_UNRECOGNIZED_MODE)); - return; - } - - HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand( - message.getDestination(), - Locale.getDefault().getISO3Language()); - // TODO: figure out how to handle failed to get language code. - if (command != null) { - sendCecCommand(command); - } else { - Slog.w(TAG, "Failed to respond to <Get Menu Language>: " + message.toString()); - } - } - private boolean dispatchMessageToAction(HdmiCecMessage message) { for (FeatureAction action : mActions) { if (action.processCommand(message)) { diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java index ef128ed1..ca09fe6 100644 --- a/services/core/java/com/android/server/hdmi/HdmiUtils.java +++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java @@ -71,4 +71,24 @@ final class HdmiUtils { return cmd.getParams().length > 0 && cmd.getParams()[0] == HdmiConstants.SYSTEM_AUDIO_STATUS_ON; } + + /** + * Assemble two bytes into single integer value. + * + * @param data to be assembled + * @return assembled value + */ + static int twoBytesToInt(byte[] data) { + return ((data[0] & 0xFF) << 8) | (data[1] & 0xFF); + } + + /** + * Assemble three bytes into single integer value. + * + * @param data to be assembled + * @return assembled value + */ + static int threeBytesToInt(byte[] data) { + return ((data[0] & 0xFF) << 16) | ((data[1] & 0xFF) << 8) | (data[2] & 0xFF); + } } diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java index 088c40b..c284d10 100644 --- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java +++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java @@ -114,8 +114,7 @@ final class NewDeviceAction extends FeatureAction { } else if (mState == STATE_WAITING_FOR_DEVICE_VENDOR_ID) { if (opcode == HdmiCec.MESSAGE_DEVICE_VENDOR_ID) { if (params.length == 3) { - mVendorId = ((params[0] & 0xFF) << 16) + ((params[1] & 0xFF) << 8) - + (params[2] & 0xFF); + mVendorId = HdmiUtils.threeBytesToInt(params); } else { Slog.e(TAG, "Failed to get device vendor ID: "); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 386402b..06732ca 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -188,6 +188,8 @@ public class NotificationManagerService extends SystemService { private AppOpsManager mAppOps; + private Archive mArchive; + // Notification control database. For now just contains disabled packages. private AtomicFile mPolicyFile; private HashSet<String> mBlockedPackages = new HashSet<String>(); @@ -223,10 +225,12 @@ public class NotificationManagerService extends SystemService { private static final int REASON_LISTENER_CANCEL_ALL = 11; private static class Archive { - static final int BUFFER_SIZE = 250; - ArrayDeque<StatusBarNotification> mBuffer = new ArrayDeque<StatusBarNotification>(BUFFER_SIZE); + final int mBufferSize; + final ArrayDeque<StatusBarNotification> mBuffer; - public Archive() { + public Archive(int size) { + mBufferSize = size; + mBuffer = new ArrayDeque<StatusBarNotification>(mBufferSize); } public String toString() { @@ -240,7 +244,7 @@ public class NotificationManagerService extends SystemService { } public void record(StatusBarNotification nr) { - if (mBuffer.size() == BUFFER_SIZE) { + if (mBuffer.size() == mBufferSize) { mBuffer.removeFirst(); } @@ -250,7 +254,6 @@ public class NotificationManagerService extends SystemService { mBuffer.addLast(nr.cloneLight()); } - public void clear() { mBuffer.clear(); } @@ -300,7 +303,7 @@ public class NotificationManagerService extends SystemService { } public StatusBarNotification[] getArray(int count) { - if (count == 0) count = Archive.BUFFER_SIZE; + if (count == 0) count = mBufferSize; final StatusBarNotification[] a = new StatusBarNotification[Math.min(count, mBuffer.size())]; Iterator<StatusBarNotification> iter = descendingIterator(); @@ -312,7 +315,7 @@ public class NotificationManagerService extends SystemService { } public StatusBarNotification[] getArray(int count, String pkg, int userId) { - if (count == 0) count = Archive.BUFFER_SIZE; + if (count == 0) count = mBufferSize; final StatusBarNotification[] a = new StatusBarNotification[Math.min(count, mBuffer.size())]; Iterator<StatusBarNotification> iter = filter(descendingIterator(), pkg, userId); @@ -325,8 +328,6 @@ public class NotificationManagerService extends SystemService { } - Archive mArchive = new Archive(); - private void loadPolicyFile() { synchronized(mPolicyFile) { mBlockedPackages.clear(); @@ -854,6 +855,9 @@ public class NotificationManagerService extends SystemService { } } + mArchive = new Archive(resources.getInteger( + R.integer.config_notificationServiceArchiveSize)); + publishBinderService(Context.NOTIFICATION_SERVICE, mService); publishLocalService(NotificationManagerInternal.class, mInternalService); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 8585b4e..6cfcbdc 100755 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -4991,6 +4991,7 @@ public class PackageManagerService extends IPackageManager.Stub { mResolveActivity.packageName = mAndroidApplication.packageName; mResolveActivity.processName = "system:ui"; mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE; + mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER; mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS; mResolveActivity.theme = R.style.Theme_Holo_Dialog_Alert; mResolveActivity.exported = true; diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index d8671d9..fb4b8f0 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -409,6 +409,9 @@ public final class PowerManagerService extends com.android.server.SystemService // Current state of the low power mode setting. private boolean mLowPowerModeSetting; + // Current state of whether the settings are allowing auto low power mode. + private boolean mAutoLowPowerModeEnabled; + // True if the battery level is currently considered low. private boolean mBatteryLevelLow; @@ -558,6 +561,9 @@ public final class PowerManagerService extends com.android.server.SystemService resolver.registerContentObserver(Settings.Global.getUriFor( Settings.Global.LOW_POWER_MODE), false, mSettingsObserver, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL), + false, mSettingsObserver, UserHandle.USER_ALL); // Go. readConfigurationLocked(); updateSettingsLocked(); @@ -645,8 +651,12 @@ public final class PowerManagerService extends com.android.server.SystemService final boolean lowPowerModeEnabled = Settings.Global.getInt(resolver, Settings.Global.LOW_POWER_MODE, 0) != 0; - if (lowPowerModeEnabled != mLowPowerModeSetting) { + final boolean autoLowPowerModeEnabled = Settings.Global.getInt(resolver, + Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 15) != 0; + if (lowPowerModeEnabled != mLowPowerModeSetting + || autoLowPowerModeEnabled != mAutoLowPowerModeEnabled) { mLowPowerModeSetting = lowPowerModeEnabled; + mAutoLowPowerModeEnabled = autoLowPowerModeEnabled; updateLowPowerModeLocked(); } @@ -654,7 +664,8 @@ public final class PowerManagerService extends com.android.server.SystemService } void updateLowPowerModeLocked() { - final boolean lowPowerModeEnabled = mLowPowerModeSetting || mBatteryLevelLow; + final boolean lowPowerModeEnabled = !mIsPowered + && (mLowPowerModeSetting || (mAutoLowPowerModeEnabled && mBatteryLevelLow)); if (mLowPowerModeEnabled != lowPowerModeEnabled) { mLowPowerModeEnabled = lowPowerModeEnabled; powerHintInternal(POWER_HINT_LOW_POWER_MODE, lowPowerModeEnabled ? 1 : 0); @@ -1197,7 +1208,7 @@ public final class PowerManagerService extends com.android.server.SystemService } } - if (oldLevelLow != mBatteryLevelLow) { + if (wasPowered != mIsPowered || oldLevelLow != mBatteryLevelLow) { updateLowPowerModeLocked(); } } @@ -2168,6 +2179,8 @@ public final class PowerManagerService extends com.android.server.SystemService pw.println(" mRequestWaitForNegativeProximity=" + mRequestWaitForNegativeProximity); pw.println(" mSandmanScheduled=" + mSandmanScheduled); pw.println(" mSandmanSummoned=" + mSandmanSummoned); + pw.println(" mLowPowerModeEnabled=" + mLowPowerModeEnabled); + pw.println(" mBatteryLevelLow=" + mBatteryLevelLow); pw.println(" mLastWakeTime=" + TimeUtils.formatUptime(mLastWakeTime)); pw.println(" mLastSleepTime=" + TimeUtils.formatUptime(mLastSleepTime)); pw.println(" mLastUserActivityTime=" + TimeUtils.formatUptime(mLastUserActivityTime)); @@ -2204,6 +2217,8 @@ public final class PowerManagerService extends com.android.server.SystemService pw.println(" mDreamsEnabledSetting=" + mDreamsEnabledSetting); pw.println(" mDreamsActivateOnSleepSetting=" + mDreamsActivateOnSleepSetting); pw.println(" mDreamsActivateOnDockSetting=" + mDreamsActivateOnDockSetting); + pw.println(" mLowPowerModeSetting=" + mLowPowerModeSetting); + pw.println(" mAutoLowPowerModeEnabled=" + mAutoLowPowerModeEnabled); pw.println(" mMinimumScreenOffTimeoutConfig=" + mMinimumScreenOffTimeoutConfig); pw.println(" mMaximumScreenDimDurationConfig=" + mMaximumScreenDimDurationConfig); pw.println(" mMaximumScreenDimRatioConfig=" + mMaximumScreenDimRatioConfig); diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 32546df..b9ef492 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -186,12 +186,16 @@ public class TrustManagerService extends SystemService { if (resolveInfo.serviceInfo == null) continue; String packageName = resolveInfo.serviceInfo.packageName; + // STOPSHIP Reenable this check once the GMS Core prebuild library has the + // permission. + /* if (pm.checkPermission(PERMISSION_PROVIDE_AGENT, packageName) != PackageManager.PERMISSION_GRANTED) { Log.w(TAG, "Skipping agent because package " + packageName + " does not have permission " + PERMISSION_PROVIDE_AGENT + "."); continue; } + */ ComponentName name = getComponentName(resolveInfo); if (!enabledAgents.contains(name)) continue; diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index cfd09e5..95cfa24 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -166,6 +166,15 @@ final class AccessibilityController { } } + + public void onSomeWindowResizedOrMoved() { + // Not relevant for the display magnifier. + + if (mWindowsForAccessibilityObserver != null) { + mWindowsForAccessibilityObserver.computeChangedWindows(); + } + } + /** NOTE: This has to be called within a surface transaction. */ public void drawMagnifiedRegionBorderIfNeededLocked() { if (mDisplayMagnifier != null) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index c5d7a54..93a763a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -151,7 +151,6 @@ import java.text.DateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -9228,6 +9227,13 @@ public class WindowManagerService extends IWindowManager.Stub winAnimator.setAnimation(a); winAnimator.mAnimDw = w.mLastFrame.left - w.mFrame.left; winAnimator.mAnimDh = w.mLastFrame.top - w.mFrame.top; + + //TODO (multidisplay): Accessibility supported only for the default display. + if (mAccessibilityController != null + && displayId == Display.DEFAULT_DISPLAY) { + mAccessibilityController.onSomeWindowResizedOrMoved(); + } + try { w.mClient.moved(w.mFrame.left, w.mFrame.top); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 4a80e3e..fe771dc 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -36,6 +36,7 @@ import android.os.Debug; import android.os.RemoteCallbackList; import android.os.SystemClock; import android.util.TimeUtils; +import android.view.Display; import android.view.IWindowFocusObserver; import android.view.IWindowId; import com.android.server.input.InputWindowHandle; @@ -1359,6 +1360,13 @@ final class WindowState implements WindowManagerPolicy.WindowState { mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, reportDraw, newConfig); } + + //TODO (multidisplay): Accessibility supported only for the default display. + if (mService.mAccessibilityController != null + && getDisplayId() == Display.DEFAULT_DISPLAY) { + mService.mAccessibilityController.onSomeWindowResizedOrMoved(); + } + mOverscanInsetsChanged = false; mContentInsetsChanged = false; mVisibleInsetsChanged = false; diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp index a734026..cbc853d 100644 --- a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp +++ b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp @@ -57,6 +57,12 @@ public: int getVersion(); // Get vendor id used for vendor command. uint32_t getVendorId(); + // Set a flag and its value. + void setOption(int flag, int value); + // Set audio return channel status. + void setAudioReturnChannel(bool flag); + // Whether to hdmi device is connected to the given port. + bool isConnected(int port); jobject getCallbacksObj() const { return mCallbacksObj; @@ -222,6 +228,20 @@ uint32_t HdmiCecController::getVendorId() { return vendorId; } +void HdmiCecController::setOption(int flag, int value) { + mDevice->set_option(mDevice, flag, value); +} + +// Set audio return channel status. +void HdmiCecController::setAudioReturnChannel(bool enabled) { + mDevice->set_audio_return_channel(mDevice, enabled ? 1 : 0); +} + +// Whether to hdmi device is connected to the given port. +bool HdmiCecController::isConnected(int port) { + return mDevice->is_connected(mDevice, port) == HDMI_CONNECTED; +} + // static void HdmiCecController::onReceived(const hdmi_event_t* event, void* arg) { @@ -326,6 +346,26 @@ static jint nativeGetVendorId(JNIEnv* env, jclass clazz, jlong controllerPtr) { return controller->getVendorId(); } +static void nativeSetOption(JNIEnv* env, jclass clazz, jlong controllerPtr, jint flag, + jint value) { + HdmiCecController* controller = + reinterpret_cast<HdmiCecController*>(controllerPtr); + controller->setOption(flag, value); +} + +static void nativeSetAudioReturnChannel(JNIEnv* env, jclass clazz, jlong controllerPtr, + jboolean enabled) { + HdmiCecController* controller = + reinterpret_cast<HdmiCecController*>(controllerPtr); + controller->setAudioReturnChannel(enabled == JNI_TRUE); +} + +static jboolean nativeIsConnected(JNIEnv* env, jclass clazz, jlong controllerPtr, jint port) { + HdmiCecController* controller = + reinterpret_cast<HdmiCecController*>(controllerPtr); + return controller->isConnected(port) ? JNI_TRUE : JNI_FALSE ; +} + static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ { "nativeInit", @@ -337,6 +377,9 @@ static JNINativeMethod sMethods[] = { { "nativeGetPhysicalAddress", "(J)I", (void *) nativeGetPhysicalAddress }, { "nativeGetVersion", "(J)I", (void *) nativeGetVersion }, { "nativeGetVendorId", "(J)I", (void *) nativeGetVendorId }, + { "nativeSetOption", "(JII)V", (void *) nativeSetOption }, + { "nativeSetAudioReturnChannel", "(JZ)V", (void *) nativeSetAudioReturnChannel }, + { "nativeIsConnected", "(JI)Z", (void *) nativeIsConnected }, }; #define CLASS_PATH "com/android/server/hdmi/HdmiCecController" diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 5cfe0f1..29d6f7e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -818,12 +818,20 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } if (who != null) { + if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) { + throw new SecurityException("Admin " + candidates.get(0).info.getComponent() + + " does not own the device"); + } + if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) { + throw new SecurityException("Admin " + candidates.get(0).info.getComponent() + + " does not own the profile"); + } throw new SecurityException("Admin " + candidates.get(0).info.getComponent() + " did not specify uses-policy for: " + candidates.get(0).info.getTagForPolicy(reqPolicy)); } else { throw new SecurityException("No active admin owned by uid " - + Binder.getCallingUid() + " for policy:" + reqPolicy); + + Binder.getCallingUid() + " for policy #" + reqPolicy); } } diff --git a/tests/Camera2Tests/CameraToo/Android.mk b/tests/Camera2Tests/CameraToo/Android.mk new file mode 100644 index 0000000..7e5911d --- /dev/null +++ b/tests/Camera2Tests/CameraToo/Android.mk @@ -0,0 +1,23 @@ +# 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. + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests +LOCAL_PACKAGE_NAME := CameraToo +LOCAL_SDK_VERSION := current +LOCAL_SRC_FILES := $(call all-java-files-under,src) + +include $(BUILD_PACKAGE) diff --git a/tests/Camera2Tests/CameraToo/AndroidManifest.xml b/tests/Camera2Tests/CameraToo/AndroidManifest.xml new file mode 100644 index 0000000..a92b5d8 --- /dev/null +++ b/tests/Camera2Tests/CameraToo/AndroidManifest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.android.camera2.cameratoo"> + <uses-permission android:name="android.permission.CAMERA" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <application android:label="CameraToo"> + <activity + android:name=".CameraTooActivity" + android:screenOrientation="portrait"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/tests/Camera2Tests/CameraToo/res/layout/mainactivity.xml b/tests/Camera2Tests/CameraToo/res/layout/mainactivity.xml new file mode 100644 index 0000000..f93f177 --- /dev/null +++ b/tests/Camera2Tests/CameraToo/res/layout/mainactivity.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + 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. +--> + +<SurfaceView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/mainSurfaceView" + android:layout_height="fill_parent" + android:layout_width="fill_parent" + android:onClick="onClickOnSurfaceView" /> diff --git a/tests/Camera2Tests/CameraToo/src/com/example/android/camera2/cameratoo/CameraTooActivity.java b/tests/Camera2Tests/CameraToo/src/com/example/android/camera2/cameratoo/CameraTooActivity.java new file mode 100644 index 0000000..c630bad --- /dev/null +++ b/tests/Camera2Tests/CameraToo/src/com/example/android/camera2/cameratoo/CameraTooActivity.java @@ -0,0 +1,437 @@ +/* + * 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 com.example.android.camera2.cameratoo; + +import android.app.Activity; +import android.graphics.ImageFormat; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CameraManager; +import android.hardware.camera2.CaptureFailure; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.TotalCaptureResult; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.media.Image; +import android.media.ImageReader; +import android.os.Bundle; +import android.os.Environment; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.util.Size; +import android.util.Log; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.View; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * A basic demonstration of how to write a point-and-shoot camera app against the new + * android.hardware.camera2 API. + */ +public class CameraTooActivity extends Activity { + /** Output files will be saved as /sdcard/Pictures/cameratoo*.jpg */ + static final String CAPTURE_FILENAME_PREFIX = "cameratoo"; + /** Tag to distinguish log prints. */ + static final String TAG = "CameraToo"; + + /** An additional thread for running tasks that shouldn't block the UI. */ + HandlerThread mBackgroundThread; + /** Handler for running tasks in the background. */ + Handler mBackgroundHandler; + /** Handler for running tasks on the UI thread. */ + Handler mForegroundHandler; + /** View for displaying the camera preview. */ + SurfaceView mSurfaceView; + /** Used to retrieve the captured image when the user takes a snapshot. */ + ImageReader mCaptureBuffer; + /** Handle to the Android camera services. */ + CameraManager mCameraManager; + /** The specific camera device that we're using. */ + CameraDevice mCamera; + /** Our image capture session. */ + CameraCaptureSession mCaptureSession; + + /** + * Given {@code choices} of {@code Size}s supported by a camera, chooses the smallest one whose + * width and height are at least as large as the respective requested values. + * @param choices The list of sizes that the camera supports for the intended output class + * @param width The minimum desired width + * @param height The minimum desired height + * @return The optimal {@code Size}, or an arbitrary one if none were big enough + */ + static Size chooseBigEnoughSize(Size[] choices, int width, int height) { + // Collect the supported resolutions that are at least as big as the preview Surface + List<Size> bigEnough = new ArrayList<Size>(); + for (Size option : choices) { + if (option.getWidth() >= width && option.getHeight() >= height) { + bigEnough.add(option); + } + } + + // Pick the smallest of those, assuming we found any + if (bigEnough.size() > 0) { + return Collections.min(bigEnough, new CompareSizesByArea()); + } else { + Log.e(TAG, "Couldn't find any suitable preview size"); + return choices[0]; + } + } + + /** + * Compares two {@code Size}s based on their areas. + */ + static class CompareSizesByArea implements Comparator<Size> { + @Override + public int compare(Size lhs, Size rhs) { + // We cast here to ensure the multiplications won't overflow + return Long.signum((long) lhs.getWidth() * lhs.getHeight() - + (long) rhs.getWidth() * rhs.getHeight()); + } + } + + /** + * Called when our {@code Activity} gains focus. <p>Starts initializing the camera.</p> + */ + @Override + protected void onResume() { + super.onResume(); + + // Start a background thread to manage camera requests + mBackgroundThread = new HandlerThread("background"); + mBackgroundThread.start(); + mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); + mForegroundHandler = new Handler(getMainLooper()); + + mCameraManager = (CameraManager) getSystemService(CAMERA_SERVICE); + + // Inflate the SurfaceView, set it as the main layout, and attach a listener + View layout = getLayoutInflater().inflate(R.layout.mainactivity, null); + mSurfaceView = (SurfaceView) layout.findViewById(R.id.mainSurfaceView); + mSurfaceView.getHolder().addCallback(mSurfaceHolderCallback); + setContentView(mSurfaceView); + + // Control flow continues in mSurfaceHolderCallback.surfaceChanged() + } + + /** + * Called when our {@code Activity} loses focus. <p>Tears everything back down.</p> + */ + @Override + protected void onPause() { + super.onPause(); + + try { + // Ensure SurfaceHolderCallback#surfaceChanged() will run again if the user returns + mSurfaceView.getHolder().setFixedSize(/*width*/0, /*height*/0); + + // Cancel any stale preview jobs + if (mCaptureSession != null) { + mCaptureSession.close(); + mCaptureSession = null; + } + } finally { + if (mCamera != null) { + mCamera.close(); + mCamera = null; + } + } + + // Finish processing posted messages, then join on the handling thread + mBackgroundThread.quitSafely(); + try { + mBackgroundThread.join(); + } catch (InterruptedException ex) { + Log.e(TAG, "Background worker thread was interrupted while joined", ex); + } + + // Close the ImageReader now that the background thread has stopped + if (mCaptureBuffer != null) mCaptureBuffer.close(); + } + + /** + * Called when the user clicks on our {@code SurfaceView}, which has ID {@code mainSurfaceView} + * as defined in the {@code mainactivity.xml} layout file. <p>Captures a full-resolution image + * and saves it to permanent storage.</p> + */ + public void onClickOnSurfaceView(View v) { + if (mCaptureSession != null) { + try { + CaptureRequest.Builder requester = + mCamera.createCaptureRequest(mCamera.TEMPLATE_STILL_CAPTURE); + requester.addTarget(mCaptureBuffer.getSurface()); + try { + // This handler can be null because we aren't actually attaching any callback + mCaptureSession.capture(requester.build(), /*listener*/null, /*handler*/null); + } catch (CameraAccessException ex) { + Log.e(TAG, "Failed to file actual capture request", ex); + } + } catch (CameraAccessException ex) { + Log.e(TAG, "Failed to build actual capture request", ex); + } + } else { + Log.e(TAG, "User attempted to perform a capture outside our session"); + } + + // Control flow continues in mImageCaptureListener.onImageAvailable() + } + + /** + * Callbacks invoked upon state changes in our {@code SurfaceView}. + */ + final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() { + /** The camera device to use, or null if we haven't yet set a fixed surface size. */ + private String mCameraId; + + /** Whether we received a change callback after setting our fixed surface size. */ + private boolean mGotSecondCallback; + + @Override + public void surfaceCreated(SurfaceHolder holder) { + // This is called every time the surface returns to the foreground + Log.i(TAG, "Surface created"); + mCameraId = null; + mGotSecondCallback = false; + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + Log.i(TAG, "Surface destroyed"); + holder.removeCallback(this); + // We don't stop receiving callbacks forever because onResume() will reattach us + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + // On the first invocation, width and height were automatically set to the view's size + if (mCameraId == null) { + // Find the device's back-facing camera and set the destination buffer sizes + try { + for (String cameraId : mCameraManager.getCameraIdList()) { + CameraCharacteristics cameraCharacteristics = + mCameraManager.getCameraCharacteristics(cameraId); + if (cameraCharacteristics.get(cameraCharacteristics.LENS_FACING) == + CameraCharacteristics.LENS_FACING_BACK) { + Log.i(TAG, "Found a back-facing camera"); + StreamConfigurationMap info = cameraCharacteristics + .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + + // Bigger is better when it comes to saving our image + Size largestSize = Collections.max( + Arrays.asList(info.getOutputSizes(ImageFormat.JPEG)), + new CompareSizesByArea()); + + // Prepare an ImageReader in case the user wants to capture images + Log.i(TAG, "Capture size: " + largestSize); + mCaptureBuffer = ImageReader.newInstance(largestSize.getWidth(), + largestSize.getHeight(), ImageFormat.JPEG, /*maxImages*/2); + mCaptureBuffer.setOnImageAvailableListener( + mImageCaptureListener, mBackgroundHandler); + + // Danger, W.R.! Attempting to use too large a preview size could + // exceed the camera bus' bandwidth limitation, resulting in + // gorgeous previews but the storage of garbage capture data. + Log.i(TAG, "SurfaceView size: " + + mSurfaceView.getWidth() + 'x' + mSurfaceView.getHeight()); + Size optimalSize = chooseBigEnoughSize( + info.getOutputSizes(SurfaceHolder.class), width, height); + + // Set the SurfaceHolder to use the camera's largest supported size + Log.i(TAG, "Preview size: " + optimalSize); + SurfaceHolder surfaceHolder = mSurfaceView.getHolder(); + surfaceHolder.setFixedSize(optimalSize.getWidth(), + optimalSize.getHeight()); + + mCameraId = cameraId; + return; + + // Control flow continues with this method one more time + // (since we just changed our own size) + } + } + } catch (CameraAccessException ex) { + Log.e(TAG, "Unable to list cameras", ex); + } + + Log.e(TAG, "Didn't find any back-facing cameras"); + // This is the second time the method is being invoked: our size change is complete + } else if (!mGotSecondCallback) { + if (mCamera != null) { + Log.e(TAG, "Aborting camera open because it hadn't been closed"); + return; + } + + // Open the camera device + try { + mCameraManager.openCamera(mCameraId, mCameraStateListener, + mBackgroundHandler); + } catch (CameraAccessException ex) { + Log.e(TAG, "Failed to configure output surface", ex); + } + mGotSecondCallback = true; + + // Control flow continues in mCameraStateListener.onOpened() + } + }}; + + /** + * Calledbacks invoked upon state changes in our {@code CameraDevice}. <p>These are run on + * {@code mBackgroundThread}.</p> + */ + final CameraDevice.StateListener mCameraStateListener = + new CameraDevice.StateListener() { + @Override + public void onOpened(CameraDevice camera) { + Log.i(TAG, "Successfully opened camera"); + mCamera = camera; + try { + List<Surface> outputs = Arrays.asList( + mSurfaceView.getHolder().getSurface(), mCaptureBuffer.getSurface()); + camera.createCaptureSession(outputs, mCaptureSessionListener, + mBackgroundHandler); + } catch (CameraAccessException ex) { + Log.e(TAG, "Failed to create a capture session", ex); + } + + // Control flow continues in mCaptureSessionListener.onConfigured() + } + + @Override + public void onDisconnected(CameraDevice camera) { + Log.e(TAG, "Camera was disconnected"); + } + + @Override + public void onError(CameraDevice camera, int error) { + Log.e(TAG, "State error on device '" + camera.getId() + "': code " + error); + }}; + + /** + * Callbacks invoked upon state changes in our {@code CameraCaptureSession}. <p>These are run on + * {@code mBackgroundThread}.</p> + */ + final CameraCaptureSession.StateListener mCaptureSessionListener = + new CameraCaptureSession.StateListener() { + @Override + public void onConfigured(CameraCaptureSession session) { + Log.i(TAG, "Finished configuring camera outputs"); + mCaptureSession = session; + + SurfaceHolder holder = mSurfaceView.getHolder(); + if (holder != null) { + try { + // Build a request for preview footage + CaptureRequest.Builder requestBuilder = + mCamera.createCaptureRequest(mCamera.TEMPLATE_PREVIEW); + requestBuilder.addTarget(holder.getSurface()); + CaptureRequest previewRequest = requestBuilder.build(); + + // Start displaying preview images + try { + session.setRepeatingRequest(previewRequest, /*listener*/null, + /*handler*/null); + } catch (CameraAccessException ex) { + Log.e(TAG, "Failed to make repeating preview request", ex); + } + } catch (CameraAccessException ex) { + Log.e(TAG, "Failed to build preview request", ex); + } + } + else { + Log.e(TAG, "Holder didn't exist when trying to formulate preview request"); + } + } + + @Override + public void onClosed(CameraCaptureSession session) { + mCaptureSession = null; + } + + @Override + public void onConfigureFailed(CameraCaptureSession session) { + Log.e(TAG, "Configuration error on device '" + mCamera.getId()); + }}; + + /** + * Callback invoked when we've received a JPEG image from the camera. + */ + final ImageReader.OnImageAvailableListener mImageCaptureListener = + new ImageReader.OnImageAvailableListener() { + @Override + public void onImageAvailable(ImageReader reader) { + // Save the image once we get a chance + mBackgroundHandler.post(new CapturedImageSaver(reader.acquireNextImage())); + + // Control flow continues in CapturedImageSaver#run() + }}; + + /** + * Deferred processor responsible for saving snapshots to disk. <p>This is run on + * {@code mBackgroundThread}.</p> + */ + static class CapturedImageSaver implements Runnable { + /** The image to save. */ + private Image mCapture; + + public CapturedImageSaver(Image capture) { + mCapture = capture; + } + + @Override + public void run() { + try { + // Choose an unused filename under the Pictures/ directory + File file = File.createTempFile(CAPTURE_FILENAME_PREFIX, ".jpg", + Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_PICTURES)); + try (FileOutputStream ostream = new FileOutputStream(file)) { + Log.i(TAG, "Retrieved image is" + + (mCapture.getFormat() == ImageFormat.JPEG ? "" : "n't") + " a JPEG"); + ByteBuffer buffer = mCapture.getPlanes()[0].getBuffer(); + Log.i(TAG, "Captured image size: " + + mCapture.getWidth() + 'x' + mCapture.getHeight()); + + // Write the image out to the chosen file + byte[] jpeg = new byte[buffer.remaining()]; + buffer.get(jpeg); + ostream.write(jpeg); + } catch (FileNotFoundException ex) { + Log.e(TAG, "Unable to open output file for writing", ex); + } catch (IOException ex) { + Log.e(TAG, "Failed to write the image to the output file", ex); + } + } catch (IOException ex) { + Log.e(TAG, "Unable to create a new output file", ex); + } finally { + mCapture.close(); + } + } + } +} diff --git a/tests/Camera2Tests/CameraToo/tests/Android.mk b/tests/Camera2Tests/CameraToo/tests/Android.mk new file mode 100644 index 0000000..0b58243 --- /dev/null +++ b/tests/Camera2Tests/CameraToo/tests/Android.mk @@ -0,0 +1,25 @@ +# 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. + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests +LOCAL_PACKAGE_NAME := CameraTooTests +LOCAL_INSTRUMENTATION_FOR := CameraToo +LOCAL_SDK_VERSION := current +LOCAL_SRC_FILES := $(call all-java-files-under,src) +LOCAL_STATIC_JAVA_LIBRARIES := android-support-test mockito-target + +include $(BUILD_PACKAGE) diff --git a/tests/Camera2Tests/CameraToo/tests/AndroidManifest.xml b/tests/Camera2Tests/CameraToo/tests/AndroidManifest.xml new file mode 100644 index 0000000..30210ba --- /dev/null +++ b/tests/Camera2Tests/CameraToo/tests/AndroidManifest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.android.camera2.cameratoo.tests"> + <uses-permission android:name="android.permission.CAMERA" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <application android:label="CameraToo"> + <uses-library android:name="android.test.runner" /> + </application> + <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" + android:targetPackage="com.example.android.camera2.cameratoo" + android:label="CameraToo tests" /> +</manifest> diff --git a/tests/Camera2Tests/CameraToo/tests/src/com/example/android/camera2/cameratoo/CameraTooTest.java b/tests/Camera2Tests/CameraToo/tests/src/com/example/android/camera2/cameratoo/CameraTooTest.java new file mode 100644 index 0000000..3acca5a --- /dev/null +++ b/tests/Camera2Tests/CameraToo/tests/src/com/example/android/camera2/cameratoo/CameraTooTest.java @@ -0,0 +1,189 @@ +/* + * 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 com.example.android.camera2.cameratoo; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.media.Image; +import android.os.Environment; +import android.util.Size; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FilenameFilter; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Set; + +import com.example.android.camera2.cameratoo.CameraTooActivity; +import org.junit.Test; + +public class CameraTooTest { + private <T> void assertComparatorEq(T lhs, T rhs, Comparator<T> rel) { + assertEquals(String.format("%s should be equal to %s", lhs, rhs), rel.compare(lhs, rhs), 0); + assertEquals(String.format("%s should be equal to %s (reverse check)", lhs, rhs), + rel.compare(rhs, lhs), 0); + } + + private <T> void assertComparatorLt(T lhs, T rhs, Comparator<T> rel) { + assertTrue(String.format("%s should be less than %s", lhs, rhs), rel.compare(lhs, rhs) < 0); + assertTrue(String.format("%s should be less than %s (reverse check)", lhs, rhs), + rel.compare(rhs, lhs) > 0); + } + + @Test + public void compareSizesByArea() { + Size empty = new Size(0, 0), fatAndFlat = new Size(100, 0), tallAndThin = new Size(0, 100); + Size smallSquare = new Size(4, 4), horizRect = new Size(8, 2), vertRect = new Size(2, 8); + Size largeSquare = new Size(5, 5); + Comparator<Size> rel = new CameraTooActivity.CompareSizesByArea(); + + assertComparatorEq(empty, fatAndFlat, rel); + assertComparatorEq(empty, tallAndThin, rel); + assertComparatorEq(fatAndFlat, empty, rel); + assertComparatorEq(fatAndFlat, tallAndThin, rel); + assertComparatorEq(tallAndThin, empty, rel); + assertComparatorEq(tallAndThin, fatAndFlat, rel); + + assertComparatorEq(smallSquare, horizRect, rel); + assertComparatorEq(smallSquare, vertRect, rel); + assertComparatorEq(horizRect, smallSquare, rel); + assertComparatorEq(horizRect, vertRect, rel); + assertComparatorEq(vertRect, smallSquare, rel); + assertComparatorEq(vertRect, horizRect, rel); + + assertComparatorLt(empty, smallSquare, rel); + assertComparatorLt(empty, horizRect, rel); + assertComparatorLt(empty, vertRect, rel); + + assertComparatorLt(fatAndFlat, smallSquare, rel); + assertComparatorLt(fatAndFlat, horizRect, rel); + assertComparatorLt(fatAndFlat, vertRect, rel); + + assertComparatorLt(tallAndThin, smallSquare, rel); + assertComparatorLt(tallAndThin, horizRect, rel); + assertComparatorLt(tallAndThin, vertRect, rel); + + assertComparatorLt(empty, largeSquare, rel); + assertComparatorLt(fatAndFlat, largeSquare, rel); + assertComparatorLt(tallAndThin, largeSquare, rel); + assertComparatorLt(smallSquare, largeSquare, rel); + assertComparatorLt(horizRect, largeSquare, rel); + assertComparatorLt(vertRect, largeSquare, rel); + } + + private void assertOptimalSize(Size[] options, int minWidth, int minHeight, Size expected) { + Size verdict = CameraTooActivity.chooseBigEnoughSize(options, minWidth, minHeight); + assertEquals(String.format("Expected optimal size %s but got %s", expected, verdict), + verdict, expected); + } + + @Test + public void chooseBigEnoughSize() { + Size empty = new Size(0, 0), fatAndFlat = new Size(100, 0), tallAndThin = new Size(0, 100); + Size smallSquare = new Size(4, 4), horizRect = new Size(8, 2), vertRect = new Size(2, 8); + Size largeSquare = new Size(5, 5); + Size[] siz = + { empty, fatAndFlat, tallAndThin, smallSquare, horizRect, vertRect, largeSquare }; + + assertOptimalSize(siz, 0, 0, empty); + + assertOptimalSize(siz, 1, 0, fatAndFlat); + assertOptimalSize(siz, 0, 1, tallAndThin); + + assertOptimalSize(siz, 4, 4, smallSquare); + assertOptimalSize(siz, 1, 1, smallSquare); + assertOptimalSize(siz, 2, 1, smallSquare); + assertOptimalSize(siz, 1, 2, smallSquare); + assertOptimalSize(siz, 3, 4, smallSquare); + assertOptimalSize(siz, 4, 3, smallSquare); + + assertOptimalSize(siz, 8, 2, horizRect); + assertOptimalSize(siz, 5, 1, horizRect); + assertOptimalSize(siz, 5, 2, horizRect); + + assertOptimalSize(siz, 2, 8, vertRect); + assertOptimalSize(siz, 1, 5, vertRect); + assertOptimalSize(siz, 2, 5, vertRect); + + assertOptimalSize(siz, 5, 5, largeSquare); + assertOptimalSize(siz, 3, 5, largeSquare); + assertOptimalSize(siz, 5, 3, largeSquare); + } + + private static final FilenameFilter OUTPUT_FILE_DECIDER = new FilenameFilter() { + @Override + public boolean accept(File dir, String filename) { + return filename.indexOf("cameratoo") == 0 && + filename.indexOf(".jpg") == filename.length() - ".jpg".length(); + }}; + + private static <T> Set<T> newlyAddedElements(Set<T> before, Set<T> after) { + Set<T> result = new HashSet<T>(after); + result.removeAll(before); + return result; + } + + @Test + public void capturedImageSaver() throws FileNotFoundException, IOException { + ByteBuffer buf = ByteBuffer.allocate(25); + for(int index = 0; index < buf.capacity(); ++index) + buf.put(index, (byte) index); + + Image.Plane plane = mock(Image.Plane.class); + when(plane.getBuffer()).thenReturn(buf); + when(plane.getPixelStride()).thenReturn(1); + when(plane.getRowStride()).thenReturn(5); + + Image.Plane[] onlyPlaneThatMatters = { plane }; + Image image = mock(Image.class); + when(image.getPlanes()).thenReturn(onlyPlaneThatMatters); + when(image.getWidth()).thenReturn(5); + when(image.getHeight()).thenReturn(5); + + File picturesFolder = + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); + Set<File> preListing = + new HashSet<File>(Arrays.asList(picturesFolder.listFiles(OUTPUT_FILE_DECIDER))); + + CameraTooActivity.CapturedImageSaver saver = + new CameraTooActivity.CapturedImageSaver(image); + saver.run(); + + Set<File> postListing = + new HashSet<File>(Arrays.asList(picturesFolder.listFiles(OUTPUT_FILE_DECIDER))); + Set<File> newFiles = newlyAddedElements(preListing, postListing); + + assertEquals(newFiles.size(), 1); + + File picture = newFiles.iterator().next(); + FileInputStream istream = new FileInputStream(picture); + + for(int count = 0; count < buf.capacity(); ++count) { + assertEquals(istream.read(), buf.get(count)); + } + assertEquals(istream.read(), -1); + assertTrue(picture.delete()); + } +} diff --git a/tests/VoiceEnrollment/Android.mk b/tests/VoiceEnrollment/Android.mk new file mode 100644 index 0000000..2ab3d02 --- /dev/null +++ b/tests/VoiceEnrollment/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := VoiceEnrollment + +LOCAL_MODULE_TAGS := optional + +LOCAL_PRIVILEGED_MODULE := true + +include $(BUILD_PACKAGE) diff --git a/tests/VoiceEnrollment/AndroidManifest.xml b/tests/VoiceEnrollment/AndroidManifest.xml new file mode 100644 index 0000000..6321222 --- /dev/null +++ b/tests/VoiceEnrollment/AndroidManifest.xml @@ -0,0 +1,16 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.test.voiceenrollment"> + + <application + android:permission="android.permission.MANAGE_VOICE_KEYPHRASES"> + <activity android:name="TestEnrollmentActivity" android:label="Voice Enrollment Application" + android:theme="@android:style/Theme.Material.Light.Voice"> + <intent-filter> + <action android:name="com.android.intent.action.MANAGE_VOICE_KEYPHRASES" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> + <meta-data android:name="android.voice_enrollment" + android:resource="@xml/enrollment_application"/> + </application> +</manifest> diff --git a/tests/VoiceEnrollment/res/xml/enrollment_application.xml b/tests/VoiceEnrollment/res/xml/enrollment_application.xml new file mode 100644 index 0000000..710a0ac --- /dev/null +++ b/tests/VoiceEnrollment/res/xml/enrollment_application.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * 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. + */ +--> + +<voice-enrollment-application xmlns:android="http://schemas.android.com/apk/res/android" + android:searchKeyphraseId="101" + android:searchKeyphrase="Hello There" + android:searchKeyphraseSupportedLocales="en-US,en-GB,fr-FR,de-DE" /> diff --git a/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java b/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java new file mode 100644 index 0000000..7fbd965 --- /dev/null +++ b/tests/VoiceEnrollment/src/com/android/test/voiceenrollment/TestEnrollmentActivity.java @@ -0,0 +1,23 @@ +/* + * 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 com.android.test.voiceenrollment; + +import android.app.Activity; + +public class TestEnrollmentActivity extends Activity { + // TODO(sansid): Add a test enrollment flow here. +} diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java index d40b05f..00c2c64 100644 --- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java +++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java @@ -21,6 +21,8 @@ import android.os.Bundle; import android.service.voice.VoiceInteractionService; import android.util.Log; +import java.util.Arrays; + public class MainInteractionService extends VoiceInteractionService { static final String TAG = "MainInteractionService"; @@ -28,6 +30,9 @@ public class MainInteractionService extends VoiceInteractionService { public void onCreate() { super.onCreate(); Log.i(TAG, "Creating " + this); + Log.i(TAG, "Keyphrase enrollment error? " + getKeyphraseEnrollmentInfo().getParseError()); + Log.i(TAG, "Keyphrase enrollment meta-data: " + + Arrays.toString(getKeyphraseEnrollmentInfo().getKeyphrases())); } @Override diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java index 99151c3..8191edd 100644 --- a/wifi/java/android/net/wifi/ScanResult.java +++ b/wifi/java/android/net/wifi/ScanResult.java @@ -89,6 +89,21 @@ public class ScanResult implements Parcelable { * {@hide} */ public final static int UNSPECIFIED = -1; + /** + * @hide + * TODO: makes real freq boundaries + */ + public boolean is24GHz() { + return frequency > 2400 && frequency < 2500; + } + + /** + * @hide + * TODO: makes real freq boundaries + */ + public boolean is5GHz() { + return frequency > 4900 && frequency < 5900; + } /** information element from beacon * @hide diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 192cba6..48396d5 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -405,6 +405,10 @@ public class WifiConfiguration implements Parcelable { /** @hide **/ public static int INITIAL_AUTO_JOIN_ATTEMPT_MIN_5 = -70; + /** @hide + * 5GHz band is prefered over 2.4 if the 5GHz RSSI is higher than this threshold **/ + public static int A_BAND_PREFERENCE_RSSI_THRESHOLD = -65; + /** * @hide * A summary of the RSSI and Band status for that configuration @@ -481,11 +485,11 @@ public class WifiConfiguration implements Parcelable { if (result.seen == 0) continue; - if ((result.frequency > 4900) && (result.frequency < 5900)) { + if (result.is5GHz()) { //strictly speaking: [4915, 5825] //number of known BSSID on 5GHz band status.num5 = status.num5 + 1; - } else if ((result.frequency > 2400) && (result.frequency < 2500)) { + } else if (result.is24GHz()) { //strictly speaking: [2412, 2482] //number of known BSSID on 2.4Ghz band status.num24 = status.num24 + 1; @@ -493,12 +497,12 @@ public class WifiConfiguration implements Parcelable { if ((now_ms - result.seen) > age) continue; - if ((result.frequency > 4900) && (result.frequency < 5900)) { + if (result.is5GHz()) { if (result.level > status.rssi5) { status.rssi5 = result.level; status.age5 = result.seen; } - } else if ((result.frequency > 2400) && (result.frequency < 2500)) { + } else if (result.is24GHz()) { if (result.level > status.rssi24) { status.rssi24 = result.level; status.age24 = result.seen; @@ -547,6 +551,17 @@ public class WifiConfiguration implements Parcelable { */ public long blackListTimestamp; + /** + * @hide + * last time the system was connected to this configuration. + */ + public long lastConnected; + + /** + * @hide + * last time the system was disconnected to this configuration. + */ + public long lastDisconnected; /** * Set if the configuration was self added by the framework @@ -658,7 +673,20 @@ public class WifiConfiguration implements Parcelable { // TODO: Add more checks return true; + } + /** + * Helper function, identify if a configuration is linked + * @hide + */ + public boolean isLinked(WifiConfiguration config) { + if (config.linkedConfigurations != null && linkedConfigurations != null) { + if (config.linkedConfigurations.get(configKey()) != null + && linkedConfigurations.get(config.configKey()) != null) { + return true; + } + } + return false; } /** @@ -688,6 +716,7 @@ public class WifiConfiguration implements Parcelable { /** @hide **/ public void setAutoJoinStatus(int status) { + if (status < 0) status = 0; if (status == 0) { blackListTimestamp = 0; } else if (status > autoJoinStatus) { @@ -1079,6 +1108,8 @@ public class WifiConfiguration implements Parcelable { creatorUid = source.creatorUid; peerWifiConfiguration = source.peerWifiConfiguration; blackListTimestamp = source.blackListTimestamp; + lastConnected = source.lastConnected; + lastDisconnected = source.lastDisconnected; } } diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java index f6a94d0..6760c56 100644 --- a/wifi/java/android/net/wifi/WifiInfo.java +++ b/wifi/java/android/net/wifi/WifiInfo.java @@ -62,6 +62,17 @@ public class WifiInfo implements Parcelable { private String mBSSID; private WifiSsid mWifiSsid; private int mNetworkId; + + /** @hide **/ + public static final int INVALID_RSSI = -127; + + /** @hide **/ + public static final int MIN_RSSI = -126; + + /** @hide **/ + public static final int MAX_RSSI = 200; + + /** * Received Signal Strength Indicator */ @@ -131,7 +142,8 @@ public class WifiInfo implements Parcelable { public int score; /** - * @hide * + * TODO: get actual timestamp and calculate true rates + * @hide */ public void updatePacketRates(WifiLinkLayerStats stats) { if (stats != null) { @@ -156,17 +168,42 @@ public class WifiInfo implements Parcelable { rxSuccess = rxgood; txRetries = txretries; } else { - txBadRate = 0; + txBad = 0; txSuccess = 0; rxSuccess = 0; txRetries = 0; + txBadRate = 0; + txSuccessRate = 0; + rxSuccessRate = 0; + txRetriesRate = 0; } } + /** - * Flag indicating that AP has hinted that upstream connection is metered, - * and sensitive to heavy data transfers. + * This function is less powerful and used if the WifiLinkLayerStats API is not implemented + * at the Wifi HAL + * @hide */ + public void updatePacketRates(long txPackets, long rxPackets) { + //paranoia + txBad = 0; + txRetries = 0; + txBadRate = 0; + txRetriesRate = 0; + + txSuccessRate = (txSuccessRate * 0.5) + + ((double) (txPackets - txSuccess) * 0.5); + rxSuccessRate = (rxSuccessRate * 0.5) + + ((double) (rxPackets - rxSuccess) * 0.5); + txSuccess = txPackets; + rxSuccess = rxPackets; + } + + /** + * Flag indicating that AP has hinted that upstream connection is metered, + * and sensitive to heavy data transfers. + */ private boolean mMeteredHint; /** @hide */ @@ -175,9 +212,32 @@ public class WifiInfo implements Parcelable { mBSSID = null; mNetworkId = -1; mSupplicantState = SupplicantState.UNINITIALIZED; - mRssi = -9999; + mRssi = INVALID_RSSI; mLinkSpeed = -1; mFrequency = -1; + txBad = 0; + } + + /** @hide */ + public void reset() { + setInetAddress(null); + setBSSID(null); + setSSID(null); + setNetworkId(-1); + setRssi(INVALID_RSSI); + setLinkSpeed(-1); + setFrequency(-1); + setMeteredHint(false); + txSuccess = 0; + rxSuccess = 0; + txRetries = 0; + txBadRate = 0; + txSuccessRate = 0; + rxSuccessRate = 0; + txRetriesRate = 0; + lowRssiCount = 0; + badRssiCount = 0; + score = 0; } /** @@ -256,7 +316,7 @@ public class WifiInfo implements Parcelable { /** * Returns the received signal strength indicator of the current 802.11 * network, in dBm. - * @return the RSSI, in the range -110 to 10 + * @return the RSSI, in the range -127 to 200 */ public int getRssi() { return mRssi; @@ -264,6 +324,10 @@ public class WifiInfo implements Parcelable { /** @hide */ public void setRssi(int rssi) { + if (rssi < INVALID_RSSI) + rssi = INVALID_RSSI; + if (rssi > MAX_RSSI) + rssi = MAX_RSSI; mRssi = rssi; } diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java index 3b65ca8..21b700d 100644 --- a/wifi/java/android/net/wifi/WifiScanner.java +++ b/wifi/java/android/net/wifi/WifiScanner.java @@ -153,12 +153,17 @@ public class WifiScanner { dest.writeInt(band); dest.writeInt(periodInMs); dest.writeInt(reportEvents); - dest.writeInt(channels.length); - for (int i = 0; i < channels.length; i++) { - dest.writeInt(channels[i].frequency); - dest.writeInt(channels[i].dwellTimeMS); - dest.writeInt(channels[i].passive ? 1 : 0); + if (channels != null) { + dest.writeInt(channels.length); + + for (int i = 0; i < channels.length; i++) { + dest.writeInt(channels[i].frequency); + dest.writeInt(channels[i].dwellTimeMS); + dest.writeInt(channels[i].passive ? 1 : 0); + } + } else { + dest.writeInt(0); } } @@ -211,10 +216,14 @@ public class WifiScanner { /** Implement the Parcelable interface {@hide} */ public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mResults.length); - for (int i = 0; i < mResults.length; i++) { - ScanResult result = mResults[i]; - result.writeToParcel(dest, flags); + if (mResults != null) { + dest.writeInt(mResults.length); + for (int i = 0; i < mResults.length; i++) { + ScanResult result = mResults[i]; + result.writeToParcel(dest, flags); + } + } else { + dest.writeInt(0); } } @@ -324,13 +333,17 @@ public class WifiScanner { dest.writeInt(unchangedSampleSize); dest.writeInt(minApsBreachingThreshold); dest.writeInt(periodInMs); - dest.writeInt(hotspotInfos.length); - for (int i = 0; i < hotspotInfos.length; i++) { - HotspotInfo info = hotspotInfos[i]; - dest.writeString(info.bssid); - dest.writeInt(info.low); - dest.writeInt(info.high); - dest.writeInt(info.frequencyHint); + if (hotspotInfos != null) { + dest.writeInt(hotspotInfos.length); + for (int i = 0; i < hotspotInfos.length; i++) { + HotspotInfo info = hotspotInfos[i]; + dest.writeString(info.bssid); + dest.writeInt(info.low); + dest.writeInt(info.high); + dest.writeInt(info.frequencyHint); + } + } else { + dest.writeInt(0); } } @@ -456,13 +469,18 @@ public class WifiScanner { /** Implement the Parcelable interface {@hide} */ public void writeToParcel(Parcel dest, int flags) { dest.writeInt(apLostThreshold); - dest.writeInt(hotspotInfos.length); - for (int i = 0; i < hotspotInfos.length; i++) { - HotspotInfo info = hotspotInfos[i]; - dest.writeString(info.bssid); - dest.writeInt(info.low); - dest.writeInt(info.high); - dest.writeInt(info.frequencyHint); + + if (hotspotInfos != null) { + dest.writeInt(hotspotInfos.length); + for (int i = 0; i < hotspotInfos.length; i++) { + HotspotInfo info = hotspotInfos[i]; + dest.writeString(info.bssid); + dest.writeInt(info.low); + dest.writeInt(info.high); + dest.writeInt(info.frequencyHint); + } + } else { + dest.writeInt(0); } } @@ -680,6 +698,42 @@ public class WifiScanner { } } + /** @hide */ + public static class OperationResult implements Parcelable { + public int reason; + public String description; + + public OperationResult(int reason, String description) { + this.reason = reason; + this.description = description; + } + + /** Implement the Parcelable interface {@hide} */ + public int describeContents() { + return 0; + } + + /** Implement the Parcelable interface {@hide} */ + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(reason); + dest.writeString(description); + } + + /** Implement the Parcelable interface {@hide} */ + public static final Creator<OperationResult> CREATOR = + new Creator<OperationResult>() { + public OperationResult createFromParcel(Parcel in) { + int reason = in.readInt(); + String description = in.readString(); + return new OperationResult(reason, description); + } + + public OperationResult[] newArray(int size) { + return new OperationResult[size]; + } + }; + } + private static class ServiceHandler extends Handler { ServiceHandler(Looper looper) { super(looper); @@ -717,9 +771,11 @@ public class WifiScanner { case CMD_OP_SUCCEEDED : ((ActionListener) listener).onSuccess(); break; - case CMD_OP_FAILED : - ((ActionListener) listener).onFailure(msg.arg1, (String)msg.obj); - removeListener(msg.arg2); + case CMD_OP_FAILED : { + OperationResult result = (OperationResult)msg.obj; + ((ActionListener) listener).onFailure(result.reason, result.description); + removeListener(msg.arg2); + } break; case CMD_SCAN_RESULT : ((ScanListener) listener).onResults( |
