Merge "Update docs for WebResourceRequest.hasUserGesture."
diff --git a/api/current.txt b/api/current.txt
index 7ea866a..74a97b6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -482,7 +482,7 @@
field public static final int dialogTitle = 16843250; // 0x10101f2
field public static final int digits = 16843110; // 0x1010166
field public static final int direction = 16843217; // 0x10101d1
- field public static final int directionDescriptions = 16843681; // 0x10103a1
+ field public static final deprecated int directionDescriptions = 16843681; // 0x10103a1
field public static final int directionPriority = 16843218; // 0x10101d2
field public static final int disableDependentsState = 16843249; // 0x10101f1
field public static final int disabledAlpha = 16842803; // 0x1010033
@@ -1200,7 +1200,7 @@
field public static final int tag = 16842961; // 0x10100d1
field public static final int targetActivity = 16843266; // 0x1010202
field public static final int targetClass = 16842799; // 0x101002f
- field public static final int targetDescriptions = 16843680; // 0x10103a0
+ field public static final deprecated int targetDescriptions = 16843680; // 0x10103a0
field public static final int targetId = 16843740; // 0x10103dc
field public static final int targetName = 16843853; // 0x101044d
field public static final int targetPackage = 16842785; // 0x1010021
@@ -1316,6 +1316,8 @@
field public static final int topRightRadius = 16843178; // 0x10101aa
field public static final int touchscreenBlocksFocus = 16843919; // 0x101048f
field public static final int track = 16843631; // 0x101036f
+ field public static final int trackTint = 16843993; // 0x10104d9
+ field public static final int trackTintMode = 16843994; // 0x10104da
field public static final int transcriptMode = 16843008; // 0x1010100
field public static final int transformPivotX = 16843552; // 0x1010320
field public static final int transformPivotY = 16843553; // 0x1010321
@@ -4767,6 +4769,37 @@
method public android.app.Notification.Builder setWhen(long);
}
+ public static final class Notification.CarExtender implements android.app.Notification.Extender {
+ ctor public Notification.CarExtender();
+ ctor public Notification.CarExtender(android.app.Notification);
+ method public android.app.Notification.Builder extend(android.app.Notification.Builder);
+ method public int getColor();
+ method public android.graphics.Bitmap getLargeIcon();
+ method public android.app.Notification.CarExtender.UnreadConversation getUnreadConversation();
+ method public android.app.Notification.CarExtender setColor(int);
+ method public android.app.Notification.CarExtender setLargeIcon(android.graphics.Bitmap);
+ method public android.app.Notification.CarExtender setUnreadConversation(android.app.Notification.CarExtender.UnreadConversation);
+ }
+
+ public static class Notification.CarExtender.Builder {
+ ctor public Notification.CarExtender.Builder(java.lang.String);
+ method public android.app.Notification.CarExtender.Builder addMessage(java.lang.String);
+ method public android.app.Notification.CarExtender.UnreadConversation build();
+ method public android.app.Notification.CarExtender.Builder setLatestTimestamp(long);
+ method public android.app.Notification.CarExtender.Builder setReadPendingIntent(android.app.PendingIntent);
+ method public android.app.Notification.CarExtender.Builder setReplyAction(android.app.PendingIntent, android.app.RemoteInput);
+ }
+
+ public static class Notification.CarExtender.UnreadConversation {
+ method public long getLatestTimestamp();
+ method public java.lang.String[] getMessages();
+ method public java.lang.String getParticipant();
+ method public java.lang.String[] getParticipants();
+ method public android.app.PendingIntent getReadPendingIntent();
+ method public android.app.RemoteInput getRemoteInput();
+ method public android.app.PendingIntent getReplyPendingIntent();
+ }
+
public static abstract interface Notification.Extender {
method public abstract android.app.Notification.Builder extend(android.app.Notification.Builder);
}
@@ -12616,6 +12649,8 @@
field public static final int CAMERA_DISABLED = 1; // 0x1
field public static final int CAMERA_DISCONNECTED = 2; // 0x2
field public static final int CAMERA_ERROR = 3; // 0x3
+ field public static final int CAMERA_IN_USE = 4; // 0x4
+ field public static final int MAX_CAMERAS_IN_USE = 5; // 0x5
}
public abstract class CameraCaptureSession implements java.lang.AutoCloseable {
@@ -12682,6 +12717,7 @@
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> LENS_INFO_MINIMUM_FOCUS_DISTANCE;
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES;
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> REQUEST_AVAILABLE_CAPABILITIES;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_INPUT_STREAMS;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_PROC;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_PROC_STALLING;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_RAW;
@@ -12756,7 +12792,10 @@
method public java.lang.String[] getCameraIdList() throws android.hardware.camera2.CameraAccessException;
method public void openCamera(java.lang.String, android.hardware.camera2.CameraDevice.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public void registerAvailabilityCallback(android.hardware.camera2.CameraManager.AvailabilityCallback, android.os.Handler);
+ method public void registerTorchCallback(android.hardware.camera2.CameraManager.TorchCallback, android.os.Handler);
+ method public void setTorchMode(java.lang.String, boolean) throws android.hardware.camera2.CameraAccessException;
method public void unregisterAvailabilityCallback(android.hardware.camera2.CameraManager.AvailabilityCallback);
+ method public void unregisterTorchCallback(android.hardware.camera2.CameraManager.TorchCallback);
}
public static abstract class CameraManager.AvailabilityCallback {
@@ -12765,6 +12804,13 @@
method public void onCameraUnavailable(java.lang.String);
}
+ public static abstract class CameraManager.TorchCallback {
+ ctor public CameraManager.TorchCallback();
+ method public void onTorchModeAvailable(java.lang.String);
+ method public void onTorchModeChanged(java.lang.String, boolean);
+ method public void onTorchModeUnavailable(java.lang.String);
+ }
+
public abstract class CameraMetadata {
method public java.util.List<TKey> getKeys();
field public static final int COLOR_CORRECTION_ABERRATION_MODE_FAST = 1; // 0x1
@@ -12889,13 +12935,16 @@
field public static final int LENS_STATE_STATIONARY = 0; // 0x0
field public static final int NOISE_REDUCTION_MODE_FAST = 1; // 0x1
field public static final int NOISE_REDUCTION_MODE_HIGH_QUALITY = 2; // 0x2
+ field public static final int NOISE_REDUCTION_MODE_MINIMAL = 3; // 0x3
field public static final int NOISE_REDUCTION_MODE_OFF = 0; // 0x0
field public static final int REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE = 0; // 0x0
field public static final int REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE = 6; // 0x6
field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2; // 0x2
field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 1; // 0x1
+ field public static final int REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING = 4; // 0x4
field public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3; // 0x3
field public static final int REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS = 5; // 0x5
+ field public static final int REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING = 7; // 0x7
field public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0; // 0x0
field public static final int SCALER_CROPPING_TYPE_FREEFORM = 1; // 0x1
field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR = 3; // 0x3
@@ -13001,6 +13050,7 @@
field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> LENS_FOCUS_DISTANCE;
field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> LENS_OPTICAL_STABILIZATION_MODE;
field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> NOISE_REDUCTION_MODE;
+ field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR;
field public static final android.hardware.camera2.CaptureRequest.Key<android.graphics.Rect> SCALER_CROP_REGION;
field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Long> SENSOR_EXPOSURE_TIME;
field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Long> SENSOR_FRAME_DURATION;
@@ -13078,6 +13128,7 @@
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> LENS_OPTICAL_STABILIZATION_MODE;
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> LENS_STATE;
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> NOISE_REDUCTION_MODE;
+ field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR;
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Byte> REQUEST_PIPELINE_DEPTH;
field public static final android.hardware.camera2.CaptureResult.Key<android.graphics.Rect> SCALER_CROP_REGION;
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Long> SENSOR_EXPOSURE_TIME;
@@ -14117,6 +14168,7 @@
method public boolean isMicrophoneMute();
method public boolean isMusicActive();
method public boolean isSpeakerphoneOn();
+ method public boolean isStreamMute(int);
method public boolean isVolumeFixed();
method public deprecated boolean isWiredHeadsetOn();
method public void loadSoundEffects();
@@ -14135,8 +14187,8 @@
method public void setRingerMode(int);
method public deprecated void setRouting(int, int, int);
method public void setSpeakerphoneOn(boolean);
- method public void setStreamMute(int, boolean);
- method public void setStreamSolo(int, boolean);
+ method public deprecated void setStreamMute(int, boolean);
+ method public deprecated void setStreamSolo(int, boolean);
method public void setStreamVolume(int, int, int);
method public deprecated void setVibrateSetting(int, int);
method public deprecated void setWiredHeadsetOn(boolean);
@@ -14154,8 +14206,11 @@
field public static final deprecated java.lang.String ACTION_SCO_AUDIO_STATE_CHANGED = "android.media.SCO_AUDIO_STATE_CHANGED";
field public static final java.lang.String ACTION_SCO_AUDIO_STATE_UPDATED = "android.media.ACTION_SCO_AUDIO_STATE_UPDATED";
field public static final int ADJUST_LOWER = -1; // 0xffffffff
+ field public static final int ADJUST_MUTE = -100; // 0xffffff9c
field public static final int ADJUST_RAISE = 1; // 0x1
field public static final int ADJUST_SAME = 0; // 0x0
+ field public static final int ADJUST_TOGGLE_MUTE = 101; // 0x65
+ field public static final int ADJUST_UNMUTE = 100; // 0x64
field public static final int AUDIOFOCUS_GAIN = 1; // 0x1
field public static final int AUDIOFOCUS_GAIN_TRANSIENT = 2; // 0x2
field public static final int AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE = 4; // 0x4
@@ -16205,6 +16260,7 @@
method public void connect();
method public void disconnect();
method public android.os.Bundle getExtras();
+ method public void getMediaItem(java.lang.String, android.media.browse.MediaBrowser.MediaItemCallback);
method public java.lang.String getRoot();
method public android.content.ComponentName getServiceComponent();
method public android.media.session.MediaSession.Token getSessionToken();
@@ -16234,6 +16290,12 @@
field public static final int FLAG_PLAYABLE = 2; // 0x2
}
+ public static abstract class MediaBrowser.MediaItemCallback {
+ ctor public MediaBrowser.MediaItemCallback();
+ method public void onError();
+ method public void onMediaItemLoaded(android.media.browse.MediaBrowser.MediaItem);
+ }
+
public static abstract class MediaBrowser.SubscriptionCallback {
ctor public MediaBrowser.SubscriptionCallback();
method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
@@ -21770,6 +21832,7 @@
field public static final int KITKAT_WATCH = 20; // 0x14
field public static final int LOLLIPOP = 21; // 0x15
field public static final int LOLLIPOP_MR1 = 22; // 0x16
+ field public static final int MNC = 10000; // 0x2710
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -27348,6 +27411,7 @@
public abstract class MediaBrowserService extends android.app.Service {
ctor public MediaBrowserService();
method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+ method public void getMediaItem(java.lang.String, android.service.media.MediaBrowserService.Result<android.media.browse.MediaBrowser.MediaItem>) throws java.lang.UnsupportedOperationException;
method public android.media.session.MediaSession.Token getSessionToken();
method public void notifyChildrenChanged(java.lang.String);
method public android.os.IBinder onBind(android.content.Intent);
@@ -34084,6 +34148,7 @@
method public void setOnHoverListener(android.view.View.OnHoverListener);
method public void setOnKeyListener(android.view.View.OnKeyListener);
method public void setOnLongClickListener(android.view.View.OnLongClickListener);
+ method public void setOnScrollChangeListener(android.view.View.OnScrollChangeListener);
method public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener);
method public void setOnTouchListener(android.view.View.OnTouchListener);
method public void setOutlineProvider(android.view.ViewOutlineProvider);
@@ -34351,6 +34416,10 @@
method public abstract boolean onLongClick(android.view.View);
}
+ public static abstract interface View.OnScrollChangeListener {
+ method public abstract void onScrollChange(android.view.View, int, int, int, int);
+ }
+
public static abstract interface View.OnSystemUiVisibilityChangeListener {
method public abstract void onSystemUiVisibilityChange(int);
}
@@ -38390,6 +38459,8 @@
method public void setClippingEnabled(boolean);
method public void setContentView(android.view.View);
method public void setElevation(float);
+ method public void setEnterTransition(android.transition.Transition);
+ method public void setExitTransition(android.transition.Transition);
method public void setFocusable(boolean);
method public void setHeight(int);
method public void setIgnoreCheekPress();
@@ -38991,7 +39062,11 @@
method public java.lang.CharSequence getTextOn();
method public android.graphics.drawable.Drawable getThumbDrawable();
method public int getThumbTextPadding();
+ method public android.content.res.ColorStateList getThumbTintList();
+ method public android.graphics.PorterDuff.Mode getThumbTintMode();
method public android.graphics.drawable.Drawable getTrackDrawable();
+ method public android.content.res.ColorStateList getTrackTintList();
+ method public android.graphics.PorterDuff.Mode getTrackTintMode();
method public void onMeasure(int, int);
method public void setShowText(boolean);
method public void setSplitTrack(boolean);
@@ -39005,8 +39080,12 @@
method public void setThumbDrawable(android.graphics.drawable.Drawable);
method public void setThumbResource(int);
method public void setThumbTextPadding(int);
+ method public void setThumbTintList(android.content.res.ColorStateList);
+ method public void setThumbTintMode(android.graphics.PorterDuff.Mode);
method public void setTrackDrawable(android.graphics.drawable.Drawable);
method public void setTrackResource(int);
+ method public void setTrackTintList(android.content.res.ColorStateList);
+ method public void setTrackTintMode(android.graphics.PorterDuff.Mode);
}
public class TabHost extends android.widget.FrameLayout implements android.view.ViewTreeObserver.OnTouchModeChangeListener {
diff --git a/api/system-current.txt b/api/system-current.txt
index 05100b0..27b7a91 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -554,7 +554,7 @@
field public static final int dialogTitle = 16843250; // 0x10101f2
field public static final int digits = 16843110; // 0x1010166
field public static final int direction = 16843217; // 0x10101d1
- field public static final int directionDescriptions = 16843681; // 0x10103a1
+ field public static final deprecated int directionDescriptions = 16843681; // 0x10103a1
field public static final int directionPriority = 16843218; // 0x10101d2
field public static final int disableDependentsState = 16843249; // 0x10101f1
field public static final int disabledAlpha = 16842803; // 0x1010033
@@ -1276,7 +1276,7 @@
field public static final int tag = 16842961; // 0x10100d1
field public static final int targetActivity = 16843266; // 0x1010202
field public static final int targetClass = 16842799; // 0x101002f
- field public static final int targetDescriptions = 16843680; // 0x10103a0
+ field public static final deprecated int targetDescriptions = 16843680; // 0x10103a0
field public static final int targetId = 16843740; // 0x10103dc
field public static final int targetName = 16843853; // 0x101044d
field public static final int targetPackage = 16842785; // 0x1010021
@@ -1392,6 +1392,8 @@
field public static final int topRightRadius = 16843178; // 0x10101aa
field public static final int touchscreenBlocksFocus = 16843919; // 0x101048f
field public static final int track = 16843631; // 0x101036f
+ field public static final int trackTint = 16843993; // 0x10104d9
+ field public static final int trackTintMode = 16843994; // 0x10104da
field public static final int transcriptMode = 16843008; // 0x1010100
field public static final int transformPivotX = 16843552; // 0x1010320
field public static final int transformPivotY = 16843553; // 0x1010321
@@ -4857,6 +4859,37 @@
method public android.app.Notification.Builder setWhen(long);
}
+ public static final class Notification.CarExtender implements android.app.Notification.Extender {
+ ctor public Notification.CarExtender();
+ ctor public Notification.CarExtender(android.app.Notification);
+ method public android.app.Notification.Builder extend(android.app.Notification.Builder);
+ method public int getColor();
+ method public android.graphics.Bitmap getLargeIcon();
+ method public android.app.Notification.CarExtender.UnreadConversation getUnreadConversation();
+ method public android.app.Notification.CarExtender setColor(int);
+ method public android.app.Notification.CarExtender setLargeIcon(android.graphics.Bitmap);
+ method public android.app.Notification.CarExtender setUnreadConversation(android.app.Notification.CarExtender.UnreadConversation);
+ }
+
+ public static class Notification.CarExtender.Builder {
+ ctor public Notification.CarExtender.Builder(java.lang.String);
+ method public android.app.Notification.CarExtender.Builder addMessage(java.lang.String);
+ method public android.app.Notification.CarExtender.UnreadConversation build();
+ method public android.app.Notification.CarExtender.Builder setLatestTimestamp(long);
+ method public android.app.Notification.CarExtender.Builder setReadPendingIntent(android.app.PendingIntent);
+ method public android.app.Notification.CarExtender.Builder setReplyAction(android.app.PendingIntent, android.app.RemoteInput);
+ }
+
+ public static class Notification.CarExtender.UnreadConversation {
+ method public long getLatestTimestamp();
+ method public java.lang.String[] getMessages();
+ method public java.lang.String getParticipant();
+ method public java.lang.String[] getParticipants();
+ method public android.app.PendingIntent getReadPendingIntent();
+ method public android.app.RemoteInput getRemoteInput();
+ method public android.app.PendingIntent getReplyPendingIntent();
+ }
+
public static abstract interface Notification.Extender {
method public abstract android.app.Notification.Builder extend(android.app.Notification.Builder);
}
@@ -12884,6 +12917,8 @@
field public static final int CAMERA_DISABLED = 1; // 0x1
field public static final int CAMERA_DISCONNECTED = 2; // 0x2
field public static final int CAMERA_ERROR = 3; // 0x3
+ field public static final int CAMERA_IN_USE = 4; // 0x4
+ field public static final int MAX_CAMERAS_IN_USE = 5; // 0x5
}
public abstract class CameraCaptureSession implements java.lang.AutoCloseable {
@@ -12950,6 +12985,7 @@
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> LENS_INFO_MINIMUM_FOCUS_DISTANCE;
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES;
field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> REQUEST_AVAILABLE_CAPABILITIES;
+ field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_INPUT_STREAMS;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_PROC;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_PROC_STALLING;
field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_RAW;
@@ -13024,7 +13060,10 @@
method public java.lang.String[] getCameraIdList() throws android.hardware.camera2.CameraAccessException;
method public void openCamera(java.lang.String, android.hardware.camera2.CameraDevice.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public void registerAvailabilityCallback(android.hardware.camera2.CameraManager.AvailabilityCallback, android.os.Handler);
+ method public void registerTorchCallback(android.hardware.camera2.CameraManager.TorchCallback, android.os.Handler);
+ method public void setTorchMode(java.lang.String, boolean) throws android.hardware.camera2.CameraAccessException;
method public void unregisterAvailabilityCallback(android.hardware.camera2.CameraManager.AvailabilityCallback);
+ method public void unregisterTorchCallback(android.hardware.camera2.CameraManager.TorchCallback);
}
public static abstract class CameraManager.AvailabilityCallback {
@@ -13033,6 +13072,13 @@
method public void onCameraUnavailable(java.lang.String);
}
+ public static abstract class CameraManager.TorchCallback {
+ ctor public CameraManager.TorchCallback();
+ method public void onTorchModeAvailable(java.lang.String);
+ method public void onTorchModeChanged(java.lang.String, boolean);
+ method public void onTorchModeUnavailable(java.lang.String);
+ }
+
public abstract class CameraMetadata {
method public java.util.List<TKey> getKeys();
field public static final int COLOR_CORRECTION_ABERRATION_MODE_FAST = 1; // 0x1
@@ -13157,13 +13203,16 @@
field public static final int LENS_STATE_STATIONARY = 0; // 0x0
field public static final int NOISE_REDUCTION_MODE_FAST = 1; // 0x1
field public static final int NOISE_REDUCTION_MODE_HIGH_QUALITY = 2; // 0x2
+ field public static final int NOISE_REDUCTION_MODE_MINIMAL = 3; // 0x3
field public static final int NOISE_REDUCTION_MODE_OFF = 0; // 0x0
field public static final int REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE = 0; // 0x0
field public static final int REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE = 6; // 0x6
field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2; // 0x2
field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 1; // 0x1
+ field public static final int REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING = 4; // 0x4
field public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3; // 0x3
field public static final int REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS = 5; // 0x5
+ field public static final int REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING = 7; // 0x7
field public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0; // 0x0
field public static final int SCALER_CROPPING_TYPE_FREEFORM = 1; // 0x1
field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR = 3; // 0x3
@@ -13269,6 +13318,7 @@
field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> LENS_FOCUS_DISTANCE;
field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> LENS_OPTICAL_STABILIZATION_MODE;
field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> NOISE_REDUCTION_MODE;
+ field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR;
field public static final android.hardware.camera2.CaptureRequest.Key<android.graphics.Rect> SCALER_CROP_REGION;
field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Long> SENSOR_EXPOSURE_TIME;
field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Long> SENSOR_FRAME_DURATION;
@@ -13346,6 +13396,7 @@
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> LENS_OPTICAL_STABILIZATION_MODE;
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> LENS_STATE;
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> NOISE_REDUCTION_MODE;
+ field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR;
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Byte> REQUEST_PIPELINE_DEPTH;
field public static final android.hardware.camera2.CaptureResult.Key<android.graphics.Rect> SCALER_CROP_REGION;
field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Long> SENSOR_EXPOSURE_TIME;
@@ -15095,6 +15146,7 @@
method public boolean isMicrophoneMute();
method public boolean isMusicActive();
method public boolean isSpeakerphoneOn();
+ method public boolean isStreamMute(int);
method public boolean isVolumeFixed();
method public deprecated boolean isWiredHeadsetOn();
method public void loadSoundEffects();
@@ -15116,8 +15168,8 @@
method public void setRingerMode(int);
method public deprecated void setRouting(int, int, int);
method public void setSpeakerphoneOn(boolean);
- method public void setStreamMute(int, boolean);
- method public void setStreamSolo(int, boolean);
+ method public deprecated void setStreamMute(int, boolean);
+ method public deprecated void setStreamSolo(int, boolean);
method public void setStreamVolume(int, int, int);
method public deprecated void setVibrateSetting(int, int);
method public deprecated void setWiredHeadsetOn(boolean);
@@ -15136,8 +15188,11 @@
field public static final deprecated java.lang.String ACTION_SCO_AUDIO_STATE_CHANGED = "android.media.SCO_AUDIO_STATE_CHANGED";
field public static final java.lang.String ACTION_SCO_AUDIO_STATE_UPDATED = "android.media.ACTION_SCO_AUDIO_STATE_UPDATED";
field public static final int ADJUST_LOWER = -1; // 0xffffffff
+ field public static final int ADJUST_MUTE = -100; // 0xffffff9c
field public static final int ADJUST_RAISE = 1; // 0x1
field public static final int ADJUST_SAME = 0; // 0x0
+ field public static final int ADJUST_TOGGLE_MUTE = 101; // 0x65
+ field public static final int ADJUST_UNMUTE = 100; // 0x64
field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1
field public static final int AUDIOFOCUS_FLAG_LOCK = 4; // 0x4
field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2
@@ -17255,6 +17310,7 @@
method public void connect();
method public void disconnect();
method public android.os.Bundle getExtras();
+ method public void getMediaItem(java.lang.String, android.media.browse.MediaBrowser.MediaItemCallback);
method public java.lang.String getRoot();
method public android.content.ComponentName getServiceComponent();
method public android.media.session.MediaSession.Token getSessionToken();
@@ -17284,6 +17340,12 @@
field public static final int FLAG_PLAYABLE = 2; // 0x2
}
+ public static abstract class MediaBrowser.MediaItemCallback {
+ ctor public MediaBrowser.MediaItemCallback();
+ method public void onError();
+ method public void onMediaItemLoaded(android.media.browse.MediaBrowser.MediaItem);
+ }
+
public static abstract class MediaBrowser.SubscriptionCallback {
ctor public MediaBrowser.SubscriptionCallback();
method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
@@ -23349,6 +23411,7 @@
field public static final int KITKAT_WATCH = 20; // 0x14
field public static final int LOLLIPOP = 21; // 0x15
field public static final int LOLLIPOP_MR1 = 22; // 0x16
+ field public static final int MNC = 10000; // 0x2710
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -28937,6 +29000,7 @@
public abstract class MediaBrowserService extends android.app.Service {
ctor public MediaBrowserService();
method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+ method public void getMediaItem(java.lang.String, android.service.media.MediaBrowserService.Result<android.media.browse.MediaBrowser.MediaItem>) throws java.lang.UnsupportedOperationException;
method public android.media.session.MediaSession.Token getSessionToken();
method public void notifyChildrenChanged(java.lang.String);
method public android.os.IBinder onBind(android.content.Intent);
@@ -36283,6 +36347,7 @@
method public void setOnHoverListener(android.view.View.OnHoverListener);
method public void setOnKeyListener(android.view.View.OnKeyListener);
method public void setOnLongClickListener(android.view.View.OnLongClickListener);
+ method public void setOnScrollChangeListener(android.view.View.OnScrollChangeListener);
method public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener);
method public void setOnTouchListener(android.view.View.OnTouchListener);
method public void setOutlineProvider(android.view.ViewOutlineProvider);
@@ -36550,6 +36615,10 @@
method public abstract boolean onLongClick(android.view.View);
}
+ public static abstract interface View.OnScrollChangeListener {
+ method public abstract void onScrollChange(android.view.View, int, int, int, int);
+ }
+
public static abstract interface View.OnSystemUiVisibilityChangeListener {
method public abstract void onSystemUiVisibilityChange(int);
}
@@ -40885,6 +40954,8 @@
method public void setClippingEnabled(boolean);
method public void setContentView(android.view.View);
method public void setElevation(float);
+ method public void setEnterTransition(android.transition.Transition);
+ method public void setExitTransition(android.transition.Transition);
method public void setFocusable(boolean);
method public void setHeight(int);
method public void setIgnoreCheekPress();
@@ -41486,7 +41557,11 @@
method public java.lang.CharSequence getTextOn();
method public android.graphics.drawable.Drawable getThumbDrawable();
method public int getThumbTextPadding();
+ method public android.content.res.ColorStateList getThumbTintList();
+ method public android.graphics.PorterDuff.Mode getThumbTintMode();
method public android.graphics.drawable.Drawable getTrackDrawable();
+ method public android.content.res.ColorStateList getTrackTintList();
+ method public android.graphics.PorterDuff.Mode getTrackTintMode();
method public void onMeasure(int, int);
method public void setShowText(boolean);
method public void setSplitTrack(boolean);
@@ -41500,8 +41575,12 @@
method public void setThumbDrawable(android.graphics.drawable.Drawable);
method public void setThumbResource(int);
method public void setThumbTextPadding(int);
+ method public void setThumbTintList(android.content.res.ColorStateList);
+ method public void setThumbTintMode(android.graphics.PorterDuff.Mode);
method public void setTrackDrawable(android.graphics.drawable.Drawable);
method public void setTrackResource(int);
+ method public void setTrackTintList(android.content.res.ColorStateList);
+ method public void setTrackTintMode(android.graphics.PorterDuff.Mode);
}
public class TabHost extends android.widget.FrameLayout implements android.view.ViewTreeObserver.OnTouchModeChangeListener {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 2a0ed90..fb3d423 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -1735,6 +1735,22 @@
int right = Integer.valueOf(rightStr);
String bottomStr = nextArgRequired();
int bottom = Integer.valueOf(bottomStr);
+ if (left < 0) {
+ System.err.println("Error: bad left arg: " + leftStr);
+ return;
+ }
+ if (top < 0) {
+ System.err.println("Error: bad top arg: " + topStr);
+ return;
+ }
+ if (right <= 0) {
+ System.err.println("Error: bad right arg: " + rightStr);
+ return;
+ }
+ if (bottom <= 0) {
+ System.err.println("Error: bad bottom arg: " + bottomStr);
+ return;
+ }
try {
mAm.resizeStack(stackId, new Rect(left, top, right, bottom));
diff --git a/cmds/bootanimation/bootanimation_main.cpp b/cmds/bootanimation/bootanimation_main.cpp
index 6550d22..48a34e7 100644
--- a/cmds/bootanimation/bootanimation_main.cpp
+++ b/cmds/bootanimation/bootanimation_main.cpp
@@ -16,20 +16,14 @@
#define LOG_TAG "BootAnimation"
-#include <cutils/properties.h>
-
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
-
+#include <cutils/properties.h>
+#include <sys/resource.h>
#include <utils/Log.h>
#include <utils/threads.h>
-#if defined(HAVE_PTHREADS)
-# include <pthread.h>
-# include <sys/resource.h>
-#endif
-
#include "BootAnimation.h"
using namespace android;
@@ -38,9 +32,7 @@
int main()
{
-#if defined(HAVE_PTHREADS)
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
-#endif
char value[PROPERTY_VALUE_MAX];
property_get("debug.sf.nobootanimation", value, "0");
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 23b05c4..a09bca1 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2373,8 +2373,10 @@
if (mDefaultKeyMode == DEFAULT_KEYS_DISABLE) {
return false;
} else if (mDefaultKeyMode == DEFAULT_KEYS_SHORTCUT) {
- if (getWindow().performPanelShortcut(Window.FEATURE_OPTIONS_PANEL,
- keyCode, event, Menu.FLAG_ALWAYS_PERFORM_CLOSE)) {
+ Window w = getWindow();
+ if (w.hasFeature(Window.FEATURE_OPTIONS_PANEL) &&
+ w.performPanelShortcut(Window.FEATURE_OPTIONS_PANEL, keyCode, event,
+ Menu.FLAG_ALWAYS_PERFORM_CLOSE)) {
return true;
}
return false;
@@ -2939,7 +2941,8 @@
* time it needs to be displayed.
*/
public void invalidateOptionsMenu() {
- if (mActionBar == null || !mActionBar.invalidateOptionsMenu()) {
+ if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL) &&
+ (mActionBar == null || !mActionBar.invalidateOptionsMenu())) {
mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL);
}
}
@@ -3151,7 +3154,8 @@
* open, this method does nothing.
*/
public void openOptionsMenu() {
- if (mActionBar == null || !mActionBar.openOptionsMenu()) {
+ if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL) &&
+ (mActionBar == null || !mActionBar.openOptionsMenu())) {
mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, null);
}
}
@@ -3161,7 +3165,9 @@
* closed, this method does nothing.
*/
public void closeOptionsMenu() {
- mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL);
+ if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL)) {
+ mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL);
+ }
}
/**
@@ -3220,7 +3226,9 @@
* Programmatically closes the most recently opened context menu, if showing.
*/
public void closeContextMenu() {
- mWindow.closePanel(Window.FEATURE_CONTEXT_MENU);
+ if (mWindow.hasFeature(Window.FEATURE_CONTEXT_MENU)) {
+ mWindow.closePanel(Window.FEATURE_CONTEXT_MENU);
+ }
}
/**
@@ -4607,7 +4615,7 @@
if (Looper.myLooper() != mMainThread.getLooper()) {
throw new IllegalStateException("Must be called from main thread");
}
- mMainThread.requestRelaunchActivity(mToken, null, null, 0, false, null, false);
+ mMainThread.requestRelaunchActivity(mToken, null, null, 0, false, null, null, false);
}
/**
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 379fe11..97b9f4c 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -766,6 +766,14 @@
return true;
}
+ case GET_FOCUSED_STACK_ID_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int focusedStackId = getFocusedStackId();
+ reply.writeNoException();
+ reply.writeInt(focusedStackId);
+ return true;
+ }
+
case REGISTER_TASK_STACK_LISTENER_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
@@ -3290,6 +3298,18 @@
reply.recycle();
}
@Override
+ public int getFocusedStackId() throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ mRemote.transact(GET_FOCUSED_STACK_ID_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int focusedStackId = reply.readInt();
+ data.recycle();
+ reply.recycle();
+ return focusedStackId;
+ }
+ @Override
public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException
{
Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ea37b89..de3c95c 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -293,6 +293,7 @@
boolean hideForNow;
Configuration newConfig;
Configuration createdConfig;
+ Configuration overrideConfig;
ActivityClientRecord nextIdle;
ProfilerInfo profilerInfo;
@@ -615,12 +616,13 @@
// we use token to identify this activity without having to send the
// activity itself back to the activity manager. (matters more with ipc)
+ @Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
- ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
- String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state,
- PersistableBundle persistentState, List<ResultInfo> pendingResults,
- List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward,
- ProfilerInfo profilerInfo) {
+ ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
+ CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
+ int procState, Bundle state, PersistableBundle persistentState,
+ List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
+ boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {
updateProcessState(procState, false);
@@ -644,16 +646,19 @@
r.profilerInfo = profilerInfo;
+ r.overrideConfig = overrideConfig;
updatePendingConfiguration(curConfig);
sendMessage(H.LAUNCH_ACTIVITY, r);
}
+ @Override
public final void scheduleRelaunchActivity(IBinder token,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
- int configChanges, boolean notResumed, Configuration config) {
+ int configChanges, boolean notResumed, Configuration config,
+ Configuration overrideConfig) {
requestRelaunchActivity(token, pendingResults, pendingNewIntents,
- configChanges, notResumed, config, true);
+ configChanges, notResumed, config, overrideConfig, true);
}
public final void scheduleNewIntent(List<ReferrerIntent> intents, IBinder token) {
@@ -887,6 +892,7 @@
sendMessage(H.LOW_MEMORY, null);
}
+ @Override
public void scheduleActivityConfigurationChanged(IBinder token) {
sendMessage(H.ACTIVITY_CONFIGURATION_CHANGED, token);
}
@@ -1668,7 +1674,7 @@
String[] libDirs, int displayId, Configuration overrideConfiguration,
LoadedApk pkgInfo) {
return mResourcesManager.getTopLevelResources(resDir, splitResDirs, overlayDirs, libDirs,
- displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo(), null);
+ displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo());
}
final Handler getHandler() {
@@ -2353,7 +2359,8 @@
private Context createBaseContextForActivity(ActivityClientRecord r,
final Activity activity) {
- ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token);
+ ContextImpl appContext =
+ ContextImpl.createActivityContext(this, r.packageInfo, r.overrideConfig);
appContext.setOuterContext(activity);
Context baseContext = appContext;
@@ -3561,7 +3568,7 @@
// request all activities to relaunch for the changes to take place
for (Map.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) {
- requestRelaunchActivity(entry.getKey(), null, null, 0, false, null, false);
+ requestRelaunchActivity(entry.getKey(), null, null, 0, false, null, null, false);
}
}
}
@@ -3805,7 +3812,7 @@
public final void requestRelaunchActivity(IBinder token,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
int configChanges, boolean notResumed, Configuration config,
- boolean fromServer) {
+ Configuration overrideConfig, boolean fromServer) {
ActivityClientRecord target = null;
synchronized (mResourcesManager) {
@@ -3854,6 +3861,9 @@
if (config != null) {
target.createdConfig = config;
}
+ if (overrideConfig != null) {
+ target.overrideConfig = overrideConfig;
+ }
target.pendingConfigChanges |= configChanges;
}
}
@@ -3966,6 +3976,7 @@
}
}
r.startsNotResumed = tmp.startsNotResumed;
+ r.overrideConfig = tmp.overrideConfig;
handleLaunchActivity(r, currentIntent);
}
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index d0d9d71..e3b27b5 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -799,6 +799,15 @@
mIsStartingTransition = false;
}
+ /**
+ * Cancels any pending transitions and returns true if there is a transition is in
+ * the middle of starting.
+ */
+ protected boolean cancelPendingTransitions() {
+ mPendingTransition = null;
+ return mIsStartingTransition;
+ }
+
protected void moveSharedElementsToOverlay() {
if (mWindow == null || !mWindow.getSharedElementsUseOverlay()) {
return;
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 555d20b..a2bfa4e 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -22,6 +22,7 @@
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
import android.view.Window;
import java.lang.ref.WeakReference;
@@ -252,7 +253,7 @@
}
}
- public boolean startExitBackTransition(Activity activity) {
+ public boolean startExitBackTransition(final Activity activity) {
if (mEnteringNames == null) {
return false;
} else {
@@ -260,10 +261,11 @@
mHasExited = true;
Transition enterViewsTransition = null;
ViewGroup decor = null;
+ boolean delayExitBack = false;
if (mEnterTransitionCoordinator != null) {
enterViewsTransition = mEnterTransitionCoordinator.getEnterViewsTransition();
decor = mEnterTransitionCoordinator.getDecor();
- mEnterTransitionCoordinator.cancelEnter();
+ delayExitBack = mEnterTransitionCoordinator.cancelEnter();
mEnterTransitionCoordinator = null;
if (enterViewsTransition != null && decor != null) {
enterViewsTransition.pause(decor);
@@ -275,7 +277,23 @@
if (enterViewsTransition != null && decor != null) {
enterViewsTransition.resume(decor);
}
- mReturnExitCoordinator.startExit(activity.mResultCode, activity.mResultData);
+ if (delayExitBack && decor != null) {
+ final ViewGroup finalDecor = decor;
+ decor.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ finalDecor.getViewTreeObserver().removeOnPreDrawListener(this);
+ if (mReturnExitCoordinator != null) {
+ mReturnExitCoordinator.startExit(activity.mResultCode,
+ activity.mResultData);
+ }
+ return true;
+ }
+ });
+ } else {
+ mReturnExitCoordinator.startExit(activity.mResultCode, activity.mResultData);
+ }
}
return true;
}
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index eb3ddb2..349b66d 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -140,6 +140,10 @@
int ident = data.readInt();
ActivityInfo info = ActivityInfo.CREATOR.createFromParcel(data);
Configuration curConfig = Configuration.CREATOR.createFromParcel(data);
+ Configuration overrideConfig = null;
+ if (data.readInt() != 0) {
+ overrideConfig = Configuration.CREATOR.createFromParcel(data);
+ }
CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
String referrer = data.readString();
IVoiceInteractor voiceInteractor = IVoiceInteractor.Stub.asInterface(
@@ -153,8 +157,8 @@
boolean isForward = data.readInt() != 0;
ProfilerInfo profilerInfo = data.readInt() != 0
? ProfilerInfo.CREATOR.createFromParcel(data) : null;
- scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo, referrer,
- voiceInteractor, procState, state, persistentState, ri, pi,
+ scheduleLaunchActivity(intent, b, ident, info, curConfig, overrideConfig, compatInfo,
+ referrer, voiceInteractor, procState, state, persistentState, ri, pi,
notResumed, isForward, profilerInfo);
return true;
}
@@ -167,14 +171,15 @@
List<ReferrerIntent> pi = data.createTypedArrayList(ReferrerIntent.CREATOR);
int configChanges = data.readInt();
boolean notResumed = data.readInt() != 0;
- Configuration config = null;
+ Configuration config = Configuration.CREATOR.createFromParcel(data);
+ Configuration overrideConfig = null;
if (data.readInt() != 0) {
- config = Configuration.CREATOR.createFromParcel(data);
+ overrideConfig = Configuration.CREATOR.createFromParcel(data);
}
- scheduleRelaunchActivity(b, ri, pi, configChanges, notResumed, config);
+ scheduleRelaunchActivity(b, ri, pi, configChanges, notResumed, config, overrideConfig);
return true;
}
-
+
case SCHEDULE_NEW_INTENT_TRANSACTION:
{
data.enforceInterface(IApplicationThread.descriptor);
@@ -775,11 +780,11 @@
}
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
- ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
- String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state,
- PersistableBundle persistentState, List<ResultInfo> pendingResults,
- List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward,
- ProfilerInfo profilerInfo) throws RemoteException {
+ ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
+ CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
+ int procState, Bundle state, PersistableBundle persistentState,
+ List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
+ boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
intent.writeToParcel(data, 0);
@@ -787,6 +792,12 @@
data.writeInt(ident);
info.writeToParcel(data, 0);
curConfig.writeToParcel(data, 0);
+ if (overrideConfig != null) {
+ data.writeInt(1);
+ overrideConfig.writeToParcel(data, 0);
+ } else {
+ data.writeInt(0);
+ }
compatInfo.writeToParcel(data, 0);
data.writeString(referrer);
data.writeStrongBinder(voiceInteractor != null ? voiceInteractor.asBinder() : null);
@@ -810,8 +821,8 @@
public final void scheduleRelaunchActivity(IBinder token,
List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
- int configChanges, boolean notResumed, Configuration config)
- throws RemoteException {
+ int configChanges, boolean notResumed, Configuration config,
+ Configuration overrideConfig) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeStrongBinder(token);
@@ -819,9 +830,10 @@
data.writeTypedList(pendingNewIntents);
data.writeInt(configChanges);
data.writeInt(notResumed ? 1 : 0);
- if (config != null) {
+ config.writeToParcel(data, 0);
+ if (overrideConfig != null) {
data.writeInt(1);
- config.writeToParcel(data, 0);
+ overrideConfig.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
@@ -1112,8 +1124,7 @@
data.recycle();
}
- public final void scheduleActivityConfigurationChanged(
- IBinder token) throws RemoteException {
+ public final void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeStrongBinder(token);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index e9df8c6..39caf0b 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2264,11 +2264,10 @@
}
static ContextImpl createActivityContext(ActivityThread mainThread,
- LoadedApk packageInfo, IBinder activityToken) {
+ LoadedApk packageInfo, Configuration overrideConfiguration) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
- if (activityToken == null) throw new IllegalArgumentException("activityInfo");
- return new ContextImpl(null, mainThread,
- packageInfo, activityToken, null, false, null, null);
+ return new ContextImpl(null, mainThread, packageInfo, null, null, false, null,
+ overrideConfiguration);
}
private ContextImpl(ContextImpl container, ActivityThread mainThread,
@@ -2303,15 +2302,14 @@
Resources resources = packageInfo.getResources(mainThread);
if (resources != null) {
- if (activityToken != null
- || displayId != Display.DEFAULT_DISPLAY
+ if (displayId != Display.DEFAULT_DISPLAY
|| overrideConfiguration != null
|| (compatInfo != null && compatInfo.applicationScale
!= resources.getCompatibilityInfo().applicationScale)) {
resources = mResourcesManager.getTopLevelResources(packageInfo.getResDir(),
packageInfo.getSplitResDirs(), packageInfo.getOverlayDirs(),
packageInfo.getApplicationInfo().sharedLibraryFiles, displayId,
- overrideConfiguration, compatInfo, activityToken);
+ overrideConfiguration, compatInfo);
}
}
mResources = resources;
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 12d4513..067073a 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -910,21 +910,27 @@
* @see Activity#openOptionsMenu()
*/
public void openOptionsMenu() {
- mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, null);
+ if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL)) {
+ mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, null);
+ }
}
-
+
/**
* @see Activity#closeOptionsMenu()
*/
public void closeOptionsMenu() {
- mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL);
+ if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL)) {
+ mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL);
+ }
}
/**
* @see Activity#invalidateOptionsMenu()
*/
public void invalidateOptionsMenu() {
- mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL);
+ if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL)) {
+ mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL);
+ }
}
/**
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index ecf19c7..c053c83 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -18,7 +18,6 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
-import android.graphics.Matrix;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.ResultReceiver;
@@ -565,7 +564,12 @@
clearState();
}
- public void cancelEnter() {
+ /**
+ * Cancels the enter transition.
+ * @return True if the enter transition is still pending capturing the target state. If so,
+ * any transition started on the decor will do nothing.
+ */
+ public boolean cancelEnter() {
setGhostVisibility(View.INVISIBLE);
mHasStopped = true;
mIsCanceled = true;
@@ -576,6 +580,7 @@
}
mActivity = null;
clearState();
+ return super.cancelPendingTransitions();
}
private void makeOpaque() {
diff --git a/core/java/android/app/IActivityContainer.aidl b/core/java/android/app/IActivityContainer.aidl
index 52884f7..ff1175f 100644
--- a/core/java/android/app/IActivityContainer.aidl
+++ b/core/java/android/app/IActivityContainer.aidl
@@ -32,6 +32,7 @@
void checkEmbeddedAllowed(in Intent intent);
void checkEmbeddedAllowedIntentSender(in IIntentSender intentSender);
int getDisplayId();
+ int getStackId();
boolean injectEvent(in InputEvent event);
void release();
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index f152c6f..efc4543 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -139,6 +139,7 @@
public StackInfo getStackInfo(int stackId) throws RemoteException;
public boolean isInHomeStack(int taskId) throws RemoteException;
public void setFocusedStack(int stackId) throws RemoteException;
+ public int getFocusedStackId() throws RemoteException;
public void registerTaskStackListener(ITaskStackListener listener) throws RemoteException;
public int getTaskForActivity(IBinder token, boolean onlyRoot) throws RemoteException;
public ContentProviderHolder getContentProvider(IApplicationThread caller,
@@ -800,4 +801,5 @@
// Start of M transactions
int NOTIFY_CLEARTEXT_NETWORK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+280;
int CREATE_STACK_ON_DISPLAY = IBinder.FIRST_CALL_TRANSACTION+281;
+ int GET_FOCUSED_STACK_ID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+282;
}
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 8bf8cd7..f2c912e 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -33,7 +33,6 @@
import android.os.RemoteException;
import android.os.IBinder;
import android.os.IInterface;
-import android.service.voice.IVoiceInteractionSession;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.ReferrerIntent;
@@ -59,14 +58,14 @@
throws RemoteException;
void scheduleSendResult(IBinder token, List<ResultInfo> results) throws RemoteException;
void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
- ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
- String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state,
- PersistableBundle persistentState, List<ResultInfo> pendingResults,
- List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward,
- ProfilerInfo profilerInfo) throws RemoteException;
+ ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
+ CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
+ int procState, Bundle state, PersistableBundle persistentState,
+ List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
+ boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) throws RemoteException;
void scheduleRelaunchActivity(IBinder token, List<ResultInfo> pendingResults,
- List<ReferrerIntent> pendingNewIntents, int configChanges,
- boolean notResumed, Configuration config) throws RemoteException;
+ List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed,
+ Configuration config, Configuration overrideConfig) throws RemoteException;
void scheduleNewIntent(List<ReferrerIntent> intent, IBinder token) throws RemoteException;
void scheduleDestroyActivity(IBinder token, boolean finished,
int configChanges) throws RemoteException;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 860b9cc..87e744c 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5065,6 +5065,403 @@
}
/**
+ * <p>Helper class to add Android Auto extensions to notifications. To create a notification
+ * with car extensions:
+ *
+ * <ol>
+ * <li>Create an {@link Notification.Builder}, setting any desired
+ * properties.
+ * <li>Create a {@link CarExtender}.
+ * <li>Set car-specific properties using the {@code add} and {@code set} methods of
+ * {@link CarExtender}.
+ * <li>Call {@link Notification.Builder#extend(Notification.Extender)}
+ * to apply the extensions to a notification.
+ * </ol>
+ *
+ * <pre class="prettyprint">
+ * Notification notification = new Notification.Builder(context)
+ * ...
+ * .extend(new CarExtender()
+ * .set*(...))
+ * .build();
+ * </pre>
+ *
+ * <p>Car extensions can be accessed on an existing notification by using the
+ * {@code CarExtender(Notification)} constructor, and then using the {@code get} methods
+ * to access values.
+ */
+ public static final class CarExtender implements Extender {
+ private static final String TAG = "CarExtender";
+
+ private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
+ private static final String EXTRA_LARGE_ICON = "large_icon";
+ private static final String EXTRA_CONVERSATION = "car_conversation";
+ private static final String EXTRA_COLOR = "app_color";
+
+ private Bitmap mLargeIcon;
+ private UnreadConversation mUnreadConversation;
+ private int mColor = Notification.COLOR_DEFAULT;
+
+ /**
+ * Create a {@link CarExtender} with default options.
+ */
+ public CarExtender() {
+ }
+
+ /**
+ * Create a {@link CarExtender} from the CarExtender options of an existing Notification.
+ *
+ * @param notif The notification from which to copy options.
+ */
+ public CarExtender(Notification notif) {
+ Bundle carBundle = notif.extras == null ?
+ null : notif.extras.getBundle(EXTRA_CAR_EXTENDER);
+ if (carBundle != null) {
+ mLargeIcon = carBundle.getParcelable(EXTRA_LARGE_ICON);
+ mColor = carBundle.getInt(EXTRA_COLOR, Notification.COLOR_DEFAULT);
+
+ Bundle b = carBundle.getBundle(EXTRA_CONVERSATION);
+ mUnreadConversation = UnreadConversation.getUnreadConversationFromBundle(b);
+ }
+ }
+
+ /**
+ * Apply car extensions to a notification that is being built. This is typically called by
+ * the {@link Notification.Builder#extend(Notification.Extender)}
+ * method of {@link Notification.Builder}.
+ */
+ @Override
+ public Notification.Builder extend(Notification.Builder builder) {
+ Bundle carExtensions = new Bundle();
+
+ if (mLargeIcon != null) {
+ carExtensions.putParcelable(EXTRA_LARGE_ICON, mLargeIcon);
+ }
+ if (mColor != Notification.COLOR_DEFAULT) {
+ carExtensions.putInt(EXTRA_COLOR, mColor);
+ }
+
+ if (mUnreadConversation != null) {
+ Bundle b = mUnreadConversation.getBundleForUnreadConversation();
+ carExtensions.putBundle(EXTRA_CONVERSATION, b);
+ }
+
+ builder.getExtras().putBundle(EXTRA_CAR_EXTENDER, carExtensions);
+ return builder;
+ }
+
+ /**
+ * Sets the accent color to use when Android Auto presents the notification.
+ *
+ * Android Auto uses the color set with {@link Notification.Builder#setColor(int)}
+ * to accent the displayed notification. However, not all colors are acceptable in an
+ * automotive setting. This method can be used to override the color provided in the
+ * notification in such a situation.
+ */
+ public CarExtender setColor(int color) {
+ mColor = color;
+ return this;
+ }
+
+ /**
+ * Gets the accent color.
+ *
+ * @see setColor
+ */
+ public int getColor() {
+ return mColor;
+ }
+
+ /**
+ * Sets the large icon of the car notification.
+ *
+ * If no large icon is set in the extender, Android Auto will display the icon
+ * specified by {@link Notification.Builder#setLargeIcon(android.graphics.Bitmap)}
+ *
+ * @param largeIcon The large icon to use in the car notification.
+ * @return This object for method chaining.
+ */
+ public CarExtender setLargeIcon(Bitmap largeIcon) {
+ mLargeIcon = largeIcon;
+ return this;
+ }
+
+ /**
+ * Gets the large icon used in this car notification, or null if no icon has been set.
+ *
+ * @return The large icon for the car notification.
+ * @see CarExtender#setLargeIcon
+ */
+ public Bitmap getLargeIcon() {
+ return mLargeIcon;
+ }
+
+ /**
+ * Sets the unread conversation in a message notification.
+ *
+ * @param unreadConversation The unread part of the conversation this notification conveys.
+ * @return This object for method chaining.
+ */
+ public CarExtender setUnreadConversation(UnreadConversation unreadConversation) {
+ mUnreadConversation = unreadConversation;
+ return this;
+ }
+
+ /**
+ * Returns the unread conversation conveyed by this notification.
+ * @see #setUnreadConversation(UnreadConversation)
+ */
+ public UnreadConversation getUnreadConversation() {
+ return mUnreadConversation;
+ }
+
+ /**
+ * A class which holds the unread messages from a conversation.
+ */
+ public static class UnreadConversation {
+ private static final String KEY_AUTHOR = "author";
+ private static final String KEY_TEXT = "text";
+ private static final String KEY_MESSAGES = "messages";
+ private static final String KEY_REMOTE_INPUT = "remote_input";
+ private static final String KEY_ON_REPLY = "on_reply";
+ private static final String KEY_ON_READ = "on_read";
+ private static final String KEY_PARTICIPANTS = "participants";
+ private static final String KEY_TIMESTAMP = "timestamp";
+
+ private final String[] mMessages;
+ private final RemoteInput mRemoteInput;
+ private final PendingIntent mReplyPendingIntent;
+ private final PendingIntent mReadPendingIntent;
+ private final String[] mParticipants;
+ private final long mLatestTimestamp;
+
+ UnreadConversation(String[] messages, RemoteInput remoteInput,
+ PendingIntent replyPendingIntent, PendingIntent readPendingIntent,
+ String[] participants, long latestTimestamp) {
+ mMessages = messages;
+ mRemoteInput = remoteInput;
+ mReadPendingIntent = readPendingIntent;
+ mReplyPendingIntent = replyPendingIntent;
+ mParticipants = participants;
+ mLatestTimestamp = latestTimestamp;
+ }
+
+ /**
+ * Gets the list of messages conveyed by this notification.
+ */
+ public String[] getMessages() {
+ return mMessages;
+ }
+
+ /**
+ * Gets the remote input that will be used to convey the response to a message list, or
+ * null if no such remote input exists.
+ */
+ public RemoteInput getRemoteInput() {
+ return mRemoteInput;
+ }
+
+ /**
+ * Gets the pending intent that will be triggered when the user replies to this
+ * notification.
+ */
+ public PendingIntent getReplyPendingIntent() {
+ return mReplyPendingIntent;
+ }
+
+ /**
+ * Gets the pending intent that Android Auto will send after it reads aloud all messages
+ * in this object's message list.
+ */
+ public PendingIntent getReadPendingIntent() {
+ return mReadPendingIntent;
+ }
+
+ /**
+ * Gets the participants in the conversation.
+ */
+ public String[] getParticipants() {
+ return mParticipants;
+ }
+
+ /**
+ * Gets the firs participant in the conversation.
+ */
+ public String getParticipant() {
+ return mParticipants.length > 0 ? mParticipants[0] : null;
+ }
+
+ /**
+ * Gets the timestamp of the conversation.
+ */
+ public long getLatestTimestamp() {
+ return mLatestTimestamp;
+ }
+
+ Bundle getBundleForUnreadConversation() {
+ Bundle b = new Bundle();
+ String author = null;
+ if (mParticipants != null && mParticipants.length > 1) {
+ author = mParticipants[0];
+ }
+ Parcelable[] messages = new Parcelable[mMessages.length];
+ for (int i = 0; i < messages.length; i++) {
+ Bundle m = new Bundle();
+ m.putString(KEY_TEXT, mMessages[i]);
+ m.putString(KEY_AUTHOR, author);
+ messages[i] = m;
+ }
+ b.putParcelableArray(KEY_MESSAGES, messages);
+ if (mRemoteInput != null) {
+ b.putParcelable(KEY_REMOTE_INPUT, mRemoteInput);
+ }
+ b.putParcelable(KEY_ON_REPLY, mReplyPendingIntent);
+ b.putParcelable(KEY_ON_READ, mReadPendingIntent);
+ b.putStringArray(KEY_PARTICIPANTS, mParticipants);
+ b.putLong(KEY_TIMESTAMP, mLatestTimestamp);
+ return b;
+ }
+
+ static UnreadConversation getUnreadConversationFromBundle(Bundle b) {
+ if (b == null) {
+ return null;
+ }
+ Parcelable[] parcelableMessages = b.getParcelableArray(KEY_MESSAGES);
+ String[] messages = null;
+ if (parcelableMessages != null) {
+ String[] tmp = new String[parcelableMessages.length];
+ boolean success = true;
+ for (int i = 0; i < tmp.length; i++) {
+ if (!(parcelableMessages[i] instanceof Bundle)) {
+ success = false;
+ break;
+ }
+ tmp[i] = ((Bundle) parcelableMessages[i]).getString(KEY_TEXT);
+ if (tmp[i] == null) {
+ success = false;
+ break;
+ }
+ }
+ if (success) {
+ messages = tmp;
+ } else {
+ return null;
+ }
+ }
+
+ PendingIntent onRead = b.getParcelable(KEY_ON_READ);
+ PendingIntent onReply = b.getParcelable(KEY_ON_REPLY);
+
+ RemoteInput remoteInput = b.getParcelable(KEY_REMOTE_INPUT);
+
+ String[] participants = b.getStringArray(KEY_PARTICIPANTS);
+ if (participants == null || participants.length != 1) {
+ return null;
+ }
+
+ return new UnreadConversation(messages,
+ remoteInput,
+ onReply,
+ onRead,
+ participants, b.getLong(KEY_TIMESTAMP));
+ }
+ };
+
+ /**
+ * Builder class for {@link CarExtender.UnreadConversation} objects.
+ */
+ public static class Builder {
+ private final List<String> mMessages = new ArrayList<String>();
+ private final String mParticipant;
+ private RemoteInput mRemoteInput;
+ private PendingIntent mReadPendingIntent;
+ private PendingIntent mReplyPendingIntent;
+ private long mLatestTimestamp;
+
+ /**
+ * Constructs a new builder for {@link CarExtender.UnreadConversation}.
+ *
+ * @param name The name of the other participant in the conversation.
+ */
+ public Builder(String name) {
+ mParticipant = name;
+ }
+
+ /**
+ * Appends a new unread message to the list of messages for this conversation.
+ *
+ * The messages should be added from oldest to newest.
+ *
+ * @param message The text of the new unread message.
+ * @return This object for method chaining.
+ */
+ public Builder addMessage(String message) {
+ mMessages.add(message);
+ return this;
+ }
+
+ /**
+ * Sets the pending intent and remote input which will convey the reply to this
+ * notification.
+ *
+ * @param pendingIntent The pending intent which will be triggered on a reply.
+ * @param remoteInput The remote input parcelable which will carry the reply.
+ * @return This object for method chaining.
+ *
+ * @see CarExtender.UnreadConversation#getRemoteInput
+ * @see CarExtender.UnreadConversation#getReplyPendingIntent
+ */
+ public Builder setReplyAction(
+ PendingIntent pendingIntent, RemoteInput remoteInput) {
+ mRemoteInput = remoteInput;
+ mReplyPendingIntent = pendingIntent;
+
+ return this;
+ }
+
+ /**
+ * Sets the pending intent that will be sent once the messages in this notification
+ * are read.
+ *
+ * @param pendingIntent The pending intent to use.
+ * @return This object for method chaining.
+ */
+ public Builder setReadPendingIntent(PendingIntent pendingIntent) {
+ mReadPendingIntent = pendingIntent;
+ return this;
+ }
+
+ /**
+ * Sets the timestamp of the most recent message in an unread conversation.
+ *
+ * If a messaging notification has been posted by your application and has not
+ * yet been cancelled, posting a later notification with the same id and tag
+ * but without a newer timestamp may result in Android Auto not displaying a
+ * heads up notification for the later notification.
+ *
+ * @param timestamp The timestamp of the most recent message in the conversation.
+ * @return This object for method chaining.
+ */
+ public Builder setLatestTimestamp(long timestamp) {
+ mLatestTimestamp = timestamp;
+ return this;
+ }
+
+ /**
+ * Builds a new unread conversation object.
+ *
+ * @return The new unread conversation object.
+ */
+ public UnreadConversation build() {
+ String[] messages = mMessages.toArray(new String[mMessages.size()]);
+ String[] participants = { mParticipant };
+ return new UnreadConversation(messages, mRemoteInput, mReplyPendingIntent,
+ mReadPendingIntent, participants, mLatestTimestamp);
+ }
+ }
+ }
+
+ /**
* Get an array of Notification objects from a parcelable array bundle field.
* Update the bundle to have a typed array so fetches in the future don't need
* to do an array copy.
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 1691d8e..fac40b2 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -25,7 +25,6 @@
import android.content.res.Resources;
import android.content.res.ResourcesKey;
import android.hardware.display.DisplayManagerGlobal;
-import android.os.IBinder;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
import android.util.Slog;
@@ -38,8 +37,7 @@
/** @hide */
public class ResourcesManager {
static final String TAG = "ResourcesManager";
- static final boolean DEBUG_CACHE = false;
- static final boolean DEBUG_STATS = true;
+ private static final boolean DEBUG = false;
private static ResourcesManager sResourcesManager;
final ArrayMap<ResourcesKey, WeakReference<Resources> > mActiveResources
@@ -51,7 +49,6 @@
CompatibilityInfo mResCompatibilityInfo;
Configuration mResConfiguration;
- final Configuration mTmpConfig = new Configuration();
public static ResourcesManager getInstance() {
synchronized (ResourcesManager.class) {
@@ -144,30 +141,32 @@
* Creates the top level Resources for applications with the given compatibility info.
*
* @param resDir the resource directory.
+ * @param splitResDirs split resource directories.
* @param overlayDirs the resource overlay directories.
* @param libDirs the shared library resource dirs this app references.
- * @param compatInfo the compability info. Must not be null.
- * @param token the application token for determining stack bounds.
+ * @param displayId display Id.
+ * @param overrideConfiguration override configurations.
+ * @param compatInfo the compatibility info. Must not be null.
*/
public Resources getTopLevelResources(String resDir, String[] splitResDirs,
String[] overlayDirs, String[] libDirs, int displayId,
- Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) {
+ Configuration overrideConfiguration, CompatibilityInfo compatInfo) {
final float scale = compatInfo.applicationScale;
- ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale, token);
+ Configuration overrideConfigCopy = (overrideConfiguration != null)
+ ? new Configuration(overrideConfiguration) : null;
+ ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfigCopy, scale);
Resources r;
synchronized (this) {
// Resources is app scale dependent.
- if (false) {
- Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale);
- }
+ if (DEBUG) Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale);
+
WeakReference<Resources> wr = mActiveResources.get(key);
r = wr != null ? wr.get() : null;
//if (r != null) Slog.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate());
if (r != null && r.getAssets().isUpToDate()) {
- if (false) {
- Slog.w(TAG, "Returning cached resources " + r + " " + resDir
- + ": appScale=" + r.getCompatibilityInfo().applicationScale);
- }
+ if (DEBUG) Slog.w(TAG, "Returning cached resources " + r + " " + resDir
+ + ": appScale=" + r.getCompatibilityInfo().applicationScale
+ + " key=" + key + " overrideConfig=" + overrideConfiguration);
return r;
}
}
@@ -213,7 +212,7 @@
//Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);
DisplayMetrics dm = getDisplayMetricsLocked(displayId);
Configuration config;
- boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+ final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
final boolean hasOverrideConfig = key.hasOverrideConfiguration();
if (!isDefaultDisplay || hasOverrideConfig) {
config = new Configuration(getConfiguration());
@@ -222,16 +221,14 @@
}
if (hasOverrideConfig) {
config.updateFrom(key.mOverrideConfiguration);
+ if (DEBUG) Slog.v(TAG, "Applied overrideConfig=" + key.mOverrideConfiguration);
}
} else {
config = getConfiguration();
}
- r = new Resources(assets, dm, config, compatInfo, token);
- if (false) {
- Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
- + r.getConfiguration() + " appScale="
- + r.getCompatibilityInfo().applicationScale);
- }
+ r = new Resources(assets, dm, config, compatInfo);
+ if (DEBUG) Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
+ + r.getConfiguration() + " appScale=" + r.getCompatibilityInfo().applicationScale);
synchronized (this) {
WeakReference<Resources> wr = mActiveResources.get(key);
@@ -244,7 +241,8 @@
}
// XXX need to remove entries when weak references go away
- mActiveResources.put(key, new WeakReference<Resources>(r));
+ mActiveResources.put(key, new WeakReference<>(r));
+ if (DEBUG) Slog.v(TAG, "mActiveResources.size()=" + mActiveResources.size());
return r;
}
}
@@ -255,7 +253,7 @@
mResConfiguration = new Configuration();
}
if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
- if (DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
+ if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
+ mResConfiguration.seq + ", newSeq=" + config.seq);
return false;
}
@@ -287,7 +285,7 @@
ResourcesKey key = mActiveResources.keyAt(i);
Resources r = mActiveResources.valueAt(i).get();
if (r != null) {
- if (DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
+ if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
+ r + " config to: " + config);
int displayId = key.mDisplayId;
boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 55c3960..bedec72 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -694,7 +694,7 @@
public void setPasswordQuality(ComponentName admin, int quality) {
if (mService != null) {
try {
- mService.setPasswordQuality(admin, quality, UserHandle.myUserId());
+ mService.setPasswordQuality(admin, quality);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -747,7 +747,7 @@
public void setPasswordMinimumLength(ComponentName admin, int length) {
if (mService != null) {
try {
- mService.setPasswordMinimumLength(admin, length, UserHandle.myUserId());
+ mService.setPasswordMinimumLength(admin, length);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -801,7 +801,7 @@
public void setPasswordMinimumUpperCase(ComponentName admin, int length) {
if (mService != null) {
try {
- mService.setPasswordMinimumUpperCase(admin, length, UserHandle.myUserId());
+ mService.setPasswordMinimumUpperCase(admin, length);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -862,7 +862,7 @@
public void setPasswordMinimumLowerCase(ComponentName admin, int length) {
if (mService != null) {
try {
- mService.setPasswordMinimumLowerCase(admin, length, UserHandle.myUserId());
+ mService.setPasswordMinimumLowerCase(admin, length);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -922,7 +922,7 @@
public void setPasswordMinimumLetters(ComponentName admin, int length) {
if (mService != null) {
try {
- mService.setPasswordMinimumLetters(admin, length, UserHandle.myUserId());
+ mService.setPasswordMinimumLetters(admin, length);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -980,7 +980,7 @@
public void setPasswordMinimumNumeric(ComponentName admin, int length) {
if (mService != null) {
try {
- mService.setPasswordMinimumNumeric(admin, length, UserHandle.myUserId());
+ mService.setPasswordMinimumNumeric(admin, length);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1039,7 +1039,7 @@
public void setPasswordMinimumSymbols(ComponentName admin, int length) {
if (mService != null) {
try {
- mService.setPasswordMinimumSymbols(admin, length, UserHandle.myUserId());
+ mService.setPasswordMinimumSymbols(admin, length);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1097,7 +1097,7 @@
public void setPasswordMinimumNonLetter(ComponentName admin, int length) {
if (mService != null) {
try {
- mService.setPasswordMinimumNonLetter(admin, length, UserHandle.myUserId());
+ mService.setPasswordMinimumNonLetter(admin, length);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1157,7 +1157,7 @@
public void setPasswordHistoryLength(ComponentName admin, int length) {
if (mService != null) {
try {
- mService.setPasswordHistoryLength(admin, length, UserHandle.myUserId());
+ mService.setPasswordHistoryLength(admin, length);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1189,7 +1189,7 @@
public void setPasswordExpirationTimeout(ComponentName admin, long timeout) {
if (mService != null) {
try {
- mService.setPasswordExpirationTimeout(admin, timeout, UserHandle.myUserId());
+ mService.setPasswordExpirationTimeout(admin, timeout);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1334,7 +1334,7 @@
public void setMaximumFailedPasswordsForWipe(ComponentName admin, int num) {
if (mService != null) {
try {
- mService.setMaximumFailedPasswordsForWipe(admin, num, UserHandle.myUserId());
+ mService.setMaximumFailedPasswordsForWipe(admin, num);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1418,7 +1418,7 @@
public boolean resetPassword(String password, int flags) {
if (mService != null) {
try {
- return mService.resetPassword(password, flags, UserHandle.myUserId());
+ return mService.resetPassword(password, flags);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1442,7 +1442,7 @@
public void setMaximumTimeToLock(ComponentName admin, long timeMs) {
if (mService != null) {
try {
- mService.setMaximumTimeToLock(admin, timeMs, UserHandle.myUserId());
+ mService.setMaximumTimeToLock(admin, timeMs);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1592,7 +1592,7 @@
!= android.net.Proxy.PROXY_VALID)
throw new IllegalArgumentException();
}
- return mService.setGlobalProxy(admin, hostSpec, exclSpec, UserHandle.myUserId());
+ return mService.setGlobalProxy(admin, hostSpec, exclSpec);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1758,7 +1758,7 @@
public int setStorageEncryption(ComponentName admin, boolean encrypt) {
if (mService != null) {
try {
- return mService.setStorageEncryption(admin, encrypt, UserHandle.myUserId());
+ return mService.setStorageEncryption(admin, encrypt);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -1977,7 +1977,7 @@
public void setCameraDisabled(ComponentName admin, boolean disabled) {
if (mService != null) {
try {
- mService.setCameraDisabled(admin, disabled, UserHandle.myUserId());
+ mService.setCameraDisabled(admin, disabled);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -2021,7 +2021,7 @@
public void setScreenCaptureDisabled(ComponentName admin, boolean disabled) {
if (mService != null) {
try {
- mService.setScreenCaptureDisabled(admin, UserHandle.myUserId(), disabled);
+ mService.setScreenCaptureDisabled(admin, disabled);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -2065,7 +2065,7 @@
public void setAutoTimeRequired(ComponentName admin, boolean required) {
if (mService != null) {
try {
- mService.setAutoTimeRequired(admin, UserHandle.myUserId(), required);
+ mService.setAutoTimeRequired(admin, required);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -2106,7 +2106,7 @@
public void setKeyguardDisabledFeatures(ComponentName admin, int which) {
if (mService != null) {
try {
- mService.setKeyguardDisabledFeatures(admin, which, UserHandle.myUserId());
+ mService.setKeyguardDisabledFeatures(admin, which);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
@@ -2688,8 +2688,7 @@
PersistableBundle configuration) {
if (mService != null) {
try {
- mService.setTrustAgentConfiguration(admin, target, configuration,
- UserHandle.myUserId());
+ mService.setTrustAgentConfiguration(admin, target, configuration);
} catch (RemoteException e) {
Log.w(TAG, "Failed talking with device policy service", e);
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 0ca60c0..67bca4e 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -32,34 +32,34 @@
* {@hide}
*/
interface IDevicePolicyManager {
- void setPasswordQuality(in ComponentName who, int quality, int userHandle);
+ void setPasswordQuality(in ComponentName who, int quality);
int getPasswordQuality(in ComponentName who, int userHandle);
- void setPasswordMinimumLength(in ComponentName who, int length, int userHandle);
+ void setPasswordMinimumLength(in ComponentName who, int length);
int getPasswordMinimumLength(in ComponentName who, int userHandle);
- void setPasswordMinimumUpperCase(in ComponentName who, int length, int userHandle);
+ void setPasswordMinimumUpperCase(in ComponentName who, int length);
int getPasswordMinimumUpperCase(in ComponentName who, int userHandle);
- void setPasswordMinimumLowerCase(in ComponentName who, int length, int userHandle);
+ void setPasswordMinimumLowerCase(in ComponentName who, int length);
int getPasswordMinimumLowerCase(in ComponentName who, int userHandle);
- void setPasswordMinimumLetters(in ComponentName who, int length, int userHandle);
+ void setPasswordMinimumLetters(in ComponentName who, int length);
int getPasswordMinimumLetters(in ComponentName who, int userHandle);
- void setPasswordMinimumNumeric(in ComponentName who, int length, int userHandle);
+ void setPasswordMinimumNumeric(in ComponentName who, int length);
int getPasswordMinimumNumeric(in ComponentName who, int userHandle);
- void setPasswordMinimumSymbols(in ComponentName who, int length, int userHandle);
+ void setPasswordMinimumSymbols(in ComponentName who, int length);
int getPasswordMinimumSymbols(in ComponentName who, int userHandle);
- void setPasswordMinimumNonLetter(in ComponentName who, int length, int userHandle);
+ void setPasswordMinimumNonLetter(in ComponentName who, int length);
int getPasswordMinimumNonLetter(in ComponentName who, int userHandle);
- void setPasswordHistoryLength(in ComponentName who, int length, int userHandle);
+ void setPasswordHistoryLength(in ComponentName who, int length);
int getPasswordHistoryLength(in ComponentName who, int userHandle);
- void setPasswordExpirationTimeout(in ComponentName who, long expiration, int userHandle);
+ void setPasswordExpirationTimeout(in ComponentName who, long expiration);
long getPasswordExpirationTimeout(in ComponentName who, int userHandle);
long getPasswordExpiration(in ComponentName who, int userHandle);
@@ -68,33 +68,33 @@
int getCurrentFailedPasswordAttempts(int userHandle);
int getProfileWithMinimumFailedPasswordsForWipe(int userHandle);
- void setMaximumFailedPasswordsForWipe(in ComponentName admin, int num, int userHandle);
+ void setMaximumFailedPasswordsForWipe(in ComponentName admin, int num);
int getMaximumFailedPasswordsForWipe(in ComponentName admin, int userHandle);
- boolean resetPassword(String password, int flags, int userHandle);
+ boolean resetPassword(String password, int flags);
- void setMaximumTimeToLock(in ComponentName who, long timeMs, int userHandle);
+ void setMaximumTimeToLock(in ComponentName who, long timeMs);
long getMaximumTimeToLock(in ComponentName who, int userHandle);
void lockNow();
void wipeData(int flags, int userHandle);
- ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList, int userHandle);
+ ComponentName setGlobalProxy(in ComponentName admin, String proxySpec, String exclusionList);
ComponentName getGlobalProxyAdmin(int userHandle);
void setRecommendedGlobalProxy(in ComponentName admin, in ProxyInfo proxyInfo);
- int setStorageEncryption(in ComponentName who, boolean encrypt, int userHandle);
+ int setStorageEncryption(in ComponentName who, boolean encrypt);
boolean getStorageEncryption(in ComponentName who, int userHandle);
int getStorageEncryptionStatus(int userHandle);
- void setCameraDisabled(in ComponentName who, boolean disabled, int userHandle);
+ void setCameraDisabled(in ComponentName who, boolean disabled);
boolean getCameraDisabled(in ComponentName who, int userHandle);
- void setScreenCaptureDisabled(in ComponentName who, int userHandle, boolean disabled);
+ void setScreenCaptureDisabled(in ComponentName who, boolean disabled);
boolean getScreenCaptureDisabled(in ComponentName who, int userHandle);
- void setKeyguardDisabledFeatures(in ComponentName who, int which, int userHandle);
+ void setKeyguardDisabledFeatures(in ComponentName who, int which);
int getKeyguardDisabledFeatures(in ComponentName who, int userHandle);
void setActiveAdmin(in ComponentName policyReceiver, boolean refreshing, int userHandle);
@@ -186,7 +186,7 @@
boolean getCrossProfileCallerIdDisabledForUser(int userId);
void setTrustAgentConfiguration(in ComponentName admin, in ComponentName agent,
- in PersistableBundle args, int userId);
+ in PersistableBundle args);
List<PersistableBundle> getTrustAgentConfiguration(in ComponentName admin,
in ComponentName agent, int userId);
@@ -194,7 +194,7 @@
boolean removeCrossProfileWidgetProvider(in ComponentName admin, String packageName);
List<String> getCrossProfileWidgetProviders(in ComponentName admin);
- void setAutoTimeRequired(in ComponentName who, int userHandle, boolean required);
+ void setAutoTimeRequired(in ComponentName who, boolean required);
boolean getAutoTimeRequired();
boolean isRemovingAdmin(in ComponentName adminReceiver, int userHandle);
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 0450150..767f59e 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.media.AudioManager;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.RemoteException;
@@ -415,9 +416,16 @@
}
/**
- * Tells remote device to adjust volume. Only if absolute volume is supported.
+ * Tells remote device to adjust volume. Only if absolute volume is
+ * supported. Uses the following values:
+ * <ul>
+ * <li>{@link AudioManager#ADJUST_LOWER}</li>
+ * <li>{@link AudioManager#ADJUST_RAISE}</li>
+ * <li>{@link AudioManager#ADJUST_MUTE}</li>
+ * <li>{@link AudioManager#ADJUST_UNMUTE}</li>
+ * </ul>
*
- * @param direction 1 to increase volume, or -1 to decrease volume
+ * @param direction One of the supported adjust values.
* @hide
*/
public void adjustAvrcpAbsoluteVolume(int direction) {
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index d7d9e8b..5c09b42 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3082,6 +3082,12 @@
a.info.flags |= ActivityInfo.FLAG_IMMERSIVE;
}
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestActivity_primaryUserOnly,
+ false)) {
+ a.info.flags |= ActivityInfo.FLAG_PRIMARY_USER_ONLY;
+ }
+
if (!receiver) {
if (sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestActivity_hardwareAccelerated,
@@ -3153,11 +3159,6 @@
setExported = true;
}
}
- if (sa.getBoolean(
- com.android.internal.R.styleable.AndroidManifestActivity_primaryUserOnly,
- false)) {
- a.info.flags |= ActivityInfo.FLAG_PRIMARY_USER_ONLY;
- }
}
sa.recycle();
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index c931c01..6fb7299 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -141,9 +141,6 @@
private CompatibilityInfo mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
- @SuppressWarnings("unused")
- private WeakReference<IBinder> mToken;
-
static {
sPreloadedDrawables = new LongSparseArray[2];
sPreloadedDrawables[0] = new LongSparseArray<ConstantState>();
@@ -224,46 +221,44 @@
/**
* Create a new Resources object on top of an existing set of assets in an
* AssetManager.
- *
- * @param assets Previously created AssetManager.
- * @param metrics Current display metrics to consider when
+ *
+ * @param assets Previously created AssetManager.
+ * @param metrics Current display metrics to consider when
* selecting/computing resource values.
- * @param config Desired device configuration to consider when
+ * @param config Desired device configuration to consider when
* selecting/computing resource values (optional).
*/
public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {
- this(assets, metrics, config, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+ this(assets, metrics, config, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO);
}
/**
* Creates a new Resources object with CompatibilityInfo.
- *
- * @param assets Previously created AssetManager.
- * @param metrics Current display metrics to consider when
+ *
+ * @param assets Previously created AssetManager.
+ * @param metrics Current display metrics to consider when
* selecting/computing resource values.
- * @param config Desired device configuration to consider when
+ * @param config Desired device configuration to consider when
* selecting/computing resource values (optional).
* @param compatInfo this resource's compatibility info. Must not be null.
- * @param token The Activity token for determining stack affiliation. Usually null.
* @hide
*/
public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config,
- CompatibilityInfo compatInfo, IBinder token) {
+ CompatibilityInfo compatInfo) {
mAssets = assets;
mMetrics.setToDefaults();
if (compatInfo != null) {
mCompatibilityInfo = compatInfo;
}
- mToken = new WeakReference<IBinder>(token);
updateConfiguration(config, metrics);
assets.ensureStringBlocks();
}
/**
* Return a global shared Resources object that provides access to only
- * system resources (no application resources), and is not configured for
- * the current screen (can not use dimension units, does not change based
- * on orientation, etc).
+ * system resources (no application resources), and is not configured for
+ * the current screen (can not use dimension units, does not change based
+ * on orientation, etc).
*/
public static Resources getSystem() {
synchronized (sSync) {
diff --git a/core/java/android/content/res/ResourcesKey.java b/core/java/android/content/res/ResourcesKey.java
index 4ae3825..9548d49 100644
--- a/core/java/android/content/res/ResourcesKey.java
+++ b/core/java/android/content/res/ResourcesKey.java
@@ -16,27 +16,23 @@
package android.content.res;
-import android.os.IBinder;
+import java.util.Objects;
/** @hide */
public final class ResourcesKey {
- final String mResDir;
- final float mScale;
+ private final String mResDir;
+ private final float mScale;
private final int mHash;
- private final IBinder mToken;
public final int mDisplayId;
- public final Configuration mOverrideConfiguration = new Configuration();
+ public final Configuration mOverrideConfiguration;
public ResourcesKey(String resDir, int displayId, Configuration overrideConfiguration,
- float scale, IBinder token) {
+ float scale) {
mResDir = resDir;
mDisplayId = displayId;
- if (overrideConfiguration != null) {
- mOverrideConfiguration.setTo(overrideConfiguration);
- }
+ mOverrideConfiguration = overrideConfiguration;
mScale = scale;
- mToken = token;
int hash = 17;
hash = 31 * hash + (mResDir == null ? 0 : mResDir.hashCode());
@@ -48,7 +44,8 @@
}
public boolean hasOverrideConfiguration() {
- return !Configuration.EMPTY.equals(mOverrideConfiguration);
+ return mOverrideConfiguration != null
+ && !Configuration.EMPTY.equals(mOverrideConfiguration);
}
@Override
@@ -63,17 +60,9 @@
}
ResourcesKey peer = (ResourcesKey) obj;
- if ((mResDir == null) && (peer.mResDir != null)) {
+ if (!Objects.equals(mResDir, peer.mResDir)) {
return false;
}
- if ((mResDir != null) && (peer.mResDir == null)) {
- return false;
- }
- if ((mResDir != null) && (peer.mResDir != null)) {
- if (!mResDir.equals(peer.mResDir)) {
- return false;
- }
- }
if (mDisplayId != peer.mDisplayId) {
return false;
}
diff --git a/core/java/android/hardware/camera2/CameraAccessException.java b/core/java/android/hardware/camera2/CameraAccessException.java
index 91ef6ca..84e9912 100644
--- a/core/java/android/hardware/camera2/CameraAccessException.java
+++ b/core/java/android/hardware/camera2/CameraAccessException.java
@@ -28,16 +28,14 @@
*/
public class CameraAccessException extends AndroidException {
/**
- * The camera device is in use already
- * @hide
+ * The camera device is in use already.
*/
public static final int CAMERA_IN_USE = 4;
/**
- * The system-wide limit for number of open cameras has been reached,
- * and more camera devices cannot be opened until previous instances are
- * closed.
- * @hide
+ * The system-wide limit for number of open cameras or camera resources has
+ * been reached, and more camera devices cannot be opened or torch mode
+ * cannot be turned on until previous instances are closed.
*/
public static final int MAX_CAMERAS_IN_USE = 5;
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index eace1c4..5310071 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1069,8 +1069,11 @@
* android.scaler.availableInputOutputFormatsMap. When using an
* input stream, there must be at least one output stream
* configured to to receive the reprocessed images.</p>
+ * <p>When an input stream and some output streams are used in a reprocessing request,
+ * only the input buffer will be used to produce these output stream buffers, and a
+ * new sensor image will not be captured.</p>
* <p>For example, for Zero Shutter Lag (ZSL) still capture use case, the input
- * stream image format will be RAW_OPAQUE, the associated output stream image format
+ * stream image format will be OPAQUE, the associated output stream image format
* should be JPEG.</p>
* <p><b>Range of valid values:</b><br></p>
* <p>0 or 1.</p>
@@ -1080,8 +1083,8 @@
* {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
*
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
- * @hide
*/
+ @PublicKey
public static final Key<Integer> REQUEST_MAX_NUM_INPUT_STREAMS =
new Key<Integer>("android.request.maxNumInputStreams", int.class);
@@ -1157,8 +1160,10 @@
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR MANUAL_SENSOR}</li>
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING MANUAL_POST_PROCESSING}</li>
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_RAW RAW}</li>
+ * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING OPAQUE_REPROCESSING}</li>
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS READ_SENSOR_SETTINGS}</li>
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE BURST_CAPTURE}</li>
+ * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING YUV_REPROCESSING}</li>
* </ul></p>
* <p>This key is available on all devices.</p>
*
@@ -1167,8 +1172,10 @@
* @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR
* @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING
* @see #REQUEST_AVAILABLE_CAPABILITIES_RAW
+ * @see #REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING
* @see #REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS
* @see #REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
+ * @see #REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING
*/
@PublicKey
public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES =
@@ -1345,10 +1352,10 @@
* <p>The mapping of image formats that are supported by this
* camera device for input streams, to their corresponding output formats.</p>
* <p>All camera devices with at least 1
- * android.request.maxNumInputStreams will have at least one
+ * {@link CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS android.request.maxNumInputStreams} will have at least one
* available input format.</p>
* <p>The camera device will support the following map of formats,
- * if its dependent capability is supported:</p>
+ * if its dependent capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}) is supported:</p>
* <table>
* <thead>
* <tr>
@@ -1359,45 +1366,42 @@
* </thead>
* <tbody>
* <tr>
- * <td align="left">RAW_OPAQUE</td>
+ * <td align="left">OPAQUE</td>
* <td align="left">JPEG</td>
- * <td align="left">ZSL</td>
+ * <td align="left">OPAQUE_REPROCESSING</td>
* </tr>
* <tr>
- * <td align="left">RAW_OPAQUE</td>
+ * <td align="left">OPAQUE</td>
* <td align="left">YUV_420_888</td>
- * <td align="left">ZSL</td>
+ * <td align="left">OPAQUE_REPROCESSING</td>
* </tr>
* <tr>
- * <td align="left">RAW_OPAQUE</td>
- * <td align="left">RAW16</td>
- * <td align="left">RAW</td>
- * </tr>
- * <tr>
- * <td align="left">RAW16</td>
* <td align="left">YUV_420_888</td>
- * <td align="left">RAW</td>
- * </tr>
- * <tr>
- * <td align="left">RAW16</td>
* <td align="left">JPEG</td>
- * <td align="left">RAW</td>
+ * <td align="left">YUV_REPROCESSING</td>
+ * </tr>
+ * <tr>
+ * <td align="left">YUV_420_888</td>
+ * <td align="left">YUV_420_888</td>
+ * <td align="left">YUV_REPROCESSING</td>
* </tr>
* </tbody>
* </table>
- * <p>For ZSL-capable camera devices, using the RAW_OPAQUE format
+ * <p>OPAQUE refers to a device-internal format that is not directly application-visible.
+ * An OPAQUE input or output surface can be acquired by
+ * OpaqueImageRingBufferQueue#getInputSurface() or
+ * OpaqueImageRingBufferQueue#getOutputSurface().
+ * For a OPAQUE_REPROCESSING-capable camera device, using the OPAQUE format
* as either input or output will never hurt maximum frame rate (i.e.
- * StreamConfigurationMap#getOutputStallDuration(int,Size)
- * for a <code>format =</code> RAW_OPAQUE is always 0).</p>
+ * StreamConfigurationMap#getOutputStallDuration(klass,Size) is always 0),
+ * where klass is android.media.OpaqueImageRingBufferQueue.class.</p>
* <p>Attempting to configure an input stream with output streams not
* listed as available in this map is not valid.</p>
* <p>TODO: typedef to ReprocessFormatMap</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
- * <p><b>Full capability</b> -
- * Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the
- * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
*
- * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ * @see CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS
* @hide
*/
public static final Key<int[]> SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP =
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index b6bb33b..8af3c15 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -109,8 +109,11 @@
* of the state of individual CameraManager instances.</p>
*
* @param callback the new callback to send camera availability notices to
- * @param handler The handler on which the callback should be invoked, or
- * {@code null} to use the current thread's {@link android.os.Looper looper}.
+ * @param handler The handler on which the callback should be invoked, or {@code null} to use
+ * the current thread's {@link android.os.Looper looper}.
+ *
+ * @throws IllegalArgumentException if the handler is {@code null} but the current thread has
+ * no looper.
*/
public void registerAvailabilityCallback(AvailabilityCallback callback, Handler handler) {
if (handler == null) {
@@ -138,6 +141,42 @@
}
/**
+ * Register a callback to be notified about torch mode status.
+ *
+ * <p>Registering the same callback again will replace the handler with the
+ * new one provided.</p>
+ *
+ * <p>The first time a callback is registered, it is immediately called
+ * with the torch mode status of all currently known camera devices.</p>
+ *
+ * <p>Since this callback will be registered with the camera service, remember to unregister it
+ * once it is no longer needed; otherwise the callback will continue to receive events
+ * indefinitely and it may prevent other resources from being released. Specifically, the
+ * callbacks will be invoked independently of the general activity lifecycle and independently
+ * of the state of individual CameraManager instances.</p>
+ *
+ * @param callback The new callback to send torch mode status to
+ * @param handler The handler on which the callback should be invoked, or {@code null} to use
+ * the current thread's {@link android.os.Looper looper}.
+ *
+ * @throws IllegalArgumentException if the handler is {@code null} but the current thread has
+ * no looper.
+ */
+ public void registerTorchCallback(TorchCallback callback, Handler handler) {
+ }
+
+ /**
+ * Remove a previously-added callback; the callback will no longer receive torch mode status
+ * callbacks.
+ *
+ * <p>Removing a callback that isn't registered has no effect.</p>
+ *
+ * @param callback The callback to remove from the notification list
+ */
+ public void unregisterTorchCallback(TorchCallback callback) {
+ }
+
+ /**
* <p>Query the capabilities of a camera device. These capabilities are
* immutable for a given camera.</p>
*
@@ -384,6 +423,47 @@
}
/**
+ * Set the flash unit's torch mode of the camera of the given ID without opening the camera
+ * device.
+ *
+ * <p>Use {@link #getCameraIdList} to get the list of available camera devices and use
+ * {@link #getCameraCharacteristics} to check whether the camera device has a flash unit.
+ * Note that even if a camera device has a flash unit, turning on the torch mode may fail
+ * if the camera device or other camera resources needed to turn on the torch mode are in use.
+ * </p>
+ *
+ * <p> If {@link #setTorchMode} is called to turn on or off the torch mode successfully,
+ * {@link CameraManager.TorchCallback#onTorchModeChanged} will be invoked.
+ * However, even if turning on the torch mode is successful, the application does not have the
+ * exclusive ownership of the flash unit or the camera device. The torch mode will be turned
+ * off and becomes unavailable when the camera device that the flash unit belongs to becomes
+ * unavailable ({@link CameraManager.TorchCallback#onTorchModeAvailable} will be
+ * invoked) or when other camera resources to keep the torch on become unavailable (
+ * {@link CameraManager.TorchCallback#onTorchModeUnavailable} will be invoked). Also,
+ * other applications are free to call {@link #setTorchMode} to turn off the torch mode (
+ * {@link CameraManager.TorchCallback#onTorchModeChanged} will be invoked).
+ *
+ * @param cameraId
+ * The unique identifier of the camera device that the flash unit belongs to.
+ * @param enabled
+ * The desired state of the torch mode for the target camera device. Set to
+ * {@code true} to turn on the torch mode. Set to {@code false} to turn off the
+ * torch mode.
+ *
+ * @throws CameraAccessException if it failed to access the flash unit.
+ * {@link CameraAccessException#CAMERA_IN_USE} will be thrown if the camera device
+ * is in use. {@link CameraAccessException#MAX_CAMERAS_IN_USE} will be thrown if
+ * other camera resources needed to turn on the torch mode are in use.
+ *
+ * @throws IllegalArgumentException if cameraId was null, cameraId doesn't match any currently
+ * or previously available camera device, or the camera device doesn't have a
+ * flash unit.
+ */
+ public void setTorchMode(String cameraId, boolean enabled) throws CameraAccessException {
+
+ }
+
+ /**
* A callback for camera devices becoming available or
* unavailable to open.
*
@@ -428,6 +508,68 @@
}
/**
+ * A callback for camera flash torch modes becoming available, unavailable, enabled, or
+ * disabled.
+ *
+ * <p>The torch mode becomes available when the camera device it belongs to is no longer in use
+ * and other camera resources it needs are no longer busy. It becomes unavailable when the
+ * camera device it belongs to becomes unavailable or other camera resouces it needs become
+ * busy due to other higher priority camera activities. The torch mode changes when an
+ * application calls {@link #setTorchMode} successfully.
+ *
+ * <p>Extend this callback and pass an instance of the subclass to
+ * {@link CameraManager#registerTorchCallback} to be notified of such status changes.
+ * </p>
+ *
+ * @see registerTorchCallback
+ */
+ public static abstract class TorchCallback {
+ /**
+ * The torch mode of a camera has become available to use.
+ *
+ * <p>The default implementation of this method does nothing.</p>
+ *
+ * @param cameraId The unique identifier of the camera whose torch mode has become
+ * available.
+ */
+ public void onTorchModeAvailable(String cameraId) {
+ // default empty implementation
+ }
+
+ /**
+ * A previously-available torch mode of a camera has become unavailable.
+ *
+ * <p>If torch mode was previously turned on by calling {@link #setTorchMode}, it will be
+ * turned off before {@link CameraManager.TorchCallback#onTorchModeUnavailable} is
+ * invoked. {@link #setTorchMode} will fail until the flash unit becomes available again.
+ * </p>
+ *
+ * <p>The default implementation of this method does nothing.</p>
+ *
+ * @param cameraId The unique identifier of the camera whose torch mode has become
+ * unavailable.
+ */
+ public void onTorchModeUnavailable(String cameraId) {
+ // default empty implementation
+ }
+
+ /**
+ * Torch mode of a camera has been turned on or off through {@link #setTorchMode}.
+ *
+ * <p>The default implementation of this method does nothing.</p>
+ *
+ * @param cameraId The unique identifier of the camera whose torch mode has been changed.
+ *
+ * @param enabled The state that the torch mode of the camera has been changed to.
+ * {@code true} when the torch mode has been turned on. {@code false} when
+ * the torch mode has been turned off.
+ */
+ public void onTorchModeChanged(String cameraId, boolean enabled) {
+ // default empty implementation
+ }
+ }
+
+ /**
* Return or create the list of currently connected camera devices.
*
* <p>In case of errors connecting to the camera service, will return an empty list.</p>
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index f6df302..af7d365 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -439,23 +439,40 @@
public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3;
/**
- * <p>The camera device supports the Zero Shutter Lag use case.</p>
+ * <p>The camera device supports the Zero Shutter Lag reprocessing use case.</p>
* <ul>
- * <li>At least one input stream can be used.</li>
- * <li>RAW_OPAQUE is supported as an output/input format</li>
- * <li>Using RAW_OPAQUE does not cause a frame rate drop
+ * <li>One input stream is supported, that is, <code>{@link CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS android.request.maxNumInputStreams} == 1</code>.</li>
+ * <li>OPAQUE is supported as an output/input format, that is,
+ * StreamConfigurationMap#getOutputSizes(klass) and
+ * StreamConfigurationMap#getInputSizes(klass) return non empty Size[] and have common
+ * sizes, where klass is android.media.OpaqueImageRingBufferQueue.class. See
+ * android.scaler.availableInputOutputFormatsMap for detailed information about
+ * OPAQUE format.</li>
+ * <li>android.scaler.availableInputOutputFormatsMap has the required map entries.</li>
+ * <li>Using OPAQUE does not cause a frame rate drop
* relative to the sensor's maximum capture rate (at that
- * resolution).</li>
- * <li>RAW_OPAQUE will be reprocessable into both YUV_420_888
+ * resolution), see android.scaler.availableInputOutputFormatsMap for more details.</li>
+ * <li>OPAQUE will be reprocessable into both YUV_420_888
* and JPEG formats.</li>
- * <li>The maximum available resolution for RAW_OPAQUE streams
+ * <li>The maximum available resolution for OPAQUE streams
* (both input/output) will match the maximum available
* resolution of JPEG streams.</li>
+ * <li>Only below controls are effective for reprocessing requests and
+ * will be present in capture results, other controls in reprocess
+ * requests will be ignored by the camera device.<ul>
+ * <li>android.jpeg.*</li>
+ * <li>{@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode}</li>
+ * <li>{@link CaptureRequest#EDGE_MODE android.edge.mode}</li>
* </ul>
+ * </li>
+ * </ul>
+ *
+ * @see CaptureRequest#EDGE_MODE
+ * @see CaptureRequest#NOISE_REDUCTION_MODE
+ * @see CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
- * @hide
*/
- public static final int REQUEST_AVAILABLE_CAPABILITIES_ZSL = 4;
+ public static final int REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING = 4;
/**
* <p>The camera device supports accurately reporting the sensor settings for many of
@@ -515,6 +532,45 @@
*/
public static final int REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE = 6;
+ /**
+ * <p>The camera device supports the YUV420_888 reprocessing use case, similar as
+ * OPAQUE_REPROCESSING, This capability requires the camera device to support the
+ * following:</p>
+ * <ul>
+ * <li>One input stream is supported, that is, <code>{@link CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS android.request.maxNumInputStreams} == 1</code>.</li>
+ * <li>YUV420_888 is supported as a common format for both input and output, that is,
+ * StreamConfigurationMap#getOutputSizes(YUV420_888) and
+ * StreamConfigurationMap#getInputSizes(YUV420_888) return non empty Size[] and have
+ * common sizes.</li>
+ * <li>android.scaler.availableInputOutputFormatsMap has the required map entries.</li>
+ * <li>Using YUV420_888 does not cause a frame rate drop
+ * relative to the sensor's maximum capture rate (at that
+ * resolution), see android.scaler.availableInputOutputFormatsMap for more details.</li>
+ * <li>YUV420_888 will be reprocessable into both YUV_420_888
+ * and JPEG formats.</li>
+ * <li>The maximum available resolution for YUV420_888 streams
+ * (both input/output) will match the maximum available
+ * resolution of JPEG streams.</li>
+ * <li>Only the below controls are effective for reprocessing requests and will be
+ * present in capture results. The reprocess requests are from the original capture
+ * results that are assocaited with the intermidate YUV420_888 output buffers.
+ * All other controls in the reprocess requests will be ignored by the camera device.<ul>
+ * <li>android.jpeg.*</li>
+ * <li>{@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode}</li>
+ * <li>{@link CaptureRequest#EDGE_MODE android.edge.mode}</li>
+ * <li>{@link CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR android.reprocess.effectiveExposureFactor}</li>
+ * </ul>
+ * </li>
+ * </ul>
+ *
+ * @see CaptureRequest#EDGE_MODE
+ * @see CaptureRequest#NOISE_REDUCTION_MODE
+ * @see CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR
+ * @see CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ */
+ public static final int REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING = 7;
+
//
// Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
//
@@ -1830,6 +1886,13 @@
*/
public static final int NOISE_REDUCTION_MODE_HIGH_QUALITY = 2;
+ /**
+ * <p>MINIMAL 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_MINIMAL = 3;
+
//
// Enumeration values for CaptureRequest#SENSOR_TEST_PATTERN_MODE
//
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 0849df8..fb37ae5 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1154,7 +1154,7 @@
* <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 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 OPAQUE_REPROCESSING. MANUAL will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
* contains MANUAL_SENSOR. Other intent values are always supported.</p>
* <p><b>Possible values:</b>
* <ul>
@@ -1372,6 +1372,10 @@
* camera device will use the highest-quality enhancement algorithms,
* even if it slows down capture rate. FAST means the camera device will
* not slow down capture rate when applying edge enhancement.</p>
+ * <p>For YUV_REPROCESSING, these FAST/HIGH_QUALITY modes both mean that the camera
+ * device will apply FAST/HIGH_QUALITY YUV-domain edge enhancement, respectively.
+ * The camera device may adjust its internal noise reduction parameters for best
+ * image quality based on the {@link CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR android.reprocess.effectiveExposureFactor}, if it is set.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #EDGE_MODE_OFF OFF}</li>
@@ -1387,6 +1391,7 @@
*
* @see CameraCharacteristics#EDGE_AVAILABLE_EDGE_MODES
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ * @see CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR
* @see #EDGE_MODE_OFF
* @see #EDGE_MODE_FAST
* @see #EDGE_MODE_HIGH_QUALITY
@@ -1751,18 +1756,28 @@
/**
* <p>Mode of operation for the noise reduction algorithm.</p>
* <p>The noise reduction algorithm attempts to improve image quality by removing
- * excessive noise added by the capture process, especially in dark conditions.
- * OFF means no noise reduction will be applied by the camera device.</p>
+ * excessive noise added by the capture process, especially in dark conditions.</p>
+ * <p>OFF means no noise reduction will be applied by the camera device, for both raw and
+ * YUV domain.</p>
+ * <p>MINIMAL means that only sensor raw domain basic noise reduction is enabled ,to remove
+ * demosaicing or other processing artifacts. For YUV_REPROCESSING, MINIMAL is same as OFF.
+ * This mode is optional, may not be support by all devices. The application should check
+ * {@link CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES android.noiseReduction.availableNoiseReductionModes} before using it.</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 will not
* slow down capture rate when applying noise filtering.</p>
+ * <p>For YUV_REPROCESSING, these FAST/HIGH_QUALITY modes both mean that the camera device
+ * will apply FAST/HIGH_QUALITY YUV domain noise reduction, respectively. The camera device
+ * may adjust the noise reduction parameters for best image quality based on the
+ * {@link CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR android.reprocess.effectiveExposureFactor} if it is set.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #NOISE_REDUCTION_MODE_OFF OFF}</li>
* <li>{@link #NOISE_REDUCTION_MODE_FAST FAST}</li>
* <li>{@link #NOISE_REDUCTION_MODE_HIGH_QUALITY HIGH_QUALITY}</li>
+ * <li>{@link #NOISE_REDUCTION_MODE_MINIMAL MINIMAL}</li>
* </ul></p>
* <p><b>Available values for this device:</b><br>
* {@link CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES android.noiseReduction.availableNoiseReductionModes}</p>
@@ -1773,9 +1788,11 @@
*
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
* @see CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES
+ * @see CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR
* @see #NOISE_REDUCTION_MODE_OFF
* @see #NOISE_REDUCTION_MODE_FAST
* @see #NOISE_REDUCTION_MODE_HIGH_QUALITY
+ * @see #NOISE_REDUCTION_MODE_MINIMAL
*/
@PublicKey
public static final Key<Integer> NOISE_REDUCTION_MODE =
@@ -2416,6 +2433,52 @@
public static final Key<Boolean> BLACK_LEVEL_LOCK =
new Key<Boolean>("android.blackLevel.lock", boolean.class);
+ /**
+ * <p>The amount of exposure time increase factor applied to the original output
+ * frame by the application processing before sending for reprocessing.</p>
+ * <p>This is optional, and will be supported if the camera device supports YUV_REPROCESSING
+ * capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains YUV_REPROCESSING).</p>
+ * <p>For some YUV reprocessing use cases, the application may choose to filter the original
+ * output frames to effectively reduce the noise to the same level as a frame that was
+ * captured with longer exposure time. To be more specific, assuming the original captured
+ * images were captured with a sensitivity of S and an exposure time of T, the model in
+ * the camera device is that the amount of noise in the image would be approximately what
+ * would be expected if the original capture parameters had been a sensitivity of
+ * S/effectiveExposureFactor and an exposure time of T*effectiveExposureFactor, rather
+ * than S and T respectively. If the captured images were processed by the application
+ * before being sent for reprocessing, then the application may have used image processing
+ * algorithms and/or multi-frame image fusion to reduce the noise in the
+ * application-processed images (input images). By using the effectiveExposureFactor
+ * control, the application can communicate to the camera device the actual noise level
+ * improvement in the application-processed image. With this information, the camera
+ * device can select appropriate noise reduction and edge enhancement parameters to avoid
+ * excessive noise reduction ({@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode}) and insufficient edge
+ * enhancement ({@link CaptureRequest#EDGE_MODE android.edge.mode}) being applied to the reprocessed frames.</p>
+ * <p>For example, for multi-frame image fusion use case, the application may fuse
+ * multiple output frames together to a final frame for reprocessing. When N image are
+ * fused into 1 image for reprocessing, the exposure time increase factor could be up to
+ * square root of N (based on a simple photon shot noise model). The camera device will
+ * adjust the reprocessing noise reduction and edge enhancement parameters accordingly to
+ * produce the best quality images.</p>
+ * <p>This is relative factor, 1.0 indicates the application hasn't processed the input
+ * buffer in a way that affects its effective exposure time.</p>
+ * <p>This control is only effective for YUV reprocessing capture request. For noise
+ * reduction reprocessing, it is only effective when <code>{@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode} != OFF</code>.
+ * Similarly, for edge enhancement reprocessing, it is only effective when
+ * <code>{@link CaptureRequest#EDGE_MODE android.edge.mode} != OFF</code>.</p>
+ * <p><b>Units</b>: Relative exposure time increase factor.</p>
+ * <p><b>Range of valid values:</b><br>
+ * >= 1.0</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#EDGE_MODE
+ * @see CaptureRequest#NOISE_REDUCTION_MODE
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ */
+ @PublicKey
+ public static final Key<Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR =
+ new Key<Float>("android.reprocess.effectiveExposureFactor", float.class);
+
/*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
* End generated code
*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 1396940..5642f6f 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -1627,7 +1627,7 @@
* <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 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 OPAQUE_REPROCESSING. MANUAL will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
* contains MANUAL_SENSOR. Other intent values are always supported.</p>
* <p><b>Possible values:</b>
* <ul>
@@ -1988,6 +1988,10 @@
* camera device will use the highest-quality enhancement algorithms,
* even if it slows down capture rate. FAST means the camera device will
* not slow down capture rate when applying edge enhancement.</p>
+ * <p>For YUV_REPROCESSING, these FAST/HIGH_QUALITY modes both mean that the camera
+ * device will apply FAST/HIGH_QUALITY YUV-domain edge enhancement, respectively.
+ * The camera device may adjust its internal noise reduction parameters for best
+ * image quality based on the {@link CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR android.reprocess.effectiveExposureFactor}, if it is set.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #EDGE_MODE_OFF OFF}</li>
@@ -2003,6 +2007,7 @@
*
* @see CameraCharacteristics#EDGE_AVAILABLE_EDGE_MODES
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ * @see CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR
* @see #EDGE_MODE_OFF
* @see #EDGE_MODE_FAST
* @see #EDGE_MODE_HIGH_QUALITY
@@ -2465,18 +2470,28 @@
/**
* <p>Mode of operation for the noise reduction algorithm.</p>
* <p>The noise reduction algorithm attempts to improve image quality by removing
- * excessive noise added by the capture process, especially in dark conditions.
- * OFF means no noise reduction will be applied by the camera device.</p>
+ * excessive noise added by the capture process, especially in dark conditions.</p>
+ * <p>OFF means no noise reduction will be applied by the camera device, for both raw and
+ * YUV domain.</p>
+ * <p>MINIMAL means that only sensor raw domain basic noise reduction is enabled ,to remove
+ * demosaicing or other processing artifacts. For YUV_REPROCESSING, MINIMAL is same as OFF.
+ * This mode is optional, may not be support by all devices. The application should check
+ * {@link CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES android.noiseReduction.availableNoiseReductionModes} before using it.</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 will not
* slow down capture rate when applying noise filtering.</p>
+ * <p>For YUV_REPROCESSING, these FAST/HIGH_QUALITY modes both mean that the camera device
+ * will apply FAST/HIGH_QUALITY YUV domain noise reduction, respectively. The camera device
+ * may adjust the noise reduction parameters for best image quality based on the
+ * {@link CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR android.reprocess.effectiveExposureFactor} if it is set.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #NOISE_REDUCTION_MODE_OFF OFF}</li>
* <li>{@link #NOISE_REDUCTION_MODE_FAST FAST}</li>
* <li>{@link #NOISE_REDUCTION_MODE_HIGH_QUALITY HIGH_QUALITY}</li>
+ * <li>{@link #NOISE_REDUCTION_MODE_MINIMAL MINIMAL}</li>
* </ul></p>
* <p><b>Available values for this device:</b><br>
* {@link CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES android.noiseReduction.availableNoiseReductionModes}</p>
@@ -2487,9 +2502,11 @@
*
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
* @see CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES
+ * @see CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR
* @see #NOISE_REDUCTION_MODE_OFF
* @see #NOISE_REDUCTION_MODE_FAST
* @see #NOISE_REDUCTION_MODE_HIGH_QUALITY
+ * @see #NOISE_REDUCTION_MODE_MINIMAL
*/
@PublicKey
public static final Key<Integer> NOISE_REDUCTION_MODE =
@@ -3647,6 +3664,52 @@
public static final Key<Long> SYNC_FRAME_NUMBER =
new Key<Long>("android.sync.frameNumber", long.class);
+ /**
+ * <p>The amount of exposure time increase factor applied to the original output
+ * frame by the application processing before sending for reprocessing.</p>
+ * <p>This is optional, and will be supported if the camera device supports YUV_REPROCESSING
+ * capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains YUV_REPROCESSING).</p>
+ * <p>For some YUV reprocessing use cases, the application may choose to filter the original
+ * output frames to effectively reduce the noise to the same level as a frame that was
+ * captured with longer exposure time. To be more specific, assuming the original captured
+ * images were captured with a sensitivity of S and an exposure time of T, the model in
+ * the camera device is that the amount of noise in the image would be approximately what
+ * would be expected if the original capture parameters had been a sensitivity of
+ * S/effectiveExposureFactor and an exposure time of T*effectiveExposureFactor, rather
+ * than S and T respectively. If the captured images were processed by the application
+ * before being sent for reprocessing, then the application may have used image processing
+ * algorithms and/or multi-frame image fusion to reduce the noise in the
+ * application-processed images (input images). By using the effectiveExposureFactor
+ * control, the application can communicate to the camera device the actual noise level
+ * improvement in the application-processed image. With this information, the camera
+ * device can select appropriate noise reduction and edge enhancement parameters to avoid
+ * excessive noise reduction ({@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode}) and insufficient edge
+ * enhancement ({@link CaptureRequest#EDGE_MODE android.edge.mode}) being applied to the reprocessed frames.</p>
+ * <p>For example, for multi-frame image fusion use case, the application may fuse
+ * multiple output frames together to a final frame for reprocessing. When N image are
+ * fused into 1 image for reprocessing, the exposure time increase factor could be up to
+ * square root of N (based on a simple photon shot noise model). The camera device will
+ * adjust the reprocessing noise reduction and edge enhancement parameters accordingly to
+ * produce the best quality images.</p>
+ * <p>This is relative factor, 1.0 indicates the application hasn't processed the input
+ * buffer in a way that affects its effective exposure time.</p>
+ * <p>This control is only effective for YUV reprocessing capture request. For noise
+ * reduction reprocessing, it is only effective when <code>{@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode} != OFF</code>.
+ * Similarly, for edge enhancement reprocessing, it is only effective when
+ * <code>{@link CaptureRequest#EDGE_MODE android.edge.mode} != OFF</code>.</p>
+ * <p><b>Units</b>: Relative exposure time increase factor.</p>
+ * <p><b>Range of valid values:</b><br>
+ * >= 1.0</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ *
+ * @see CaptureRequest#EDGE_MODE
+ * @see CaptureRequest#NOISE_REDUCTION_MODE
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ */
+ @PublicKey
+ public static final Key<Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR =
+ new Key<Float>("android.reprocess.effectiveExposureFactor", float.class);
+
/*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
* End generated code
*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index 03540e1..347db05 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -328,15 +328,15 @@
appendStreamConfig(availableStreamConfigs,
ImageFormat.YUV_420_888, previewSizes);
for (int format : p.getSupportedPreviewFormats()) {
- if (ImageFormat.isPublicFormat(format)) {
+ if (ImageFormat.isPublicFormat(format) && format != ImageFormat.NV21) {
appendStreamConfig(availableStreamConfigs, format, previewSizes);
- } else {
+ } else if (VERBOSE) {
/*
* Do not add any formats unknown to us
* (since it would fail runtime checks in StreamConfigurationMap)
*/
- Log.w(TAG,
- String.format("mapStreamConfigs - Skipping non-public format %x", format));
+ Log.v(TAG,
+ String.format("mapStreamConfigs - Skipping format %x", format));
}
}
@@ -389,8 +389,8 @@
int j = 0;
for (String mode : antiBandingModes) {
int convertedMode = convertAntiBandingMode(mode);
- if (convertedMode == -1) {
- Log.w(TAG, "Antibanding mode " + ((mode == null) ? "NULL" : mode) +
+ if (VERBOSE && convertedMode == -1) {
+ Log.v(TAG, "Antibanding mode " + ((mode == null) ? "NULL" : mode) +
" not supported, skipping...");
} else {
modes[j++] = convertedMode;
diff --git a/core/java/android/hardware/camera2/utils/ArrayUtils.java b/core/java/android/hardware/camera2/utils/ArrayUtils.java
index 5a78bbd..79a335c 100644
--- a/core/java/android/hardware/camera2/utils/ArrayUtils.java
+++ b/core/java/android/hardware/camera2/utils/ArrayUtils.java
@@ -117,7 +117,7 @@
// Guard against unexpected values
if (strIndex < 0) {
- Log.w(TAG, "Ignoring invalid value " + str);
+ if (VERBOSE) Log.v(TAG, "Ignoring invalid value " + str);
continue;
}
diff --git a/core/java/android/hardware/hdmi/HdmiRecordSources.java b/core/java/android/hardware/hdmi/HdmiRecordSources.java
index 922b8e7..7e94b89 100644
--- a/core/java/android/hardware/hdmi/HdmiRecordSources.java
+++ b/core/java/android/hardware/hdmi/HdmiRecordSources.java
@@ -759,6 +759,8 @@
*/
@SystemApi
public static boolean checkRecordSource(byte[] recordSource) {
+ if (recordSource == null || recordSource.length == 0) return false;
+
int recordSourceType = recordSource[0];
int extraDataSize = recordSource.length - 1;
switch (recordSourceType) {
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index f64ef87..f283051 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -68,6 +68,8 @@
* accessory function is enabled
* <li> {@link #USB_FUNCTION_AUDIO_SOURCE} boolean extra indicating whether the
* audio source function is enabled
+ * <li> {@link #USB_FUNCTION_MIDI} boolean extra indicating whether the
+ * MIDI function is enabled
* </ul>
*
* {@hide}
@@ -188,6 +190,14 @@
public static final String USB_FUNCTION_AUDIO_SOURCE = "audio_source";
/**
+ * Name of the MIDI USB function.
+ * Used in extras for the {@link #ACTION_USB_STATE} broadcast
+ *
+ * {@hide}
+ */
+ public static final String USB_FUNCTION_MIDI = "midi";
+
+ /**
* Name of the Accessory USB function.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast
*
diff --git a/core/java/android/midi/MidiDevice.java b/core/java/android/midi/MidiDevice.java
index 8aaa86d..b91aedf 100644
--- a/core/java/android/midi/MidiDevice.java
+++ b/core/java/android/midi/MidiDevice.java
@@ -30,6 +30,7 @@
* This class is used for sending and receiving data to and from an MIDI device
* Instances of this class are created by {@link MidiManager#openDevice}.
*
+ * CANDIDATE FOR PUBLIC API
* @hide
*/
public final class MidiDevice {
diff --git a/core/java/android/midi/MidiDeviceInfo.java b/core/java/android/midi/MidiDeviceInfo.java
index 5cf62b5..dde2669 100644
--- a/core/java/android/midi/MidiDeviceInfo.java
+++ b/core/java/android/midi/MidiDeviceInfo.java
@@ -28,6 +28,7 @@
* This class is just an immutable object to encapsulate the MIDI device description.
* Use the MidiDevice class to actually communicate with devices.
*
+ * CANDIDATE FOR PUBLIC API
* @hide
*/
public class MidiDeviceInfo implements Parcelable {
@@ -45,7 +46,7 @@
public static final int TYPE_VIRTUAL = 2;
private final int mType; // USB or virtual
- private final int mId; // unique ID generated by MidiService
+ private final int mId; // unique ID generated by MidiService
private final int mInputPortCount;
private final int mOutputPortCount;
private final Bundle mProperties;
diff --git a/core/java/android/midi/MidiDeviceServer.java b/core/java/android/midi/MidiDeviceServer.java
index ccb2e0c..7499934 100644
--- a/core/java/android/midi/MidiDeviceServer.java
+++ b/core/java/android/midi/MidiDeviceServer.java
@@ -25,7 +25,14 @@
import java.io.IOException;
import java.util.ArrayList;
-/** @hide */
+/**
+ * This class is used to provide the implemention of MIDI device.
+ * Applications may call {@link MidiManager#createDeviceServer}
+ * to create an instance of this class to implement a virtual MIDI device.
+ *
+ * CANDIDATE FOR PUBLIC API
+ * @hide
+ */
public final class MidiDeviceServer implements Closeable {
private static final String TAG = "MidiDeviceServer";
diff --git a/core/java/android/midi/MidiInputPort.java b/core/java/android/midi/MidiInputPort.java
index 5d806cf8..51c47dd 100644
--- a/core/java/android/midi/MidiInputPort.java
+++ b/core/java/android/midi/MidiInputPort.java
@@ -26,6 +26,7 @@
/**
* This class is used for sending data to a port on a MIDI device
*
+ * CANDIDATE FOR PUBLIC API
* @hide
*/
public class MidiInputPort extends MidiPort implements MidiReceiver {
@@ -33,7 +34,7 @@
private final FileOutputStream mOutputStream;
// buffer to use for sending messages out our output stream
- private final byte[] mBuffer = new byte[MAX_PACKED_MESSAGE_SIZE];
+ private final byte[] mBuffer = new byte[MAX_PACKET_SIZE];
/* package */ MidiInputPort(ParcelFileDescriptor pfd, int portNumber) {
super(portNumber);
@@ -50,10 +51,19 @@
* {@link java.lang.System#nanoTime}
*/
public void onPost(byte[] msg, int offset, int count, long timestamp) throws IOException {
+ assert(offset >= 0 && count >= 0 && offset + count <= msg.length);
+
synchronized (mBuffer) {
- int length = packMessage(msg, offset, count, timestamp, mBuffer);
try {
- mOutputStream.write(mBuffer, 0, length);
+ while (count > 0) {
+ int length = packMessage(msg, offset, count, timestamp, mBuffer);
+ mOutputStream.write(mBuffer, 0, length);
+ int sent = getMessageSize(mBuffer, length);
+ assert(sent >= 0 && sent <= length);
+
+ offset += sent;
+ count -= sent;
+ }
} catch (IOException e) {
IoUtils.closeQuietly(mOutputStream);
// report I/O failure
diff --git a/core/java/android/midi/MidiManager.java b/core/java/android/midi/MidiManager.java
index 2c1c7bf..8aa8395 100644
--- a/core/java/android/midi/MidiManager.java
+++ b/core/java/android/midi/MidiManager.java
@@ -35,6 +35,7 @@
* {@samplecode
* MidiManager manager = (MidiManager) getSystemService(Context.MIDI_SERVICE);}
*
+ * CANDIDATE FOR PUBLIC API
* @hide
*/
public class MidiManager {
@@ -184,7 +185,7 @@
* @param properties a {@link android.os.Bundle} containing properties describing the device
* @param isPrivate true if this device should only be visible and accessible to apps
* with the same UID as the caller
- * @return a {@link MidiVirtualDevice} object to locally represent the device
+ * @return a {@link MidiDeviceServer} object to locally represent the device
*/
public MidiDeviceServer createDeviceServer(int numInputPorts, int numOutputPorts,
Bundle properties, boolean isPrivate) {
diff --git a/core/java/android/midi/MidiOutputPort.java b/core/java/android/midi/MidiOutputPort.java
index b550ed4..332b431 100644
--- a/core/java/android/midi/MidiOutputPort.java
+++ b/core/java/android/midi/MidiOutputPort.java
@@ -26,8 +26,9 @@
import java.util.ArrayList;
/**
- * This class is used for receiving data to a port on a MIDI device
+ * This class is used for receiving data from a port on a MIDI device
*
+ * CANDIDATE FOR PUBLIC API
* @hide
*/
public class MidiOutputPort extends MidiPort implements MidiSender {
@@ -45,7 +46,7 @@
private final Thread mThread = new Thread() {
@Override
public void run() {
- byte[] buffer = new byte[MAX_PACKED_MESSAGE_SIZE];
+ byte[] buffer = new byte[MAX_PACKET_SIZE];
ArrayList<MidiReceiver> deadReceivers = new ArrayList<MidiReceiver>();
try {
@@ -54,9 +55,6 @@
int count = mInputStream.read(buffer);
if (count < 0) {
break;
- } else if (count < MIN_PACKED_MESSAGE_SIZE || count > MAX_PACKED_MESSAGE_SIZE) {
- Log.e(TAG, "Number of bytes read out of range: " + count);
- continue;
}
int offset = getMessageOffset(buffer, count);
@@ -88,15 +86,15 @@
}
}
} catch (IOException e) {
- Log.e(TAG, "read failed");
// report I/O failure
+ Log.e(TAG, "read failed");
+ } finally {
IoUtils.closeQuietly(mInputStream);
onIOException();
}
}
};
-
/* package */ MidiOutputPort(ParcelFileDescriptor pfd, int portNumber) {
super(portNumber);
mInputStream = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
diff --git a/core/java/android/midi/MidiPort.java b/core/java/android/midi/MidiPort.java
index 1fa7946..7512a90 100644
--- a/core/java/android/midi/MidiPort.java
+++ b/core/java/android/midi/MidiPort.java
@@ -24,6 +24,7 @@
* This class represents a MIDI input or output port.
* Base class for {@link MidiInputPort} and {@link MidiOutputPort}
*
+ * CANDIDATE FOR PUBLIC API
* @hide
*/
abstract public class MidiPort implements Closeable {
@@ -32,16 +33,19 @@
private final int mPortNumber;
/**
- * Minimum size of packed message as sent through our ParcelFileDescriptor
- * 8 bytes for timestamp and 1 to 3 bytes for message
+ * Maximum size of a packet that can pass through our ParcelFileDescriptor
*/
- protected static final int MIN_PACKED_MESSAGE_SIZE = 9;
+ protected static final int MAX_PACKET_SIZE = 1024;
/**
- * Maximum size of packed message as sent through our ParcelFileDescriptor
- * 8 bytes for timestamp and 1 to 3 bytes for message
+ * size of message timestamp in bytes
*/
- protected static final int MAX_PACKED_MESSAGE_SIZE = 11;
+ private static final int TIMESTAMP_SIZE = 8;
+
+ /**
+ * Maximum amount of MIDI data that can be included in a packet
+ */
+ public static final int MAX_PACKET_DATA_SIZE = MAX_PACKET_SIZE - TIMESTAMP_SIZE;
/* package */ MidiPort(int portNumber) {
@@ -76,49 +80,52 @@
*/
protected static int packMessage(byte[] message, int offset, int size, long timestamp,
byte[] dest) {
- // pack variable length message first
+ if (size + TIMESTAMP_SIZE > MAX_PACKET_SIZE) {
+ size = MAX_PACKET_SIZE - TIMESTAMP_SIZE;
+ }
+ // message data goes first
System.arraycopy(message, offset, dest, 0, size);
- int destOffset = size;
- // timestamp takes 8 bytes
- for (int i = 0; i < 8; i++) {
- dest[destOffset++] = (byte)timestamp;
+
+ // followed by timestamp
+ for (int i = 0; i < TIMESTAMP_SIZE; i++) {
+ dest[size++] = (byte)timestamp;
timestamp >>= 8;
}
- return destOffset;
+ return size;
}
/**
- * Utility function for unpacking a MIDI message to be sent through our ParcelFileDescriptor
- * returns the offet of of MIDI message in packed buffer
+ * Utility function for unpacking a MIDI message received from our ParcelFileDescriptor
+ * returns the offset of the MIDI message in packed buffer
*/
protected static int getMessageOffset(byte[] buffer, int bufferLength) {
- // message is at start of buffer
+ // message is at the beginning
return 0;
}
/**
- * Utility function for unpacking a MIDI message to be sent through our ParcelFileDescriptor
- * returns size of MIDI message in packed buffer
+ * Utility function for unpacking a MIDI message received from our ParcelFileDescriptor
+ * returns size of MIDI data in packed buffer
*/
protected static int getMessageSize(byte[] buffer, int bufferLength) {
- // message length is total buffer length minus size of the timestamp and port number
- return bufferLength - 8 /* sizeof(timestamp) */;
+ // message length is total buffer length minus size of the timestamp
+ return bufferLength - TIMESTAMP_SIZE;
}
/**
- * Utility function for unpacking a MIDI message to be sent through our ParcelFileDescriptor
+ * Utility function for unpacking a MIDI message received from our ParcelFileDescriptor
* unpacks timestamp from packed buffer
*/
protected static long getMessageTimeStamp(byte[] buffer, int bufferLength) {
+ // timestamp is at end of the packet
+ int offset = bufferLength;
long timestamp = 0;
- // timestamp follows variable length message data
- int dataLength = getMessageSize(buffer, bufferLength);
- for (int i = dataLength + 7; i >= dataLength; i--) {
- int b = (int)buffer[i] & 0xFF;
+ for (int i = 0; i < TIMESTAMP_SIZE; i++) {
+ int b = (int)buffer[--offset] & 0xFF;
timestamp = (timestamp << 8) | b;
}
return timestamp;
- }
+ }
}
diff --git a/core/java/android/midi/MidiReceiver.java b/core/java/android/midi/MidiReceiver.java
index 0b183cc..fdfe51a 100644
--- a/core/java/android/midi/MidiReceiver.java
+++ b/core/java/android/midi/MidiReceiver.java
@@ -19,21 +19,24 @@
import java.io.IOException;
/**
- * Interface for receiving events from a MIDI device.
+ * Interface for receiving data from a MIDI device.
*
+ * CANDIDATE FOR PUBLIC API
* @hide
*/
public interface MidiReceiver {
/**
- * Called to pass a MIDI event to the receiver.
+ * Called to pass MIDI data to the receiver.
*
* NOTE: the msg array parameter is only valid within the context of this call.
* The msg bytes should be copied by the receiver rather than retaining a reference
* to this parameter.
+ * Also, modifying the contents of the msg array parameter may result in other receivers
+ * in the same application receiving incorrect values in their onPost() method.
*
- * @param msg a byte array containing the MIDI message
- * @param offset the offset of the first byte of the message in the byte array
- * @param count the number of bytes in the message
+ * @param msg a byte array containing the MIDI data
+ * @param offset the offset of the first byte of the data in the byte array
+ * @param count the number of bytes of MIDI data in the array
* @param timestamp the timestamp of the message (based on {@link java.lang.System#nanoTime}
* @throws IOException
*/
diff --git a/core/java/android/midi/MidiSender.java b/core/java/android/midi/MidiSender.java
index 7958a06..2b7afad 100644
--- a/core/java/android/midi/MidiSender.java
+++ b/core/java/android/midi/MidiSender.java
@@ -20,6 +20,7 @@
* Interface provided by a device to allow attaching
* MidiReceivers to a MIDI device.
*
+ * CANDIDATE FOR PUBLIC API
* @hide
*/
public interface MidiSender {
diff --git a/core/java/android/midi/MidiUtils.java b/core/java/android/midi/MidiUtils.java
deleted file mode 100644
index e60e2db..0000000
--- a/core/java/android/midi/MidiUtils.java
+++ /dev/null
@@ -1,65 +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 android.midi;
-
-import android.util.Log;
-
-/**
- * Class containing miscellaneous MIDI utilities.
- *
- * @hide
- */
-public final class MidiUtils {
- private static final String TAG = "MidiUtils";
-
- private MidiUtils() { }
-
- /**
- * Returns data size of a MIDI message based on the message's command byte
- * @param b the message command byte
- * @return the message's data length
- */
- public static int getMessageDataSize(byte b) {
- switch (b & 0xF0) {
- case 0x80:
- case 0x90:
- case 0xA0:
- case 0xB0:
- case 0xE0:
- return 2;
- case 0xC0:
- case 0xD0:
- return 1;
- case 0xF0:
- switch (b & 0x0F) {
- case 0x00:
- Log.e(TAG, "System Exclusive not supported yet");
- return -1;
- case 0x01:
- case 0x03:
- return 1;
- case 0x02:
- return 2;
- default:
- return 0;
- }
- default:
- Log.e(TAG, "unknown MIDI command " + b);
- return -1;
- }
- }
-}
diff --git a/core/java/android/net/StaticIpConfiguration.java b/core/java/android/net/StaticIpConfiguration.java
index 365f2b6..37ee961 100644
--- a/core/java/android/net/StaticIpConfiguration.java
+++ b/core/java/android/net/StaticIpConfiguration.java
@@ -188,6 +188,7 @@
for (InetAddress dnsServer : dnsServers) {
NetworkUtils.parcelInetAddress(dest, dnsServer, flags);
}
+ dest.writeString(domains);
}
protected static void readFromParcel(StaticIpConfiguration s, Parcel in) {
@@ -198,5 +199,6 @@
for (int i = 0; i < size; i++) {
s.dnsServers.add(NetworkUtils.unparcelInetAddress(in));
}
+ s.domains = in.readString();
}
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 2dfd919..e4b594a 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -597,6 +597,11 @@
* Lollipop with an extra sugar coating on the outside!
*/
public static final int LOLLIPOP_MR1 = 22;
+
+ /**
+ * M comes after L.
+ */
+ public static final int MNC = CUR_DEVELOPMENT;
}
/** The type of build, like "user" or "eng". */
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 7dd559c..cd86a3c 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -2194,6 +2194,16 @@
public static final String CONTACT_ID = "contact_id";
/**
+ * Persistent unique id for each raw_contact within its account.
+ * This id is provided by its own data source, and can be used to backup metadata
+ * to the server.
+ * This should be unique within each set of account_name/account_type/data_set
+ *
+ * @hide
+ */
+ public static final String BACKUP_ID = "backup_id";
+
+ /**
* The data set within the account that this row belongs to. This allows
* multiple sync adapters for the same account type to distinguish between
* each others' data.
@@ -3986,6 +3996,13 @@
public static final String MIMETYPE = "mimetype";
/**
+ * Hash id on the data fields, used for backup and restore.
+ *
+ * @hide
+ */
+ public static final String HASH_ID = "hash_id";
+
+ /**
* A reference to the {@link RawContacts#_ID}
* that this data belongs to.
*/
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 7b20e72..743f6b7 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -81,7 +81,7 @@
void addAppToken(int addPos, IApplicationToken token, int groupId, int stackId,
int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId,
int configChanges, boolean voiceInteraction, boolean launchTaskBehind);
- void setAppGroupId(IBinder token, int groupId);
+ void setAppTask(IBinder token, int taskId);
void setAppOrientation(IApplicationToken token, int requestedOrientation);
int getAppOrientation(IApplicationToken token);
void setFocusedApp(IBinder token, boolean moveFocusNow);
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 7b13e84..d08ab46 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -188,9 +188,6 @@
void wallpaperCommandComplete(IBinder window, in Bundle result);
- void setUniverseTransform(IBinder window, float alpha, float offx, float offy,
- float dsdx, float dtdx, float dsdy, float dtdy);
-
/**
* Notifies that a rectangle on the screen has been requested.
*/
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1e6f6c9..5b26ebb 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4569,11 +4569,18 @@
}
/**
- * Register a callback to be invoked when the scroll position of this view
- * changed.
+ * Register a callback to be invoked when the scroll X or Y positions of
+ * this view change.
+ * <p>
+ * <b>Note:</b> Some views handle scrolling independently from View and may
+ * have their own separate listeners for scroll-type events. For example,
+ * {@link android.widget.ListView ListView} allows clients to register an
+ * {@link android.widget.ListView#setOnScrollListener(android.widget.AbsListView.OnScrollListener) AbsListView.OnScrollListener}
+ * to listen for changes in list scroll position.
*
- * @param l The callback that will run.
- * @hide Only used internally.
+ * @param l The listener to notify when the scroll X or Y position changes.
+ * @see android.view.View#getScrollX()
+ * @see android.view.View#getScrollY()
*/
public void setOnScrollChangeListener(OnScrollChangeListener l) {
getListenerInfo().mOnScrollChangeListener = l;
@@ -5896,23 +5903,6 @@
return true;
}
- /**
- * Adds the clickable rectangles withing the bounds of this view. They
- * may overlap. This method is intended for use only by the accessibility
- * layer.
- *
- * @param outRects List to which to add clickable areas.
- *
- * @hide
- */
- public void addClickableRectsForAccessibility(List<RectF> outRects) {
- if (isClickable() || isLongClickable()) {
- RectF bounds = new RectF();
- bounds.set(0, 0, getWidth(), getHeight());
- outRects.add(bounds);
- }
- }
-
static void offsetRects(List<RectF> rects, float offsetX, float offsetY) {
final int rectCount = rects.size();
for (int i = 0; i < rectCount; i++) {
@@ -6790,7 +6780,6 @@
@RemotableViewMethod
public void setVisibility(@Visibility int visibility) {
setFlags(visibility, VISIBILITY_MASK);
- if (mBackground != null) mBackground.setVisible(visibility == VISIBLE, false);
}
/**
@@ -8816,20 +8805,28 @@
}
/**
- * Called when the visibility of the view or an ancestor of the view is changed.
- * @param changedView The view whose visibility changed. Could be 'this' or
- * an ancestor view.
- * @param visibility The new visibility of changedView: {@link #VISIBLE},
- * {@link #INVISIBLE} or {@link #GONE}.
+ * Called when the visibility of the view or an ancestor of the view has
+ * changed.
+ *
+ * @param changedView The view whose visibility changed. May be
+ * {@code this} or an ancestor view.
+ * @param visibility The new visibility, one of {@link #VISIBLE},
+ * {@link #INVISIBLE} or {@link #GONE}.
*/
protected void onVisibilityChanged(@NonNull View changedView, @Visibility int visibility) {
- if (visibility == VISIBLE) {
+ final boolean visible = visibility == VISIBLE && getVisibility() == VISIBLE;
+ if (visible) {
if (mAttachInfo != null) {
initialAwakenScrollBars();
} else {
mPrivateFlags |= PFLAG_AWAKEN_SCROLL_BARS_ON_ATTACH;
}
}
+
+ final Drawable dr = mBackground;
+ if (dr != null && visible != dr.isVisible()) {
+ dr.setVisible(visible, false);
+ }
}
/**
@@ -9959,9 +9956,15 @@
/**
* Interface definition for a callback to be invoked when the scroll
- * position of a view changes.
+ * X or Y positions of a view change.
+ * <p>
+ * <b>Note:</b> Some views handle scrolling independently from View and may
+ * have their own separate listeners for scroll-type events. For example,
+ * {@link android.widget.ListView ListView} allows clients to register an
+ * {@link android.widget.ListView#setOnScrollListener(android.widget.AbsListView.OnScrollListener) AbsListView.OnScrollListener}
+ * to listen for changes in list scroll position.
*
- * @hide Only used internally.
+ * @see #setOnScrollChangeListener(View.OnScrollChangeListener)
*/
public interface OnScrollChangeListener {
/**
@@ -14889,10 +14892,9 @@
void setDisplayListProperties(RenderNode renderNode) {
if (renderNode != null) {
renderNode.setHasOverlappingRendering(hasOverlappingRendering());
- if (mParent instanceof ViewGroup) {
- renderNode.setClipToBounds(
- (((ViewGroup) mParent).mGroupFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0);
- }
+ renderNode.setClipToBounds(mParent instanceof ViewGroup
+ && ((ViewGroup) mParent).getClipChildren());
+
float alpha = 1;
if (mParent instanceof ViewGroup && (((ViewGroup) mParent).mGroupFlags &
ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
@@ -16642,6 +16644,7 @@
if (changed) {
requestLayout();
+ invalidateOutline();
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index ee9845f..49e4efa 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -857,27 +857,11 @@
// Compute the intersection between the child and the sibling.
if (siblingBounds.intersect(bounds)) {
- List<RectF> clickableRects = new ArrayList<>();
- sibling.addClickableRectsForAccessibility(clickableRects);
-
- final int clickableRectCount = clickableRects.size();
- for (int j = 0; j < clickableRectCount; j++) {
- RectF clickableRect = clickableRects.get(j);
-
- // Translate the clickable rect to our coordinates.
- offsetChildRectToMyCoords(clickableRect, sibling);
-
- // Compute the intersection between the child and the clickable rects.
- if (clickableRect.intersect(bounds)) {
- // If a clickable rect completely covers the child, done.
- if (clickableRect.equals(bounds)) {
- releaseOrderedChildIterator();
- return false;
- }
- // Keep track of the intersection rectangle.
- intersections.add(clickableRect);
- }
- }
+ // Conservatively we consider an overlapping sibling to be
+ // interactive and ignore it. This is not ideal as if the
+ // sibling completely covers the view despite handling no
+ // touch events we will not be able to click on the view.
+ intersections.add(siblingBounds);
}
}
@@ -892,54 +876,6 @@
return true;
}
- /**
- * @hide
- */
- @Override
- public void addClickableRectsForAccessibility(List<RectF> outRects) {
- int sizeBefore = outRects.size();
-
- super.addClickableRectsForAccessibility(outRects);
-
- // If we added ourselves, then no need to visit children.
- if (outRects.size() > sizeBefore) {
- return;
- }
-
- Iterator<View> iterator = obtainOrderedChildIterator();
- while (iterator.hasNext()) {
- View child = iterator.next();
-
- // Cannot click on an invisible view.
- if (!isVisible(child)) {
- continue;
- }
-
- sizeBefore = outRects.size();
-
- // Add clickable rects in the child bounds.
- child.addClickableRectsForAccessibility(outRects);
-
- // Offset the clickable rects for out children to our coordinates.
- final int sizeAfter = outRects.size();
- for (int j = sizeBefore; j < sizeAfter; j++) {
- RectF rect = outRects.get(j);
-
- // Translate the clickable rect to our coordinates.
- offsetChildRectToMyCoords(rect, child);
-
- // If a clickable rect fills the parent, done.
- if ((int) rect.left == 0 && (int) rect.top == 0
- && (int) rect.right == mRight && (int) rect.bottom == mBottom) {
- releaseOrderedChildIterator();
- return;
- }
- }
- }
-
- releaseOrderedChildIterator();
- }
-
private void offsetChildRectToMyCoords(RectF rect, View child) {
if (!child.hasIdentityMatrix()) {
child.getMatrix().mapRect(rect);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 87d9a58..15e7060 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -472,8 +472,10 @@
// Compute surface insets required to draw at specified Z value.
// TODO: Use real shadow insets for a constant max Z.
- final int surfaceInset = (int) Math.ceil(view.getZ() * 2);
- attrs.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
+ if (!attrs.hasManualSurfaceInsets) {
+ final int surfaceInset = (int) Math.ceil(view.getZ() * 2);
+ attrs.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
+ }
CompatibilityInfo compatibilityInfo = mDisplayAdjustments.getCompatibilityInfo();
mTranslator = compatibilityInfo.getTranslator();
@@ -760,6 +762,7 @@
final int oldInsetRight = mWindowAttributes.surfaceInsets.right;
final int oldInsetBottom = mWindowAttributes.surfaceInsets.bottom;
final int oldSoftInputMode = mWindowAttributes.softInputMode;
+ final boolean oldHasManualSurfaceInsets = mWindowAttributes.hasManualSurfaceInsets;
// Keep track of the actual window flags supplied by the client.
mClientWindowLayoutFlags = attrs.flags;
@@ -786,6 +789,7 @@
// Restore old surface insets.
mWindowAttributes.surfaceInsets.set(
oldInsetLeft, oldInsetTop, oldInsetRight, oldInsetBottom);
+ mWindowAttributes.hasManualSurfaceInsets = oldHasManualSurfaceInsets;
applyKeepScreenOnFlag(mWindowAttributes);
@@ -1326,6 +1330,11 @@
}
}
+ // Non-visible windows can't hold accessibility focus.
+ if (mAttachInfo.mWindowVisibility != View.VISIBLE) {
+ host.clearAccessibilityFocus();
+ }
+
// Execute enqueued actions on every traversal in case a detached view enqueued an action
getRunQueue().executeActions(mAttachInfo.mHandler);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 094a8a1..740cb5d 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -501,13 +501,6 @@
public static final int TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24;
/**
- * Window type: Behind the universe of the real windows.
- * In multiuser systems shows on all users' windows.
- * @hide
- */
- public static final int TYPE_UNIVERSE_BACKGROUND = FIRST_SYSTEM_WINDOW+25;
-
- /**
* Window type: Display overlay window. Used to simulate secondary display devices.
* In multiuser systems shows on all users' windows.
* @hide
@@ -1332,6 +1325,16 @@
* @hide
*/
public final Rect surfaceInsets = new Rect();
+
+ /**
+ * Whether the surface insets have been manually set. When set to
+ * {@code false}, the view root will automatically determine the
+ * appropriate surface insets.
+ *
+ * @see #surfaceInsets
+ * @hide
+ */
+ public boolean hasManualSurfaceInsets;
/**
* The desired bitmap format. May be one of the constants in
@@ -1628,6 +1631,7 @@
out.writeInt(surfaceInsets.top);
out.writeInt(surfaceInsets.right);
out.writeInt(surfaceInsets.bottom);
+ out.writeInt(hasManualSurfaceInsets ? 1 : 0);
out.writeInt(needsMenuKey);
}
@@ -1676,6 +1680,7 @@
surfaceInsets.top = in.readInt();
surfaceInsets.right = in.readInt();
surfaceInsets.bottom = in.readInt();
+ hasManualSurfaceInsets = in.readInt() != 0;
needsMenuKey = in.readInt();
}
@@ -1858,6 +1863,11 @@
changes |= SURFACE_INSETS_CHANGED;
}
+ if (hasManualSurfaceInsets != o.hasManualSurfaceInsets) {
+ hasManualSurfaceInsets = o.hasManualSurfaceInsets;
+ changes |= SURFACE_INSETS_CHANGED;
+ }
+
if (needsMenuKey != o.needsMenuKey) {
needsMenuKey = o.needsMenuKey;
changes |= NEEDS_MENU_KEY_CHANGED;
@@ -1966,8 +1976,11 @@
if (userActivityTimeout >= 0) {
sb.append(" userActivityTimeout=").append(userActivityTimeout);
}
- if (!surfaceInsets.equals(Insets.NONE)) {
+ if (!surfaceInsets.equals(Insets.NONE) || hasManualSurfaceInsets) {
sb.append(" surfaceInsets=").append(surfaceInsets);
+ if (hasManualSurfaceInsets) {
+ sb.append(" (manual)");
+ }
}
if (needsMenuKey != NEEDS_MENU_UNSET) {
sb.append(" needsMenuKey=");
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index b8e94ee..ff1fde7 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -579,13 +579,6 @@
public int getMaxWallpaperLayer();
/**
- * Return the window layer at which windows appear above the normal
- * universe (that is no longer impacted by the universe background
- * transform).
- */
- public int getAboveUniverseLayer();
-
- /**
* Return the display width available after excluding any screen
* decorations that can never be removed. That is, system bar or
* button bar.
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index b5afdf7..6096d7d 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -721,7 +721,7 @@
* @return Whether the refresh succeeded.
*/
public boolean refresh() {
- return refresh(false);
+ return refresh(true);
}
/**
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index f208fff..b5782fc 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -18,6 +18,7 @@
import java.util.ArrayList;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -203,11 +204,15 @@
}
@Override
- @RemotableViewMethod
- public void setVisibility(@Visibility int visibility) {
- super.setVisibility(visibility);
- if (mForeground != null) {
- mForeground.setVisible(visibility == VISIBLE, false);
+ protected void onVisibilityChanged(@NonNull View changedView, @Visibility int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+
+ final Drawable dr = mForeground;
+ if (dr != null) {
+ final boolean visible = visibility == VISIBLE && getVisibility() == VISIBLE;
+ if (visible != dr.isVisible()) {
+ dr.setVisible(visible, false);
+ }
}
}
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index c6c979e..f1fa1b6 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -762,18 +762,6 @@
awakenScrollBars();
}
- /**
- * @hide
- */
- @Override
- public void addClickableRectsForAccessibility(List<RectF> outRects) {
- // This class always consumes touch events, therefore if it
- // covers a view we do not want to send a click over it.
- RectF bounds = new RectF();
- bounds.set(0, 0, getWidth(), getHeight());
- outRects.add(bounds);
- }
-
/** @hide */
@Override
public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index f5cd915..7cf3eed 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -27,6 +27,11 @@
import android.graphics.drawable.StateListDrawable;
import android.os.Build;
import android.os.IBinder;
+import android.transition.Transition;
+import android.transition.Transition.EpicenterCallback;
+import android.transition.TransitionInflater;
+import android.transition.TransitionManager;
+import android.transition.TransitionSet;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.KeyEvent;
@@ -39,12 +44,13 @@
import android.view.WindowManager;
import java.lang.ref.WeakReference;
+import java.util.List;
/**
* <p>A popup window that can be used to display an arbitrary view. The popup
* window is a floating container that appears on top of the current
* activity.</p>
- *
+ *
* @see android.widget.AutoCompleteTextView
* @see android.widget.Spinner
*/
@@ -56,7 +62,7 @@
* it doesn't.
*/
public static final int INPUT_METHOD_FROM_FOCUSABLE = 0;
-
+
/**
* Mode for {@link #setInputMethodMode(int)}: this popup always needs to
* work with an input method, regardless of whether it is focusable. This
@@ -64,7 +70,7 @@
* the input method while it is shown.
*/
public static final int INPUT_METHOD_NEEDED = 1;
-
+
/**
* Mode for {@link #setInputMethodMode(int)}: this popup never needs to
* work with an input method, regardless of whether it is focusable. This
@@ -75,14 +81,32 @@
private static final int DEFAULT_ANCHORED_GRAVITY = Gravity.TOP | Gravity.START;
+ /**
+ * Default animation style indicating that separate animations should be
+ * used for top/bottom anchoring states.
+ */
+ private static final int ANIMATION_STYLE_DEFAULT = -1;
+
+ private final int[] mDrawingLocation = new int[2];
+ private final int[] mScreenLocation = new int[2];
+ private final Rect mTempRect = new Rect();
+ private final Rect mAnchorBounds = new Rect();
+
private Context mContext;
private WindowManager mWindowManager;
-
+
private boolean mIsShowing;
private boolean mIsDropdown;
+ /** View that handles event dispatch and content transitions. */
+ private PopupDecorView mDecorView;
+
+ /** View that holds the popup background. May be the content view. */
+ private View mBackgroundView;
+
+ /** The contents of the popup. */
private View mContentView;
- private View mPopupView;
+
private boolean mFocusable;
private int mInputMethodMode = INPUT_METHOD_FROM_FOCUSABLE;
private int mSoftInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
@@ -112,49 +136,52 @@
private float mElevation;
- private int[] mDrawingLocation = new int[2];
- private int[] mScreenLocation = new int[2];
- private Rect mTempRect = new Rect();
-
private Drawable mBackground;
private Drawable mAboveAnchorBackgroundDrawable;
private Drawable mBelowAnchorBackgroundDrawable;
- // Temporary animation centers. Should be moved into window params?
- private int mAnchorRelativeX;
- private int mAnchorRelativeY;
+ private Transition mEnterTransition;
+ private Transition mExitTransition;
private boolean mAboveAnchor;
private int mWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
-
+
private OnDismissListener mOnDismissListener;
private boolean mIgnoreCheekPress = false;
- private int mAnimationStyle = -1;
-
+ private int mAnimationStyle = ANIMATION_STYLE_DEFAULT;
+
private static final int[] ABOVE_ANCHOR_STATE_SET = new int[] {
com.android.internal.R.attr.state_above_anchor
};
private WeakReference<View> mAnchor;
- private final OnScrollChangedListener mOnScrollChangedListener =
- new OnScrollChangedListener() {
- @Override
- public void onScrollChanged() {
- final View anchor = mAnchor != null ? mAnchor.get() : null;
- if (anchor != null && mPopupView != null) {
- final WindowManager.LayoutParams p = (WindowManager.LayoutParams)
- mPopupView.getLayoutParams();
+ private final EpicenterCallback mEpicenterCallback = new EpicenterCallback() {
+ @Override
+ public Rect onGetEpicenter(Transition transition) {
+ return mAnchorBounds;
+ }
+ };
- updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
- mAnchoredGravity));
- update(p.x, p.y, -1, -1, true);
- }
+ private final OnScrollChangedListener mOnScrollChangedListener = new OnScrollChangedListener() {
+ @Override
+ public void onScrollChanged() {
+ final View anchor = mAnchor != null ? mAnchor.get() : null;
+ if (anchor != null && mDecorView != null) {
+ final WindowManager.LayoutParams p = (WindowManager.LayoutParams)
+ mDecorView.getLayoutParams();
+
+ updateAboveAnchor(findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
+ mAnchoredGravity));
+ update(p.x, p.y, -1, -1, true);
}
- };
+ }
+ };
- private int mAnchorXoff, mAnchorYoff, mAnchoredGravity;
+ private int mAnchorXoff;
+ private int mAnchorYoff;
+ private int mAnchoredGravity;
private boolean mOverlapAnchor;
private boolean mPopupViewInitialLayoutDirectionInherited;
@@ -185,10 +212,10 @@
public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
-
+
/**
* <p>Create a new, empty, non focusable popup window of dimension (0,0).</p>
- *
+ *
* <p>The popup does not provide a background.</p>
*/
public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
@@ -201,11 +228,34 @@
mElevation = a.getDimension(R.styleable.PopupWindow_popupElevation, 0);
mOverlapAnchor = a.getBoolean(R.styleable.PopupWindow_overlapAnchor, false);
- final int animStyle = a.getResourceId(R.styleable.PopupWindow_popupAnimationStyle, -1);
- mAnimationStyle = animStyle == R.style.Animation_PopupWindow ? -1 : animStyle;
+ // Preserve default behavior from Gingerbread. If the animation is
+ // undefined or explicitly specifies the Gingerbread animation style,
+ // use a sentinel value.
+ if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupAnimationStyle)) {
+ final int animStyle = a.getResourceId(R.styleable.PopupWindow_popupAnimationStyle, 0);
+ if (animStyle == R.style.Animation_PopupWindow) {
+ mAnimationStyle = ANIMATION_STYLE_DEFAULT;
+ } else {
+ mAnimationStyle = animStyle;
+ }
+ } else {
+ mAnimationStyle = ANIMATION_STYLE_DEFAULT;
+ }
+
+ final Transition enterTransition = getTransition(a.getResourceId(
+ R.styleable.PopupWindow_popupEnterTransition, 0));
+ final Transition exitTransition;
+ if (a.hasValueOrEmpty(R.styleable.PopupWindow_popupExitTransition)) {
+ exitTransition = getTransition(a.getResourceId(
+ R.styleable.PopupWindow_popupExitTransition, 0));
+ } else {
+ exitTransition = enterTransition == null ? null : enterTransition.clone();
+ }
a.recycle();
+ setEnterTransition(enterTransition);
+ setExitTransition(exitTransition);
setBackgroundDrawable(bg);
}
@@ -286,6 +336,37 @@
setFocusable(focusable);
}
+ public void setEnterTransition(Transition enterTransition) {
+ mEnterTransition = enterTransition;
+
+ if (mEnterTransition != null) {
+ mEnterTransition.setEpicenterCallback(mEpicenterCallback);
+ }
+ }
+
+ public void setExitTransition(Transition exitTransition) {
+ mExitTransition = exitTransition;
+
+ if (mExitTransition != null) {
+ mExitTransition.setEpicenterCallback(mEpicenterCallback);
+ }
+ }
+
+ private Transition getTransition(int resId) {
+ if (resId != 0 && resId != R.transition.no_transition) {
+ final TransitionInflater inflater = TransitionInflater.from(mContext);
+ final Transition transition = inflater.inflateTransition(resId);
+ if (transition != null) {
+ final boolean isEmpty = transition instanceof TransitionSet
+ && ((TransitionSet) transition).getTransitionCount() == 0;
+ if (!isEmpty) {
+ return transition;
+ }
+ }
+ }
+ return null;
+ }
+
/**
* Return the drawable used as the popup window's background.
*
@@ -379,7 +460,7 @@
* Set the flag on popup to ignore cheek press events; by default this flag
* is set to false
* which means the popup will not ignore cheek press dispatch events.
- *
+ *
* <p>If the popup is showing, calling this method will take effect only
* the next time the popup is shown or through a manual call to one of
* the {@link #update()} methods.</p>
@@ -389,7 +470,7 @@
public void setIgnoreCheekPress() {
mIgnoreCheekPress = true;
}
-
+
/**
* <p>Change the animation style resource for this popup.</p>
@@ -401,13 +482,13 @@
* @param animationStyle animation style to use when the popup appears
* and disappears. Set to -1 for the default animation, 0 for no
* animation, or a resource identifier for an explicit animation.
- *
+ *
* @see #update()
*/
public void setAnimationStyle(int animationStyle) {
mAnimationStyle = animationStyle;
}
-
+
/**
* <p>Return the view used as the content of the popup window.</p>
*
@@ -491,7 +572,7 @@
* @param focusable true if the popup should grab focus, false otherwise.
*
* @see #isFocusable()
- * @see #isShowing()
+ * @see #isShowing()
* @see #update()
*/
public void setFocusable(boolean focusable) {
@@ -500,23 +581,23 @@
/**
* Return the current value in {@link #setInputMethodMode(int)}.
- *
+ *
* @see #setInputMethodMode(int)
*/
public int getInputMethodMode() {
return mInputMethodMode;
-
+
}
-
+
/**
* Control how the popup operates with an input method: one of
* {@link #INPUT_METHOD_FROM_FOCUSABLE}, {@link #INPUT_METHOD_NEEDED},
* or {@link #INPUT_METHOD_NOT_NEEDED}.
- *
+ *
* <p>If the popup is showing, calling this method will take effect only
* the next time the popup is shown or through a manual call to one of
* the {@link #update()} methods.</p>
- *
+ *
* @see #getInputMethodMode()
* @see #update()
*/
@@ -547,12 +628,12 @@
public int getSoftInputMode() {
return mSoftInputMode;
}
-
+
/**
* <p>Indicates whether the popup window receives touch events.</p>
- *
+ *
* @return true if the popup is touchable, false otherwise
- *
+ *
* @see #setTouchable(boolean)
*/
public boolean isTouchable() {
@@ -571,7 +652,7 @@
* @param touchable true if the popup should receive touch events, false otherwise
*
* @see #isTouchable()
- * @see #isShowing()
+ * @see #isShowing()
* @see #update()
*/
public void setTouchable(boolean touchable) {
@@ -581,9 +662,9 @@
/**
* <p>Indicates whether the popup window will be informed of touch events
* outside of its window.</p>
- *
+ *
* @return true if the popup is outside touchable, false otherwise
- *
+ *
* @see #setOutsideTouchable(boolean)
*/
public boolean isOutsideTouchable() {
@@ -604,7 +685,7 @@
* touch events, false otherwise
*
* @see #isOutsideTouchable()
- * @see #isShowing()
+ * @see #isShowing()
* @see #update()
*/
public void setOutsideTouchable(boolean touchable) {
@@ -613,9 +694,9 @@
/**
* <p>Indicates whether clipping of the popup window is enabled.</p>
- *
+ *
* @return true if the clipping is enabled, false otherwise
- *
+ *
* @see #setClippingEnabled(boolean)
*/
public boolean isClippingEnabled() {
@@ -626,13 +707,13 @@
* <p>Allows the popup window to extend beyond the bounds of the screen. By default the
* window is clipped to the screen boundaries. Setting this to false will allow windows to be
* accurately positioned.</p>
- *
+ *
* <p>If the popup is showing, calling this method will take effect only
* the next time the popup is shown or through a manual call to one of
* the {@link #update()} methods.</p>
*
* @param enabled false if the window should be allowed to extend outside of the screen
- * @see #isShowing()
+ * @see #isShowing()
* @see #isClippingEnabled()
* @see #update()
*/
@@ -660,12 +741,12 @@
void setAllowScrollingAnchorParent(boolean enabled) {
mAllowScrollingAnchorParent = enabled;
}
-
+
/**
* <p>Indicates whether the popup window supports splitting touches.</p>
- *
+ *
* @return true if the touch splitting is enabled, false otherwise
- *
+ *
* @see #setSplitTouchEnabled(boolean)
*/
public boolean isSplitTouchEnabled() {
@@ -794,7 +875,7 @@
* window manager by the popup. By default these are 0, meaning that
* the current width or height is requested as an explicit size from
* the window manager. You can supply
- * {@link ViewGroup.LayoutParams#WRAP_CONTENT} or
+ * {@link ViewGroup.LayoutParams#WRAP_CONTENT} or
* {@link ViewGroup.LayoutParams#MATCH_PARENT} to have that measure
* spec supplied instead, replacing the absolute width and height that
* has been set in the popup.</p>
@@ -815,7 +896,7 @@
mWidthMode = widthSpec;
mHeightMode = heightSpec;
}
-
+
/**
* <p>Return this popup's height MeasureSpec</p>
*
@@ -836,7 +917,7 @@
* @param height the height MeasureSpec of the popup
*
* @see #getHeight()
- * @see #isShowing()
+ * @see #isShowing()
*/
public void setHeight(int height) {
mHeight = height;
@@ -847,7 +928,7 @@
*
* @return the width MeasureSpec of the popup
*
- * @see #setWidth(int)
+ * @see #setWidth(int)
*/
public int getWidth() {
return mWidth;
@@ -913,7 +994,7 @@
* a gravity of {@link android.view.Gravity#NO_GRAVITY} is similar to specifying
* <code>Gravity.LEFT | Gravity.TOP</code>.
* </p>
- *
+ *
* @param parent a parent view to get the {@link android.view.View#getWindowToken()} token from
* @param gravity the gravity which controls the placement of the popup window
* @param x the popup's x location offset
@@ -946,7 +1027,7 @@
WindowManager.LayoutParams p = createPopupLayout(token);
p.windowAnimations = computeAnimationResource();
-
+
preparePopup(p);
if (gravity == Gravity.NO_GRAVITY) {
gravity = Gravity.TOP | Gravity.START;
@@ -1049,12 +1130,12 @@
// do the job.
if (mAboveAnchorBackgroundDrawable != null) {
if (mAboveAnchor) {
- mPopupView.setBackground(mAboveAnchorBackgroundDrawable);
+ mDecorView.setBackground(mAboveAnchorBackgroundDrawable);
} else {
- mPopupView.setBackground(mBelowAnchorBackgroundDrawable);
+ mDecorView.setBackground(mBelowAnchorBackgroundDrawable);
}
} else {
- mPopupView.refreshDrawableState();
+ mDecorView.refreshDrawableState();
}
}
}
@@ -1089,36 +1170,79 @@
+ "calling setContentView() before attempting to show the popup.");
}
+ // When a background is available, we embed the content view within
+ // another view that owns the background drawable.
if (mBackground != null) {
- final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
- int height = ViewGroup.LayoutParams.MATCH_PARENT;
- if (layoutParams != null &&
- layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
- height = ViewGroup.LayoutParams.WRAP_CONTENT;
- }
-
- // when a background is available, we embed the content view
- // within another view that owns the background drawable
- PopupViewContainer popupViewContainer = new PopupViewContainer(mContext);
- PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, height
- );
- popupViewContainer.setBackground(mBackground);
- popupViewContainer.addView(mContentView, listParams);
-
- mPopupView = popupViewContainer;
+ mBackgroundView = createBackgroundView(mContentView);
+ mBackgroundView.setBackground(mBackground);
} else {
- mPopupView = mContentView;
+ mBackgroundView = mContentView;
}
- mPopupView.setElevation(mElevation);
+ mDecorView = createDecorView(mBackgroundView);
+
+ // The background owner should be elevated so that it casts a shadow.
+ mBackgroundView.setElevation(mElevation);
+
+ // We may wrap that in another view, so we'll need to manually specify
+ // the surface insets.
+ final int surfaceInset = (int) Math.ceil(mBackgroundView.getZ() * 2);
+ p.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
+ p.hasManualSurfaceInsets = true;
+
mPopupViewInitialLayoutDirectionInherited =
- (mPopupView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
+ (mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
mPopupWidth = p.width;
mPopupHeight = p.height;
}
/**
+ * Wraps a content view in a PopupViewContainer.
+ *
+ * @param contentView the content view to wrap
+ * @return a PopupViewContainer that wraps the content view
+ */
+ private PopupBackgroundView createBackgroundView(View contentView) {
+ final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
+ final int height;
+ if (layoutParams != null && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
+ height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ } else {
+ height = ViewGroup.LayoutParams.MATCH_PARENT;
+ }
+
+ final PopupBackgroundView backgroundView = new PopupBackgroundView(mContext);
+ final PopupBackgroundView.LayoutParams listParams = new PopupBackgroundView.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, height);
+ backgroundView.addView(contentView, listParams);
+
+ return backgroundView;
+ }
+
+ /**
+ * Wraps a content view in a FrameLayout.
+ *
+ * @param contentView the content view to wrap
+ * @return a FrameLayout that wraps the content view
+ */
+ private PopupDecorView createDecorView(View contentView) {
+ final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
+ final int height;
+ if (layoutParams != null && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
+ height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ } else {
+ height = ViewGroup.LayoutParams.MATCH_PARENT;
+ }
+
+ final PopupDecorView decorView = new PopupDecorView(mContext);
+ decorView.addView(contentView, ViewGroup.LayoutParams.MATCH_PARENT, height);
+ decorView.setClipChildren(false);
+ decorView.setClipToPadding(false);
+
+ return decorView;
+ }
+
+ /**
* <p>Invoke the popup window by adding the content view to the window
* manager.</p>
*
@@ -1130,16 +1254,34 @@
if (mContext != null) {
p.packageName = mContext.getPackageName();
}
- mPopupView.setFitsSystemWindows(mLayoutInsetDecor);
+
+ final View rootView = mContentView.getRootView();
+ rootView.setFitsSystemWindows(mLayoutInsetDecor);
setLayoutDirectionFromAnchor();
- mWindowManager.addView(mPopupView, p);
+
+ mWindowManager.addView(rootView, p);
+
+ // Postpone enter transition until the scene root has been laid out.
+ if (mEnterTransition != null) {
+ mEnterTransition.addTarget(mBackgroundView);
+ mEnterTransition.addListener(new Transition.TransitionListenerAdapter() {
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ transition.removeListener(this);
+ transition.removeTarget(mBackgroundView);
+ }
+ });
+
+ mDecorView.getViewTreeObserver().addOnGlobalLayoutListener(
+ new PostLayoutTransitionListener(mDecorView, mEnterTransition));
+ }
}
private void setLayoutDirectionFromAnchor() {
if (mAnchor != null) {
View anchor = mAnchor.get();
if (anchor != null && mPopupViewInitialLayoutDirectionInherited) {
- mPopupView.setLayoutDirection(anchor.getLayoutDirection());
+ mDecorView.setLayoutDirection(anchor.getLayoutDirection());
}
}
}
@@ -1224,7 +1366,7 @@
}
private int computeAnimationResource() {
- if (mAnimationStyle == -1) {
+ if (mAnimationStyle == ANIMATION_STYLE_DEFAULT) {
if (mIsDropdown) {
return mAboveAnchor
? com.android.internal.R.style.Animation_DropDownUp
@@ -1243,7 +1385,7 @@
* <p>
* The height must have been set on the layout parameters prior to calling
* this method.
- *
+ *
* @param anchor the view on which the popup window must be anchored
* @param p the layout parameters used to display the drop down
* @param xoff horizontal offset used to adjust for background padding
@@ -1342,18 +1484,18 @@
p.gravity |= Gravity.DISPLAY_CLIP_VERTICAL;
// Compute the position of the anchor relative to the popup.
- mAnchorRelativeX = mDrawingLocation[0] - p.x + anchorHeight / 2;
- mAnchorRelativeY = mDrawingLocation[1] - p.y + anchorWidth / 2;
+ mAnchorBounds.set(0, 0, anchorWidth, anchorHeight);
+ mAnchorBounds.offset(mDrawingLocation[0] - p.x, mDrawingLocation[1] - p.y);
return onTop;
}
-
+
/**
* Returns the maximum height that is available for the popup to be
* completely shown. It is recommended that this height be the maximum for
* the popup's height, otherwise it is possible that the popup will be
* clipped.
- *
+ *
* @param anchor The view on which the popup window must be anchored.
* @return The maximum available height for the popup to be completely
* shown.
@@ -1376,14 +1518,14 @@
public int getMaxAvailableHeight(View anchor, int yOffset) {
return getMaxAvailableHeight(anchor, yOffset, false);
}
-
+
/**
* Returns the maximum height that is available for the popup to be
* completely shown, optionally ignoring any bottom decorations such as
* the input method. It is recommended that this height be the maximum for
* the popup's height, otherwise it is possible that the popup will be
* clipped.
- *
+ *
* @param anchor The view on which the popup window must be anchored.
* @param yOffset y offset from the view's bottom edge
* @param ignoreBottomDecorations if true, the height returned will be
@@ -1391,7 +1533,7 @@
* bottom decorations
* @return The maximum available height for the popup to be completely
* shown.
- *
+ *
* @hide Pending API council approval.
*/
public int getMaxAvailableHeight(View anchor, int yOffset, boolean ignoreBottomDecorations) {
@@ -1400,7 +1542,7 @@
final int[] anchorPos = mDrawingLocation;
anchor.getLocationOnScreen(anchorPos);
-
+
int bottomEdge = displayFrame.bottom;
if (ignoreBottomDecorations) {
Resources res = anchor.getContext().getResources();
@@ -1413,49 +1555,78 @@
int returnedHeight = Math.max(distanceToBottom, distanceToTop);
if (mBackground != null) {
mBackground.getPadding(mTempRect);
- returnedHeight -= mTempRect.top + mTempRect.bottom;
+ returnedHeight -= mTempRect.top + mTempRect.bottom;
}
-
+
return returnedHeight;
}
-
+
/**
* <p>Dispose of the popup window. This method can be invoked only after
* {@link #showAsDropDown(android.view.View)} has been executed. Failing that, calling
* this method will have no effect.</p>
*
- * @see #showAsDropDown(android.view.View)
+ * @see #showAsDropDown(android.view.View)
*/
public void dismiss() {
- if (isShowing() && mPopupView != null) {
+ if (isShowing() && mDecorView != null) {
mIsShowing = false;
unregisterForScrollChanged();
- try {
- mWindowManager.removeViewImmediate(mPopupView);
- } finally {
- if (mPopupView != mContentView && mPopupView instanceof ViewGroup) {
- ((ViewGroup) mPopupView).removeView(mContentView);
- }
- mPopupView = null;
+ if (mExitTransition != null) {
+ mExitTransition.addTarget(mBackgroundView);
+ mExitTransition.addListener(new Transition.TransitionListenerAdapter() {
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ transition.removeListener(this);
+ transition.removeTarget(mBackgroundView);
- if (mOnDismissListener != null) {
- mOnDismissListener.onDismiss();
- }
+ dismissImmediate();
+ }
+ });
+
+ TransitionManager.beginDelayedTransition(mDecorView, mExitTransition);
+
+ // Transition to invisible.
+ mBackgroundView.setVisibility(View.INVISIBLE);
+ } else {
+ dismissImmediate();
+ }
+ }
+ }
+
+ /**
+ * Removes the popup from the window manager and tears down the supporting
+ * view hierarchy, if necessary.
+ */
+ private void dismissImmediate() {
+ try {
+ mWindowManager.removeViewImmediate(mDecorView);
+ } finally {
+ mDecorView.removeView(mBackgroundView);
+ mDecorView = null;
+
+ if (mBackgroundView != mContentView) {
+ ((ViewGroup) mBackgroundView).removeView(mContentView);
+ }
+ mBackgroundView = null;
+
+ if (mOnDismissListener != null) {
+ mOnDismissListener.onDismiss();
}
}
}
/**
* Sets the listener to be called when the window is dismissed.
- *
+ *
* @param onDismissListener The listener.
*/
public void setOnDismissListener(OnDismissListener onDismissListener) {
mOnDismissListener = onDismissListener;
}
-
+
/**
* Updates the state of the popup window, if it is currently being displayed,
* from the currently set state. This includes:
@@ -1467,12 +1638,12 @@
if (!isShowing() || mContentView == null) {
return;
}
-
- WindowManager.LayoutParams p = (WindowManager.LayoutParams)
- mPopupView.getLayoutParams();
-
+
+ final WindowManager.LayoutParams p =
+ (WindowManager.LayoutParams) mDecorView.getLayoutParams();
+
boolean update = false;
-
+
final int newAnim = computeAnimationResource();
if (newAnim != p.windowAnimations) {
p.windowAnimations = newAnim;
@@ -1487,7 +1658,7 @@
if (update) {
setLayoutDirectionFromAnchor();
- mWindowManager.updateViewLayout(mPopupView, p);
+ mWindowManager.updateViewLayout(mDecorView, p);
}
}
@@ -1500,11 +1671,11 @@
* @param height the new height
*/
public void update(int width, int height) {
- WindowManager.LayoutParams p = (WindowManager.LayoutParams)
- mPopupView.getLayoutParams();
+ final WindowManager.LayoutParams p =
+ (WindowManager.LayoutParams) mDecorView.getLayoutParams();
update(p.x, p.y, width, height, false);
}
-
+
/**
* <p>Updates the position and the dimension of the popup window. Width and
* height can be set to -1 to update location only. Calling this function
@@ -1548,7 +1719,8 @@
return;
}
- WindowManager.LayoutParams p = (WindowManager.LayoutParams) mPopupView.getLayoutParams();
+ final WindowManager.LayoutParams p =
+ (WindowManager.LayoutParams) mDecorView.getLayoutParams();
boolean update = force;
@@ -1588,7 +1760,7 @@
if (update) {
setLayoutDirectionFromAnchor();
- mWindowManager.updateViewLayout(mPopupView, p);
+ mWindowManager.updateViewLayout(mDecorView, p);
}
}
@@ -1655,7 +1827,7 @@
}
final WindowManager.LayoutParams p =
- (WindowManager.LayoutParams) mPopupView.getLayoutParams();
+ (WindowManager.LayoutParams) mDecorView.getLayoutParams();
final int x = p.x;
final int y = p.y;
if (updateLocation) {
@@ -1694,7 +1866,7 @@
private void registerForScrollChanged(View anchor, int xoff, int yoff, int gravity) {
unregisterForScrollChanged();
- mAnchor = new WeakReference<View>(anchor);
+ mAnchor = new WeakReference<>(anchor);
ViewTreeObserver vto = anchor.getViewTreeObserver();
if (vto != null) {
vto.addOnScrollChangedListener(mOnScrollChangedListener);
@@ -1705,23 +1877,49 @@
mAnchoredGravity = gravity;
}
- private class PopupViewContainer extends FrameLayout {
- private static final String TAG = "PopupWindow.PopupViewContainer";
+ /**
+ * Layout listener used to run a transition immediately after a view is
+ * laid out. Forces the view to transition from invisible to visible.
+ */
+ private static class PostLayoutTransitionListener implements
+ ViewTreeObserver.OnGlobalLayoutListener {
+ private final ViewGroup mSceneRoot;
+ private final Transition mTransition;
- public PopupViewContainer(Context context) {
- super(context);
+ public PostLayoutTransitionListener(ViewGroup sceneRoot, Transition transition) {
+ mSceneRoot = sceneRoot;
+ mTransition = transition;
}
@Override
- protected int[] onCreateDrawableState(int extraSpace) {
- if (mAboveAnchor) {
- // 1 more needed for the above anchor state
- final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
- View.mergeDrawableStates(drawableState, ABOVE_ANCHOR_STATE_SET);
- return drawableState;
- } else {
- return super.onCreateDrawableState(extraSpace);
+ public void onGlobalLayout() {
+ final ViewTreeObserver observer = mSceneRoot.getViewTreeObserver();
+ if (observer == null) {
+ // View has been detached.
+ return;
}
+
+ observer.removeOnGlobalLayoutListener(this);
+
+ // Set all targets to be initially invisible.
+ final List<View> targets = mTransition.getTargets();
+ final int N = targets.size();
+ for (int i = 0; i < N; i++) {
+ targets.get(i).setVisibility(View.INVISIBLE);
+ }
+
+ TransitionManager.beginDelayedTransition(mSceneRoot, mTransition);
+
+ // Transition targets to visible.
+ for (int i = 0; i < N; i++) {
+ targets.get(i).setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
+ private class PopupDecorView extends FrameLayout {
+ public PopupDecorView(Context context) {
+ super(context);
}
@Override
@@ -1731,15 +1929,14 @@
return super.dispatchKeyEvent(event);
}
- if (event.getAction() == KeyEvent.ACTION_DOWN
- && event.getRepeatCount() == 0) {
- KeyEvent.DispatcherState state = getKeyDispatcherState();
+ if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
+ final KeyEvent.DispatcherState state = getKeyDispatcherState();
if (state != null) {
state.startTracking(event, this);
}
return true;
} else if (event.getAction() == KeyEvent.ACTION_UP) {
- KeyEvent.DispatcherState state = getKeyDispatcherState();
+ final KeyEvent.DispatcherState state = getKeyDispatcherState();
if (state != null && state.isTracking(event) && !event.isCanceled()) {
dismiss();
return true;
@@ -1763,7 +1960,7 @@
public boolean onTouchEvent(MotionEvent event) {
final int x = (int) event.getX();
final int y = (int) event.getY();
-
+
if ((event.getAction() == MotionEvent.ACTION_DOWN)
&& ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
dismiss();
@@ -1775,17 +1972,22 @@
return super.onTouchEvent(event);
}
}
+ }
- /** @hide */
+ private class PopupBackgroundView extends FrameLayout {
+ public PopupBackgroundView(Context context) {
+ super(context);
+ }
+
@Override
- public void sendAccessibilityEventInternal(int eventType) {
- // clinets are interested in the content not the container, make it event source
- if (mContentView != null) {
- mContentView.sendAccessibilityEvent(eventType);
+ protected int[] onCreateDrawableState(int extraSpace) {
+ if (mAboveAnchor) {
+ final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
+ View.mergeDrawableStates(drawableState, ABOVE_ANCHOR_STATE_SET);
+ return drawableState;
} else {
- super.sendAccessibilityEventInternal(eventType);
+ return super.onCreateDrawableState(extraSpace);
}
}
}
-
}
diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java
index 094a712..4b061d3 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -1271,11 +1271,19 @@
@Override
protected int getVirtualViewAt(float x, float y) {
final int id;
+
+ // Calling getDegreesXY() has side-effects, so we need to cache the
+ // current inner circle value and restore after the call.
+ final boolean wasOnInnerCircle = mIsOnInnerCircle;
final int degrees = getDegreesFromXY(x, y, true);
+ final boolean isOnInnerCircle = mIsOnInnerCircle;
+ mIsOnInnerCircle = wasOnInnerCircle;
+
if (degrees != -1) {
final int snapDegrees = snapOnly30s(degrees, 0) % 360;
if (mShowHours) {
- final int hour = getHourForDegrees(snapDegrees, mIsOnInnerCircle);
+ final int hour24 = getHourForDegrees(snapDegrees, isOnInnerCircle);
+ final int hour = mIs24HourMode ? hour24 : hour24To12(hour24);
id = makeId(TYPE_HOUR, hour);
} else {
final int current = getCurrentMinute();
@@ -1404,6 +1412,16 @@
return hour24;
}
+ private int hour24To12(int hour24) {
+ if (hour24 == 0) {
+ return 12;
+ } else if (hour24 > 12) {
+ return hour24 - 12;
+ } else {
+ return hour24;
+ }
+ }
+
private void getBoundsForVirtualView(int virtualViewId, Rect bounds) {
final float radius;
final int type = getTypeFromId(virtualViewId);
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 13d6b42..a282cf5 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -17,6 +17,7 @@
package android.widget;
import android.animation.ObjectAnimator;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -24,6 +25,7 @@
import android.graphics.Canvas;
import android.graphics.Insets;
import android.graphics.Paint;
+import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.Region.Op;
@@ -84,7 +86,17 @@
private static final int MONOSPACE = 3;
private Drawable mThumbDrawable;
+ private ColorStateList mThumbTintList = null;
+ private PorterDuff.Mode mThumbTintMode = null;
+ private boolean mHasThumbTint = false;
+ private boolean mHasThumbTintMode = false;
+
private Drawable mTrackDrawable;
+ private ColorStateList mTrackTintList = null;
+ private PorterDuff.Mode mTrackTintMode = null;
+ private boolean mHasTrackTint = false;
+ private boolean mHasTrackTintMode = false;
+
private int mThumbTextPadding;
private int mSwitchMinWidth;
private int mSwitchPadding;
@@ -473,6 +485,86 @@
}
/**
+ * Applies a tint to the track drawable. Does not modify the current
+ * tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+ * <p>
+ * Subsequent calls to {@link #setTrackDrawable(Drawable)} will
+ * automatically mutate the drawable and apply the specified tint and tint
+ * mode using {@link Drawable#setTintList(ColorStateList)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#Switch_trackTint
+ * @see #getTrackTintList()
+ * @see Drawable#setTintList(ColorStateList)
+ */
+ public void setTrackTintList(@Nullable ColorStateList tint) {
+ mTrackTintList = tint;
+ mHasTrackTint = true;
+
+ applyTrackTint();
+ }
+
+ /**
+ * @return the tint applied to the track drawable
+ * @attr ref android.R.styleable#Switch_trackTint
+ * @see #setTrackTintList(ColorStateList)
+ */
+ @Nullable
+ public ColorStateList getTrackTintList() {
+ return mTrackTintList;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setTrackTintList(ColorStateList)}} to the track drawable.
+ * The default mode is {@link PorterDuff.Mode#SRC_IN}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#Switch_trackTintMode
+ * @see #getTrackTintMode()
+ * @see Drawable#setTintMode(PorterDuff.Mode)
+ */
+ public void setTrackTintMode(@Nullable PorterDuff.Mode tintMode) {
+ mTrackTintMode = tintMode;
+ mHasTrackTintMode = true;
+
+ applyTrackTint();
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the track
+ * drawable
+ * @attr ref android.R.styleable#Switch_trackTintMode
+ * @see #setTrackTintMode(PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getTrackTintMode() {
+ return mTrackTintMode;
+ }
+
+ private void applyTrackTint() {
+ if (mTrackDrawable != null && (mHasTrackTint || mHasTrackTintMode)) {
+ mTrackDrawable = mTrackDrawable.mutate();
+
+ if (mHasTrackTint) {
+ mTrackDrawable.setTintList(mTrackTintList);
+ }
+
+ if (mHasTrackTintMode) {
+ mTrackDrawable.setTintMode(mTrackTintMode);
+ }
+
+ // The drawable (or one of its children) may not have been
+ // stateful before applying the tint, so let's try again.
+ if (mTrackDrawable.isStateful()) {
+ mTrackDrawable.setState(getDrawableState());
+ }
+ }
+ }
+
+ /**
* Set the drawable used for the switch "thumb" - the piece that the user
* can physically touch and drag along the track.
*
@@ -516,6 +608,86 @@
}
/**
+ * Applies a tint to the thumb drawable. Does not modify the current
+ * tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+ * <p>
+ * Subsequent calls to {@link #setThumbDrawable(Drawable)} will
+ * automatically mutate the drawable and apply the specified tint and tint
+ * mode using {@link Drawable#setTintList(ColorStateList)}.
+ *
+ * @param tint the tint to apply, may be {@code null} to clear tint
+ *
+ * @attr ref android.R.styleable#Switch_thumbTint
+ * @see #getThumbTintList()
+ * @see Drawable#setTintList(ColorStateList)
+ */
+ public void setThumbTintList(@Nullable ColorStateList tint) {
+ mThumbTintList = tint;
+ mHasThumbTint = true;
+
+ applyThumbTint();
+ }
+
+ /**
+ * @return the tint applied to the thumb drawable
+ * @attr ref android.R.styleable#Switch_thumbTint
+ * @see #setThumbTintList(ColorStateList)
+ */
+ @Nullable
+ public ColorStateList getThumbTintList() {
+ return mThumbTintList;
+ }
+
+ /**
+ * Specifies the blending mode used to apply the tint specified by
+ * {@link #setThumbTintList(ColorStateList)}} to the thumb drawable.
+ * The default mode is {@link PorterDuff.Mode#SRC_IN}.
+ *
+ * @param tintMode the blending mode used to apply the tint, may be
+ * {@code null} to clear tint
+ * @attr ref android.R.styleable#Switch_thumbTintMode
+ * @see #getThumbTintMode()
+ * @see Drawable#setTintMode(PorterDuff.Mode)
+ */
+ public void setThumbTintMode(@Nullable PorterDuff.Mode tintMode) {
+ mThumbTintMode = tintMode;
+ mHasThumbTintMode = true;
+
+ applyThumbTint();
+ }
+
+ /**
+ * @return the blending mode used to apply the tint to the thumb
+ * drawable
+ * @attr ref android.R.styleable#Switch_thumbTintMode
+ * @see #setThumbTintMode(PorterDuff.Mode)
+ */
+ @Nullable
+ public PorterDuff.Mode getThumbTintMode() {
+ return mThumbTintMode;
+ }
+
+ private void applyThumbTint() {
+ if (mThumbDrawable != null && (mHasThumbTint || mHasThumbTintMode)) {
+ mThumbDrawable = mThumbDrawable.mutate();
+
+ if (mHasThumbTint) {
+ mThumbDrawable.setTintList(mThumbTintList);
+ }
+
+ if (mHasThumbTintMode) {
+ mThumbDrawable.setTintMode(mThumbTintMode);
+ }
+
+ // The drawable (or one of its children) may not have been
+ // stateful before applying the tint, so let's try again.
+ if (mThumbDrawable.isStateful()) {
+ mThumbDrawable.setState(getDrawableState());
+ }
+ }
+ }
+
+ /**
* Specifies whether the track should be split by the thumb. When true,
* the thumb's optical bounds will be clipped out of the track drawable,
* then the thumb will be drawn into the resulting gap.
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index e8bf623..94fc9e9 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -665,6 +665,7 @@
ColorStateList textColorLink = null;
int textSize = 15;
String fontFamily = null;
+ boolean fontFamilyExplicit = false;
int typefaceIndex = -1;
int styleIndex = -1;
boolean allCaps = false;
@@ -1029,6 +1030,7 @@
case com.android.internal.R.styleable.TextView_fontFamily:
fontFamily = a.getString(attr);
+ fontFamilyExplicit = true;
break;
case com.android.internal.R.styleable.TextView_password:
@@ -1333,6 +1335,9 @@
typefaceIndex = MONOSPACE;
}
+ if (typefaceIndex != -1 && !fontFamilyExplicit) {
+ fontFamily = null;
+ }
setTypefaceFromAttrs(fontFamily, typefaceIndex, styleIndex);
if (shadowcolor != 0) {
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 0f35e0d..c5325c4 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -1101,18 +1101,6 @@
* @hide
*/
@Override
- public void addClickableRectsForAccessibility(List<RectF> outRects) {
- // This class always consumes touch events, therefore if it
- // covers a view we do not want to send a click over it.
- RectF bounds = new RectF();
- bounds.set(0, 0, getWidth(), getHeight());
- outRects.add(bounds);
- }
-
- /**
- * @hide
- */
- @Override
protected void onSetLayoutParams(View child, ViewGroup.LayoutParams lp) {
/*
* Apps may set ActionBar.LayoutParams on their action bar custom views when
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index aba4bd0..0eb52cb 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -800,7 +800,12 @@
if (args.niceName != null) {
String property = "wrap." + args.niceName;
if (property.length() > 31) {
- property = property.substring(0, 31);
+ // Avoid creating an illegal property name when truncating.
+ if (property.charAt(30) != '.') {
+ property = property.substring(0, 31);
+ } else {
+ property = property.substring(0, 30);
+ }
}
args.invokeWith = SystemProperties.get(property);
if (args.invokeWith != null && args.invokeWith.length() == 0) {
diff --git a/core/java/com/android/internal/transition/EpicenterClipReveal.java b/core/java/com/android/internal/transition/EpicenterClipReveal.java
new file mode 100644
index 0000000..d8a7f16
--- /dev/null
+++ b/core/java/com/android/internal/transition/EpicenterClipReveal.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.transition;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.animation.RectEvaluator;
+import android.content.Context;
+import android.graphics.Rect;
+import android.transition.TransitionValues;
+import android.transition.Visibility;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * EpicenterClipReveal captures the {@link View#getClipBounds()} before and
+ * after the scene change and animates between those and the epicenter bounds
+ * during a visibility transition.
+ */
+public class EpicenterClipReveal extends Visibility {
+ private static final String PROPNAME_CLIP = "android:epicenterReveal:clip";
+ private static final String PROPNAME_BOUNDS = "android:epicenterReveal:bounds";
+
+ public EpicenterClipReveal() {}
+
+ public EpicenterClipReveal(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ super.captureStartValues(transitionValues);
+ captureValues(transitionValues);
+ }
+
+ @Override
+ public void captureEndValues(TransitionValues transitionValues) {
+ super.captureEndValues(transitionValues);
+ captureValues(transitionValues);
+ }
+
+ private void captureValues(TransitionValues values) {
+ final View view = values.view;
+ if (view.getVisibility() == View.GONE) {
+ return;
+ }
+
+ final Rect clip = view.getClipBounds();
+ values.values.put(PROPNAME_CLIP, clip);
+
+ if (clip == null) {
+ final Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
+ values.values.put(PROPNAME_BOUNDS, bounds);
+ }
+ }
+
+ @Override
+ public Animator onAppear(ViewGroup sceneRoot, View view,
+ TransitionValues startValues, TransitionValues endValues) {
+ if (endValues == null) {
+ return null;
+ }
+
+ final Rect start = getEpicenter();
+ final Rect end = getBestRect(endValues);
+
+ // Prepare the view.
+ view.setClipBounds(start);
+
+ return createRectAnimator(view, start, end);
+ }
+
+ @Override
+ public Animator onDisappear(ViewGroup sceneRoot, View view,
+ TransitionValues startValues, TransitionValues endValues) {
+ if (startValues == null) {
+ return null;
+ }
+
+ final Rect start = getBestRect(startValues);
+ final Rect end = getEpicenter();
+
+ // Prepare the view.
+ view.setClipBounds(start);
+
+ return createRectAnimator(view, start, end);
+ }
+
+ private Rect getBestRect(TransitionValues values) {
+ final Rect clipRect = (Rect) values.values.get(PROPNAME_CLIP);
+ if (clipRect == null) {
+ return (Rect) values.values.get(PROPNAME_BOUNDS);
+ }
+ return clipRect;
+ }
+
+ private Animator createRectAnimator(View view, Rect start, Rect end) {
+ final RectEvaluator evaluator = new RectEvaluator(new Rect());
+ return ObjectAnimator.ofObject(view, "clipBounds", evaluator, start, end);
+ }
+}
diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java
index e8e2c8d..7937a95 100644
--- a/core/java/com/android/internal/widget/ActionBarContainer.java
+++ b/core/java/com/android/internal/widget/ActionBarContainer.java
@@ -227,18 +227,6 @@
return true;
}
- /**
- * @hide
- */
- @Override
- public void addClickableRectsForAccessibility(List<RectF> outRects) {
- // This class always consumes touch events, therefore if it
- // covers a view we do not want to send a click over it.
- RectF bounds = new RectF();
- bounds.set(0, 0, getWidth(), getHeight());
- outRects.add(bounds);
- }
-
@Override
public boolean onHoverEvent(MotionEvent ev) {
super.onHoverEvent(ev);
diff --git a/core/java/com/android/internal/widget/FaceUnlockView.java b/core/java/com/android/internal/widget/FaceUnlockView.java
deleted file mode 100644
index 121e601..0000000
--- a/core/java/com/android/internal/widget/FaceUnlockView.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2012 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.internal.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.RelativeLayout;
-
-public class FaceUnlockView extends RelativeLayout {
- private static final String TAG = "FaceUnlockView";
-
- public FaceUnlockView(Context context) {
- this(context, null);
- }
-
- public FaceUnlockView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- private int resolveMeasured(int measureSpec, int desired)
- {
- int result = 0;
- int specSize = MeasureSpec.getSize(measureSpec);
- switch (MeasureSpec.getMode(measureSpec)) {
- case MeasureSpec.UNSPECIFIED:
- result = desired;
- break;
- case MeasureSpec.AT_MOST:
- result = Math.max(specSize, desired);
- break;
- case MeasureSpec.EXACTLY:
- default:
- result = specSize;
- }
- return result;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int minimumWidth = getSuggestedMinimumWidth();
- final int minimumHeight = getSuggestedMinimumHeight();
- int viewWidth = resolveMeasured(widthMeasureSpec, minimumWidth);
- int viewHeight = resolveMeasured(heightMeasureSpec, minimumHeight);
-
- final int chosenSize = Math.min(viewWidth, viewHeight);
- final int newWidthMeasureSpec =
- MeasureSpec.makeMeasureSpec(chosenSize, MeasureSpec.AT_MOST);
- final int newHeightMeasureSpec =
- MeasureSpec.makeMeasureSpec(chosenSize, MeasureSpec.AT_MOST);
-
- super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec);
- }
-}
diff --git a/core/java/com/android/internal/widget/SizeAdaptiveLayout.java b/core/java/com/android/internal/widget/SizeAdaptiveLayout.java
deleted file mode 100644
index 5f3c5f9..0000000
--- a/core/java/com/android/internal/widget/SizeAdaptiveLayout.java
+++ /dev/null
@@ -1,442 +0,0 @@
-/*
- * Copyright (C) 2012 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.internal.widget;
-
-import java.lang.Math;
-
-import com.android.internal.R;
-
-import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.StateListDrawable;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.StateSet;
-import android.view.View;
-import android.view.ViewDebug;
-import android.view.ViewGroup;
-import android.widget.RemoteViews.RemoteView;
-
-/**
- * A layout that switches between its children based on the requested layout height.
- * Each child specifies its minimum and maximum valid height. Results are undefined
- * if children specify overlapping ranges. A child may specify the maximum height
- * as 'unbounded' to indicate that it is willing to be displayed arbitrarily tall.
- *
- * <p>
- * See {@link SizeAdaptiveLayout.LayoutParams} for a full description of the
- * layout parameters used by SizeAdaptiveLayout.
- */
-@RemoteView
-public class SizeAdaptiveLayout extends ViewGroup {
-
- private static final String TAG = "SizeAdaptiveLayout";
- private static final boolean DEBUG = false;
- private static final boolean REPORT_BAD_BOUNDS = true;
- private static final long CROSSFADE_TIME = 250;
-
- // TypedArray indices
- private static final int MIN_VALID_HEIGHT =
- R.styleable.SizeAdaptiveLayout_Layout_layout_minHeight;
- private static final int MAX_VALID_HEIGHT =
- R.styleable.SizeAdaptiveLayout_Layout_layout_maxHeight;
-
- // view state
- private View mActiveChild;
- private View mLastActive;
-
- // animation state
- private AnimatorSet mTransitionAnimation;
- private AnimatorListener mAnimatorListener;
- private ObjectAnimator mFadePanel;
- private ObjectAnimator mFadeView;
- private int mCanceledAnimationCount;
- private View mEnteringView;
- private View mLeavingView;
- // View used to hide larger views under smaller ones to create a uniform crossfade
- private View mModestyPanel;
- private int mModestyPanelTop;
-
- public SizeAdaptiveLayout(Context context) {
- this(context, null);
- }
-
- public SizeAdaptiveLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public SizeAdaptiveLayout(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public SizeAdaptiveLayout(
- Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- initialize();
- }
-
- private void initialize() {
- mModestyPanel = new View(getContext());
- // If the SizeAdaptiveLayout has a solid background, use it as a transition hint.
- Drawable background = getBackground();
- if (background instanceof StateListDrawable) {
- StateListDrawable sld = (StateListDrawable) background;
- sld.setState(StateSet.WILD_CARD);
- background = sld.getCurrent();
- }
- if (background instanceof ColorDrawable) {
- mModestyPanel.setBackgroundDrawable(background);
- }
- SizeAdaptiveLayout.LayoutParams layout =
- new SizeAdaptiveLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT);
- mModestyPanel.setLayoutParams(layout);
- addView(mModestyPanel);
- mFadePanel = ObjectAnimator.ofFloat(mModestyPanel, "alpha", 0f);
- mFadeView = ObjectAnimator.ofFloat(null, "alpha", 0f);
- mAnimatorListener = new BringToFrontOnEnd();
- mTransitionAnimation = new AnimatorSet();
- mTransitionAnimation.play(mFadeView).with(mFadePanel);
- mTransitionAnimation.setDuration(CROSSFADE_TIME);
- mTransitionAnimation.addListener(mAnimatorListener);
- }
-
- /**
- * Visible for testing
- * @hide
- */
- public Animator getTransitionAnimation() {
- return mTransitionAnimation;
- }
-
- /**
- * Visible for testing
- * @hide
- */
- public View getModestyPanel() {
- return mModestyPanel;
- }
-
- @Override
- public void onAttachedToWindow() {
- mLastActive = null;
- // make sure all views start off invisible.
- for (int i = 0; i < getChildCount(); i++) {
- getChildAt(i).setVisibility(View.GONE);
- }
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (DEBUG) Log.d(TAG, this + " measure spec: " +
- MeasureSpec.toString(heightMeasureSpec));
- View model = selectActiveChild(heightMeasureSpec);
- if (model == null) {
- setMeasuredDimension(0, 0);
- return;
- }
- SizeAdaptiveLayout.LayoutParams lp =
- (SizeAdaptiveLayout.LayoutParams) model.getLayoutParams();
- if (DEBUG) Log.d(TAG, "active min: " + lp.minHeight + " max: " + lp.maxHeight);
- measureChild(model, widthMeasureSpec, heightMeasureSpec);
- int childHeight = model.getMeasuredHeight();
- int childWidth = model.getMeasuredHeight();
- int childState = combineMeasuredStates(0, model.getMeasuredState());
- if (DEBUG) Log.d(TAG, "measured child at: " + childHeight);
- int resolvedWidth = resolveSizeAndState(childWidth, widthMeasureSpec, childState);
- int resolvedHeight = resolveSizeAndState(childHeight, heightMeasureSpec, childState);
- if (DEBUG) Log.d(TAG, "resolved to: " + resolvedHeight);
- int boundedHeight = clampSizeToBounds(resolvedHeight, model);
- if (DEBUG) Log.d(TAG, "bounded to: " + boundedHeight);
- setMeasuredDimension(resolvedWidth, boundedHeight);
- }
-
- private int clampSizeToBounds(int measuredHeight, View child) {
- SizeAdaptiveLayout.LayoutParams lp =
- (SizeAdaptiveLayout.LayoutParams) child.getLayoutParams();
- int heightIn = View.MEASURED_SIZE_MASK & measuredHeight;
- int height = Math.max(heightIn, lp.minHeight);
- if (lp.maxHeight != SizeAdaptiveLayout.LayoutParams.UNBOUNDED) {
- height = Math.min(height, lp.maxHeight);
- }
-
- if (REPORT_BAD_BOUNDS && heightIn != height) {
- Log.d(TAG, this + "child view " + child + " " +
- "measured out of bounds at " + heightIn +"px " +
- "clamped to " + height + "px");
- }
-
- return height;
- }
-
- //TODO extend to width and height
- private View selectActiveChild(int heightMeasureSpec) {
- final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
-
- View unboundedView = null;
- View tallestView = null;
- int tallestViewSize = 0;
- View smallestView = null;
- int smallestViewSize = Integer.MAX_VALUE;
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- if (child != mModestyPanel) {
- SizeAdaptiveLayout.LayoutParams lp =
- (SizeAdaptiveLayout.LayoutParams) child.getLayoutParams();
- if (DEBUG) Log.d(TAG, "looking at " + i +
- " with min: " + lp.minHeight +
- " max: " + lp.maxHeight);
- if (lp.maxHeight == SizeAdaptiveLayout.LayoutParams.UNBOUNDED &&
- unboundedView == null) {
- unboundedView = child;
- }
- if (lp.maxHeight > tallestViewSize) {
- tallestViewSize = lp.maxHeight;
- tallestView = child;
- }
- if (lp.minHeight < smallestViewSize) {
- smallestViewSize = lp.minHeight;
- smallestView = child;
- }
- if (heightMode != MeasureSpec.UNSPECIFIED &&
- heightSize >= lp.minHeight && heightSize <= lp.maxHeight) {
- if (DEBUG) Log.d(TAG, " found exact match, finishing early");
- return child;
- }
- }
- }
- if (unboundedView != null) {
- tallestView = unboundedView;
- }
- if (heightMode == MeasureSpec.UNSPECIFIED || heightSize > tallestViewSize) {
- return tallestView;
- } else {
- return smallestView;
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- if (DEBUG) Log.d(TAG, this + " onlayout height: " + (bottom - top));
- mLastActive = mActiveChild;
- int measureSpec = View.MeasureSpec.makeMeasureSpec(bottom - top,
- View.MeasureSpec.EXACTLY);
- mActiveChild = selectActiveChild(measureSpec);
- if (mActiveChild == null) return;
-
- mActiveChild.setVisibility(View.VISIBLE);
-
- if (mLastActive != mActiveChild && mLastActive != null) {
- if (DEBUG) Log.d(TAG, this + " changed children from: " + mLastActive +
- " to: " + mActiveChild);
-
- mEnteringView = mActiveChild;
- mLeavingView = mLastActive;
-
- mEnteringView.setAlpha(1f);
-
- mModestyPanel.setAlpha(1f);
- mModestyPanel.bringToFront();
- mModestyPanelTop = mLeavingView.getHeight();
- mModestyPanel.setVisibility(View.VISIBLE);
- // TODO: mModestyPanel background should be compatible with mLeavingView
-
- mLeavingView.bringToFront();
-
- if (mTransitionAnimation.isRunning()) {
- mTransitionAnimation.cancel();
- }
- mFadeView.setTarget(mLeavingView);
- mFadeView.setFloatValues(0f);
- mFadePanel.setFloatValues(0f);
- mTransitionAnimation.setupStartValues();
- mTransitionAnimation.start();
- }
- final int childWidth = mActiveChild.getMeasuredWidth();
- final int childHeight = mActiveChild.getMeasuredHeight();
- // TODO investigate setting LAYER_TYPE_HARDWARE on mLastActive
- mActiveChild.layout(0, 0, childWidth, childHeight);
-
- if (DEBUG) Log.d(TAG, "got modesty offset of " + mModestyPanelTop);
- mModestyPanel.layout(0, mModestyPanelTop, childWidth, mModestyPanelTop + childHeight);
- }
-
- @Override
- public LayoutParams generateLayoutParams(AttributeSet attrs) {
- if (DEBUG) Log.d(TAG, "generate layout from attrs");
- return new SizeAdaptiveLayout.LayoutParams(getContext(), attrs);
- }
-
- @Override
- protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
- if (DEBUG) Log.d(TAG, "generate default layout from viewgroup");
- return new SizeAdaptiveLayout.LayoutParams(p);
- }
-
- @Override
- protected LayoutParams generateDefaultLayoutParams() {
- if (DEBUG) Log.d(TAG, "generate default layout from null");
- return new SizeAdaptiveLayout.LayoutParams();
- }
-
- @Override
- protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
- return p instanceof SizeAdaptiveLayout.LayoutParams;
- }
-
- /**
- * Per-child layout information associated with ViewSizeAdaptiveLayout.
- *
- * TODO extend to width and height
- *
- * @attr ref android.R.styleable#SizeAdaptiveLayout_Layout_layout_minHeight
- * @attr ref android.R.styleable#SizeAdaptiveLayout_Layout_layout_maxHeight
- */
- public static class LayoutParams extends ViewGroup.LayoutParams {
-
- /**
- * Indicates the minimum valid height for the child.
- */
- @ViewDebug.ExportedProperty(category = "layout")
- public int minHeight;
-
- /**
- * Indicates the maximum valid height for the child.
- */
- @ViewDebug.ExportedProperty(category = "layout")
- public int maxHeight;
-
- /**
- * Constant value for maxHeight that indicates there is not maximum height.
- */
- public static final int UNBOUNDED = -1;
-
- /**
- * {@inheritDoc}
- */
- public LayoutParams(Context c, AttributeSet attrs) {
- super(c, attrs);
- if (DEBUG) {
- Log.d(TAG, "construct layout from attrs");
- for (int i = 0; i < attrs.getAttributeCount(); i++) {
- Log.d(TAG, " " + attrs.getAttributeName(i) + " = " +
- attrs.getAttributeValue(i));
- }
- }
- TypedArray a =
- c.obtainStyledAttributes(attrs,
- R.styleable.SizeAdaptiveLayout_Layout);
-
- minHeight = a.getDimensionPixelSize(MIN_VALID_HEIGHT, 0);
- if (DEBUG) Log.d(TAG, "got minHeight of: " + minHeight);
-
- try {
- maxHeight = a.getLayoutDimension(MAX_VALID_HEIGHT, UNBOUNDED);
- if (DEBUG) Log.d(TAG, "got maxHeight of: " + maxHeight);
- } catch (Exception e) {
- if (DEBUG) Log.d(TAG, "caught exception looking for maxValidHeight " + e);
- }
-
- a.recycle();
- }
-
- /**
- * Creates a new set of layout parameters with the specified width, height
- * and valid height bounds.
- *
- * @param width the width, either {@link #MATCH_PARENT},
- * {@link #WRAP_CONTENT} or a fixed size in pixels
- * @param height the height, either {@link #MATCH_PARENT},
- * {@link #WRAP_CONTENT} or a fixed size in pixels
- * @param minHeight the minimum height of this child
- * @param maxHeight the maximum height of this child
- * or {@link #UNBOUNDED} if the child can grow forever
- */
- public LayoutParams(int width, int height, int minHeight, int maxHeight) {
- super(width, height);
- this.minHeight = minHeight;
- this.maxHeight = maxHeight;
- }
-
- /**
- * {@inheritDoc}
- */
- public LayoutParams(int width, int height) {
- this(width, height, UNBOUNDED, UNBOUNDED);
- }
-
- /**
- * Constructs a new LayoutParams with default values as defined in {@link LayoutParams}.
- */
- public LayoutParams() {
- this(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- }
-
- /**
- * {@inheritDoc}
- */
- public LayoutParams(ViewGroup.LayoutParams p) {
- super(p);
- minHeight = UNBOUNDED;
- maxHeight = UNBOUNDED;
- }
-
- public String debug(String output) {
- return output + "SizeAdaptiveLayout.LayoutParams={" +
- ", max=" + maxHeight +
- ", max=" + minHeight + "}";
- }
- }
-
- class BringToFrontOnEnd implements AnimatorListener {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mCanceledAnimationCount == 0) {
- mLeavingView.setVisibility(View.GONE);
- mModestyPanel.setVisibility(View.GONE);
- mEnteringView.bringToFront();
- mEnteringView = null;
- mLeavingView = null;
- } else {
- mCanceledAnimationCount--;
- }
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mCanceledAnimationCount++;
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
- if (DEBUG) Log.d(TAG, "fade animation repeated: should never happen.");
- assert(false);
- }
-
- @Override
- public void onAnimationStart(Animator animation) {
- }
- }
-}
diff --git a/core/java/com/android/internal/widget/WaveView.java b/core/java/com/android/internal/widget/WaveView.java
deleted file mode 100644
index 9e7a649..0000000
--- a/core/java/com/android/internal/widget/WaveView.java
+++ /dev/null
@@ -1,663 +0,0 @@
-/*
- * Copyright (C) 2010 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.internal.widget;
-
-import java.util.ArrayList;
-
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.drawable.BitmapDrawable;
-import android.media.AudioAttributes;
-import android.os.UserHandle;
-import android.os.Vibrator;
-import android.provider.Settings;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-
-import com.android.internal.R;
-
-/**
- * A special widget containing a center and outer ring. Moving the center ring to the outer ring
- * causes an event that can be caught by implementing OnTriggerListener.
- */
-public class WaveView extends View implements ValueAnimator.AnimatorUpdateListener {
- private static final String TAG = "WaveView";
- private static final boolean DBG = false;
- private static final int WAVE_COUNT = 20; // default wave count
- private static final long VIBRATE_SHORT = 20; // msec
- private static final long VIBRATE_LONG = 20; // msec
-
- // Lock state machine states
- private static final int STATE_RESET_LOCK = 0;
- private static final int STATE_READY = 1;
- private static final int STATE_START_ATTEMPT = 2;
- private static final int STATE_ATTEMPTING = 3;
- private static final int STATE_UNLOCK_ATTEMPT = 4;
- private static final int STATE_UNLOCK_SUCCESS = 5;
-
- // Animation properties.
- private static final long DURATION = 300; // duration of transitional animations
- private static final long FINAL_DURATION = 200; // duration of final animations when unlocking
- private static final long RING_DELAY = 1300; // when to start fading animated rings
- private static final long FINAL_DELAY = 200; // delay for unlock success animation
- private static final long SHORT_DELAY = 100; // for starting one animation after another.
- private static final long WAVE_DURATION = 2000; // amount of time for way to expand/decay
- private static final long RESET_TIMEOUT = 3000; // elapsed time of inactivity before we reset
- private static final long DELAY_INCREMENT = 15; // increment per wave while tracking motion
- private static final long DELAY_INCREMENT2 = 12; // increment per wave while not tracking
- private static final long WAVE_DELAY = WAVE_DURATION / WAVE_COUNT; // initial propagation delay
-
- /**
- * The scale by which to multiply the unlock handle width to compute the radius
- * in which it can be grabbed when accessibility is disabled.
- */
- private static final float GRAB_HANDLE_RADIUS_SCALE_ACCESSIBILITY_DISABLED = 0.5f;
-
- /**
- * The scale by which to multiply the unlock handle width to compute the radius
- * in which it can be grabbed when accessibility is enabled (more generous).
- */
- private static final float GRAB_HANDLE_RADIUS_SCALE_ACCESSIBILITY_ENABLED = 1.0f;
-
- private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
- .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
- .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
- .build();
-
- private Vibrator mVibrator;
- private OnTriggerListener mOnTriggerListener;
- private ArrayList<DrawableHolder> mDrawables = new ArrayList<DrawableHolder>(3);
- private ArrayList<DrawableHolder> mLightWaves = new ArrayList<DrawableHolder>(WAVE_COUNT);
- private boolean mFingerDown = false;
- private float mRingRadius = 182.0f; // Radius of bitmap ring. Used to snap halo to it
- private int mSnapRadius = 136; // minimum threshold for drag unlock
- private int mWaveCount = WAVE_COUNT; // number of waves
- private long mWaveTimerDelay = WAVE_DELAY;
- private int mCurrentWave = 0;
- private float mLockCenterX; // center of widget as dictated by widget size
- private float mLockCenterY;
- private float mMouseX; // current mouse position as of last touch event
- private float mMouseY;
- private DrawableHolder mUnlockRing;
- private DrawableHolder mUnlockDefault;
- private DrawableHolder mUnlockHalo;
- private int mLockState = STATE_RESET_LOCK;
- private int mGrabbedState = OnTriggerListener.NO_HANDLE;
- private boolean mWavesRunning;
- private boolean mFinishWaves;
-
- public WaveView(Context context) {
- this(context, null);
- }
-
- public WaveView(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- // TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WaveView);
- // mOrientation = a.getInt(R.styleable.WaveView_orientation, HORIZONTAL);
- // a.recycle();
-
- initDrawables();
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- mLockCenterX = 0.5f * w;
- mLockCenterY = 0.5f * h;
- super.onSizeChanged(w, h, oldw, oldh);
- }
-
- @Override
- protected int getSuggestedMinimumWidth() {
- // View should be large enough to contain the unlock ring + halo
- return mUnlockRing.getWidth() + mUnlockHalo.getWidth();
- }
-
- @Override
- protected int getSuggestedMinimumHeight() {
- // View should be large enough to contain the unlock ring + halo
- return mUnlockRing.getHeight() + mUnlockHalo.getHeight();
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
- int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
- int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
- int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
- int width;
- int height;
-
- if (widthSpecMode == MeasureSpec.AT_MOST) {
- width = Math.min(widthSpecSize, getSuggestedMinimumWidth());
- } else if (widthSpecMode == MeasureSpec.EXACTLY) {
- width = widthSpecSize;
- } else {
- width = getSuggestedMinimumWidth();
- }
-
- if (heightSpecMode == MeasureSpec.AT_MOST) {
- height = Math.min(heightSpecSize, getSuggestedMinimumWidth());
- } else if (heightSpecMode == MeasureSpec.EXACTLY) {
- height = heightSpecSize;
- } else {
- height = getSuggestedMinimumHeight();
- }
-
- setMeasuredDimension(width, height);
- }
-
- private void initDrawables() {
- mUnlockRing = new DrawableHolder(createDrawable(R.drawable.unlock_ring));
- mUnlockRing.setX(mLockCenterX);
- mUnlockRing.setY(mLockCenterY);
- mUnlockRing.setScaleX(0.1f);
- mUnlockRing.setScaleY(0.1f);
- mUnlockRing.setAlpha(0.0f);
- mDrawables.add(mUnlockRing);
-
- mUnlockDefault = new DrawableHolder(createDrawable(R.drawable.unlock_default));
- mUnlockDefault.setX(mLockCenterX);
- mUnlockDefault.setY(mLockCenterY);
- mUnlockDefault.setScaleX(0.1f);
- mUnlockDefault.setScaleY(0.1f);
- mUnlockDefault.setAlpha(0.0f);
- mDrawables.add(mUnlockDefault);
-
- mUnlockHalo = new DrawableHolder(createDrawable(R.drawable.unlock_halo));
- mUnlockHalo.setX(mLockCenterX);
- mUnlockHalo.setY(mLockCenterY);
- mUnlockHalo.setScaleX(0.1f);
- mUnlockHalo.setScaleY(0.1f);
- mUnlockHalo.setAlpha(0.0f);
- mDrawables.add(mUnlockHalo);
-
- BitmapDrawable wave = createDrawable(R.drawable.unlock_wave);
- for (int i = 0; i < mWaveCount; i++) {
- DrawableHolder holder = new DrawableHolder(wave);
- mLightWaves.add(holder);
- holder.setAlpha(0.0f);
- }
- }
-
- private void waveUpdateFrame(float mouseX, float mouseY, boolean fingerDown) {
- double distX = mouseX - mLockCenterX;
- double distY = mouseY - mLockCenterY;
- int dragDistance = (int) Math.ceil(Math.hypot(distX, distY));
- double touchA = Math.atan2(distX, distY);
- float ringX = (float) (mLockCenterX + mRingRadius * Math.sin(touchA));
- float ringY = (float) (mLockCenterY + mRingRadius * Math.cos(touchA));
-
- switch (mLockState) {
- case STATE_RESET_LOCK:
- if (DBG) Log.v(TAG, "State RESET_LOCK");
- mWaveTimerDelay = WAVE_DELAY;
- for (int i = 0; i < mLightWaves.size(); i++) {
- DrawableHolder holder = mLightWaves.get(i);
- holder.addAnimTo(300, 0, "alpha", 0.0f, false);
- }
- for (int i = 0; i < mLightWaves.size(); i++) {
- mLightWaves.get(i).startAnimations(this);
- }
-
- mUnlockRing.addAnimTo(DURATION, 0, "x", mLockCenterX, true);
- mUnlockRing.addAnimTo(DURATION, 0, "y", mLockCenterY, true);
- mUnlockRing.addAnimTo(DURATION, 0, "scaleX", 0.1f, true);
- mUnlockRing.addAnimTo(DURATION, 0, "scaleY", 0.1f, true);
- mUnlockRing.addAnimTo(DURATION, 0, "alpha", 0.0f, true);
-
- mUnlockDefault.removeAnimationFor("x");
- mUnlockDefault.removeAnimationFor("y");
- mUnlockDefault.removeAnimationFor("scaleX");
- mUnlockDefault.removeAnimationFor("scaleY");
- mUnlockDefault.removeAnimationFor("alpha");
- mUnlockDefault.setX(mLockCenterX);
- mUnlockDefault.setY(mLockCenterY);
- mUnlockDefault.setScaleX(0.1f);
- mUnlockDefault.setScaleY(0.1f);
- mUnlockDefault.setAlpha(0.0f);
- mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleX", 1.0f, true);
- mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleY", 1.0f, true);
- mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "alpha", 1.0f, true);
-
- mUnlockHalo.removeAnimationFor("x");
- mUnlockHalo.removeAnimationFor("y");
- mUnlockHalo.removeAnimationFor("scaleX");
- mUnlockHalo.removeAnimationFor("scaleY");
- mUnlockHalo.removeAnimationFor("alpha");
- mUnlockHalo.setX(mLockCenterX);
- mUnlockHalo.setY(mLockCenterY);
- mUnlockHalo.setScaleX(0.1f);
- mUnlockHalo.setScaleY(0.1f);
- mUnlockHalo.setAlpha(0.0f);
- mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "x", mLockCenterX, true);
- mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "y", mLockCenterY, true);
- mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "scaleX", 1.0f, true);
- mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "scaleY", 1.0f, true);
- mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "alpha", 1.0f, true);
-
- removeCallbacks(mLockTimerActions);
-
- mLockState = STATE_READY;
- break;
-
- case STATE_READY:
- if (DBG) Log.v(TAG, "State READY");
- mWaveTimerDelay = WAVE_DELAY;
- break;
-
- case STATE_START_ATTEMPT:
- if (DBG) Log.v(TAG, "State START_ATTEMPT");
- mUnlockDefault.removeAnimationFor("x");
- mUnlockDefault.removeAnimationFor("y");
- mUnlockDefault.removeAnimationFor("scaleX");
- mUnlockDefault.removeAnimationFor("scaleY");
- mUnlockDefault.removeAnimationFor("alpha");
- mUnlockDefault.setX(mLockCenterX + 182);
- mUnlockDefault.setY(mLockCenterY);
- mUnlockDefault.setScaleX(0.1f);
- mUnlockDefault.setScaleY(0.1f);
- mUnlockDefault.setAlpha(0.0f);
-
- mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleX", 1.0f, false);
- mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleY", 1.0f, false);
- mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "alpha", 1.0f, false);
-
- mUnlockRing.addAnimTo(DURATION, 0, "scaleX", 1.0f, true);
- mUnlockRing.addAnimTo(DURATION, 0, "scaleY", 1.0f, true);
- mUnlockRing.addAnimTo(DURATION, 0, "alpha", 1.0f, true);
-
- mLockState = STATE_ATTEMPTING;
- break;
-
- case STATE_ATTEMPTING:
- if (DBG) Log.v(TAG, "State ATTEMPTING (fingerDown = " + fingerDown + ")");
- if (dragDistance > mSnapRadius) {
- mFinishWaves = true; // don't start any more waves.
- if (fingerDown) {
- mUnlockHalo.addAnimTo(0, 0, "x", ringX, true);
- mUnlockHalo.addAnimTo(0, 0, "y", ringY, true);
- mUnlockHalo.addAnimTo(0, 0, "scaleX", 1.0f, true);
- mUnlockHalo.addAnimTo(0, 0, "scaleY", 1.0f, true);
- mUnlockHalo.addAnimTo(0, 0, "alpha", 1.0f, true);
- } else {
- if (DBG) Log.v(TAG, "up detected, moving to STATE_UNLOCK_ATTEMPT");
- mLockState = STATE_UNLOCK_ATTEMPT;
- }
- } else {
- // If waves have stopped, we need to kick them off again...
- if (!mWavesRunning) {
- mWavesRunning = true;
- mFinishWaves = false;
- // mWaveTimerDelay = WAVE_DELAY;
- postDelayed(mAddWaveAction, mWaveTimerDelay);
- }
- mUnlockHalo.addAnimTo(0, 0, "x", mouseX, true);
- mUnlockHalo.addAnimTo(0, 0, "y", mouseY, true);
- mUnlockHalo.addAnimTo(0, 0, "scaleX", 1.0f, true);
- mUnlockHalo.addAnimTo(0, 0, "scaleY", 1.0f, true);
- mUnlockHalo.addAnimTo(0, 0, "alpha", 1.0f, true);
- }
- break;
-
- case STATE_UNLOCK_ATTEMPT:
- if (DBG) Log.v(TAG, "State UNLOCK_ATTEMPT");
- if (dragDistance > mSnapRadius) {
- for (int n = 0; n < mLightWaves.size(); n++) {
- DrawableHolder wave = mLightWaves.get(n);
- long delay = 1000L*(6 + n - mCurrentWave)/10L;
- wave.addAnimTo(FINAL_DURATION, delay, "x", ringX, true);
- wave.addAnimTo(FINAL_DURATION, delay, "y", ringY, true);
- wave.addAnimTo(FINAL_DURATION, delay, "scaleX", 0.1f, true);
- wave.addAnimTo(FINAL_DURATION, delay, "scaleY", 0.1f, true);
- wave.addAnimTo(FINAL_DURATION, delay, "alpha", 0.0f, true);
- }
- for (int i = 0; i < mLightWaves.size(); i++) {
- mLightWaves.get(i).startAnimations(this);
- }
-
- mUnlockRing.addAnimTo(FINAL_DURATION, 0, "x", ringX, false);
- mUnlockRing.addAnimTo(FINAL_DURATION, 0, "y", ringY, false);
- mUnlockRing.addAnimTo(FINAL_DURATION, 0, "scaleX", 0.1f, false);
- mUnlockRing.addAnimTo(FINAL_DURATION, 0, "scaleY", 0.1f, false);
- mUnlockRing.addAnimTo(FINAL_DURATION, 0, "alpha", 0.0f, false);
-
- mUnlockRing.addAnimTo(FINAL_DURATION, FINAL_DELAY, "alpha", 0.0f, false);
-
- mUnlockDefault.removeAnimationFor("x");
- mUnlockDefault.removeAnimationFor("y");
- mUnlockDefault.removeAnimationFor("scaleX");
- mUnlockDefault.removeAnimationFor("scaleY");
- mUnlockDefault.removeAnimationFor("alpha");
- mUnlockDefault.setX(ringX);
- mUnlockDefault.setY(ringY);
- mUnlockDefault.setScaleX(0.1f);
- mUnlockDefault.setScaleY(0.1f);
- mUnlockDefault.setAlpha(0.0f);
-
- mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "x", ringX, true);
- mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "y", ringY, true);
- mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "scaleX", 1.0f, true);
- mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "scaleY", 1.0f, true);
- mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "alpha", 1.0f, true);
-
- mUnlockDefault.addAnimTo(FINAL_DURATION, FINAL_DELAY, "scaleX", 3.0f, false);
- mUnlockDefault.addAnimTo(FINAL_DURATION, FINAL_DELAY, "scaleY", 3.0f, false);
- mUnlockDefault.addAnimTo(FINAL_DURATION, FINAL_DELAY, "alpha", 0.0f, false);
-
- mUnlockHalo.addAnimTo(FINAL_DURATION, 0, "x", ringX, false);
- mUnlockHalo.addAnimTo(FINAL_DURATION, 0, "y", ringY, false);
-
- mUnlockHalo.addAnimTo(FINAL_DURATION, FINAL_DELAY, "scaleX", 3.0f, false);
- mUnlockHalo.addAnimTo(FINAL_DURATION, FINAL_DELAY, "scaleY", 3.0f, false);
- mUnlockHalo.addAnimTo(FINAL_DURATION, FINAL_DELAY, "alpha", 0.0f, false);
-
- removeCallbacks(mLockTimerActions);
-
- postDelayed(mLockTimerActions, RESET_TIMEOUT);
-
- dispatchTriggerEvent(OnTriggerListener.CENTER_HANDLE);
- mLockState = STATE_UNLOCK_SUCCESS;
- } else {
- mLockState = STATE_RESET_LOCK;
- }
- break;
-
- case STATE_UNLOCK_SUCCESS:
- if (DBG) Log.v(TAG, "State UNLOCK_SUCCESS");
- removeCallbacks(mAddWaveAction);
- break;
-
- default:
- if (DBG) Log.v(TAG, "Unknown state " + mLockState);
- break;
- }
- mUnlockDefault.startAnimations(this);
- mUnlockHalo.startAnimations(this);
- mUnlockRing.startAnimations(this);
- }
-
- BitmapDrawable createDrawable(int resId) {
- Resources res = getResources();
- Bitmap bitmap = BitmapFactory.decodeResource(res, resId);
- return new BitmapDrawable(res, bitmap);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- waveUpdateFrame(mMouseX, mMouseY, mFingerDown);
- for (int i = 0; i < mDrawables.size(); ++i) {
- mDrawables.get(i).draw(canvas);
- }
- for (int i = 0; i < mLightWaves.size(); ++i) {
- mLightWaves.get(i).draw(canvas);
- }
- }
-
- private final Runnable mLockTimerActions = new Runnable() {
- public void run() {
- if (DBG) Log.v(TAG, "LockTimerActions");
- // reset lock after inactivity
- if (mLockState == STATE_ATTEMPTING) {
- if (DBG) Log.v(TAG, "Timer resets to STATE_RESET_LOCK");
- mLockState = STATE_RESET_LOCK;
- }
- // for prototype, reset after successful unlock
- if (mLockState == STATE_UNLOCK_SUCCESS) {
- if (DBG) Log.v(TAG, "Timer resets to STATE_RESET_LOCK after success");
- mLockState = STATE_RESET_LOCK;
- }
- invalidate();
- }
- };
-
- private final Runnable mAddWaveAction = new Runnable() {
- public void run() {
- double distX = mMouseX - mLockCenterX;
- double distY = mMouseY - mLockCenterY;
- int dragDistance = (int) Math.ceil(Math.hypot(distX, distY));
- if (mLockState == STATE_ATTEMPTING && dragDistance < mSnapRadius
- && mWaveTimerDelay >= WAVE_DELAY) {
- mWaveTimerDelay = Math.min(WAVE_DURATION, mWaveTimerDelay + DELAY_INCREMENT);
-
- DrawableHolder wave = mLightWaves.get(mCurrentWave);
- wave.setAlpha(0.0f);
- wave.setScaleX(0.2f);
- wave.setScaleY(0.2f);
- wave.setX(mMouseX);
- wave.setY(mMouseY);
-
- wave.addAnimTo(WAVE_DURATION, 0, "x", mLockCenterX, true);
- wave.addAnimTo(WAVE_DURATION, 0, "y", mLockCenterY, true);
- wave.addAnimTo(WAVE_DURATION*2/3, 0, "alpha", 1.0f, true);
- wave.addAnimTo(WAVE_DURATION, 0, "scaleX", 1.0f, true);
- wave.addAnimTo(WAVE_DURATION, 0, "scaleY", 1.0f, true);
-
- wave.addAnimTo(1000, RING_DELAY, "alpha", 0.0f, false);
- wave.startAnimations(WaveView.this);
-
- mCurrentWave = (mCurrentWave+1) % mWaveCount;
- if (DBG) Log.v(TAG, "WaveTimerDelay: start new wave in " + mWaveTimerDelay);
- } else {
- mWaveTimerDelay += DELAY_INCREMENT2;
- }
- if (mFinishWaves) {
- // sentinel used to restart the waves after they've stopped
- mWavesRunning = false;
- } else {
- postDelayed(mAddWaveAction, mWaveTimerDelay);
- }
- }
- };
-
- @Override
- public boolean onHoverEvent(MotionEvent event) {
- if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
- final int action = event.getAction();
- switch (action) {
- case MotionEvent.ACTION_HOVER_ENTER:
- event.setAction(MotionEvent.ACTION_DOWN);
- break;
- case MotionEvent.ACTION_HOVER_MOVE:
- event.setAction(MotionEvent.ACTION_MOVE);
- break;
- case MotionEvent.ACTION_HOVER_EXIT:
- event.setAction(MotionEvent.ACTION_UP);
- break;
- }
- onTouchEvent(event);
- event.setAction(action);
- }
- return super.onHoverEvent(event);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- final int action = event.getAction();
- mMouseX = event.getX();
- mMouseY = event.getY();
- boolean handled = false;
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- removeCallbacks(mLockTimerActions);
- mFingerDown = true;
- tryTransitionToStartAttemptState(event);
- handled = true;
- break;
-
- case MotionEvent.ACTION_MOVE:
- tryTransitionToStartAttemptState(event);
- handled = true;
- break;
-
- case MotionEvent.ACTION_UP:
- if (DBG) Log.v(TAG, "ACTION_UP");
- mFingerDown = false;
- postDelayed(mLockTimerActions, RESET_TIMEOUT);
- setGrabbedState(OnTriggerListener.NO_HANDLE);
- // Normally the state machine is driven by user interaction causing redraws.
- // However, when there's no more user interaction and no running animations,
- // the state machine stops advancing because onDraw() never gets called.
- // The following ensures we advance to the next state in this case,
- // either STATE_UNLOCK_ATTEMPT or STATE_RESET_LOCK.
- waveUpdateFrame(mMouseX, mMouseY, mFingerDown);
- handled = true;
- break;
-
- case MotionEvent.ACTION_CANCEL:
- mFingerDown = false;
- handled = true;
- break;
- }
- invalidate();
- return handled ? true : super.onTouchEvent(event);
- }
-
- /**
- * Tries to transition to start attempt state.
- *
- * @param event A motion event.
- */
- private void tryTransitionToStartAttemptState(MotionEvent event) {
- final float dx = event.getX() - mUnlockHalo.getX();
- final float dy = event.getY() - mUnlockHalo.getY();
- float dist = (float) Math.hypot(dx, dy);
- if (dist <= getScaledGrabHandleRadius()) {
- setGrabbedState(OnTriggerListener.CENTER_HANDLE);
- if (mLockState == STATE_READY) {
- mLockState = STATE_START_ATTEMPT;
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- announceUnlockHandle();
- }
- }
- }
- }
-
- /**
- * @return The radius in which the handle is grabbed scaled based on
- * whether accessibility is enabled.
- */
- private float getScaledGrabHandleRadius() {
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- return GRAB_HANDLE_RADIUS_SCALE_ACCESSIBILITY_ENABLED * mUnlockHalo.getWidth();
- } else {
- return GRAB_HANDLE_RADIUS_SCALE_ACCESSIBILITY_DISABLED * mUnlockHalo.getWidth();
- }
- }
-
- /**
- * Announces the unlock handle if accessibility is enabled.
- */
- private void announceUnlockHandle() {
- setContentDescription(mContext.getString(R.string.description_target_unlock_tablet));
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
- setContentDescription(null);
- }
-
- /**
- * Triggers haptic feedback.
- */
- private synchronized void vibrate(long duration) {
- final boolean hapticEnabled = Settings.System.getIntForUser(
- mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 1,
- UserHandle.USER_CURRENT) != 0;
- if (hapticEnabled) {
- if (mVibrator == null) {
- mVibrator = (android.os.Vibrator) getContext()
- .getSystemService(Context.VIBRATOR_SERVICE);
- }
- mVibrator.vibrate(duration, VIBRATION_ATTRIBUTES);
- }
- }
-
- /**
- * Registers a callback to be invoked when the user triggers an event.
- *
- * @param listener the OnDialTriggerListener to attach to this view
- */
- public void setOnTriggerListener(OnTriggerListener listener) {
- mOnTriggerListener = listener;
- }
-
- /**
- * Dispatches a trigger event to listener. Ignored if a listener is not set.
- * @param whichHandle the handle that triggered the event.
- */
- private void dispatchTriggerEvent(int whichHandle) {
- vibrate(VIBRATE_LONG);
- if (mOnTriggerListener != null) {
- mOnTriggerListener.onTrigger(this, whichHandle);
- }
- }
-
- /**
- * Sets the current grabbed state, and dispatches a grabbed state change
- * event to our listener.
- */
- private void setGrabbedState(int newState) {
- if (newState != mGrabbedState) {
- mGrabbedState = newState;
- if (mOnTriggerListener != null) {
- mOnTriggerListener.onGrabbedStateChange(this, mGrabbedState);
- }
- }
- }
-
- public interface OnTriggerListener {
- /**
- * Sent when the user releases the handle.
- */
- public static final int NO_HANDLE = 0;
-
- /**
- * Sent when the user grabs the center handle
- */
- public static final int CENTER_HANDLE = 10;
-
- /**
- * Called when the user drags the center ring beyond a threshold.
- */
- void onTrigger(View v, int whichHandle);
-
- /**
- * Called when the "grabbed state" changes (i.e. when the user either grabs or releases
- * one of the handles.)
- *
- * @param v the view that was triggered
- * @param grabbedState the new state: {@link #NO_HANDLE}, {@link #CENTER_HANDLE},
- */
- void onGrabbedStateChange(View v, int grabbedState);
- }
-
- public void onAnimationUpdate(ValueAnimator animation) {
- invalidate();
- }
-
- public void reset() {
- if (DBG) Log.v(TAG, "reset() : resets state to STATE_RESET_LOCK");
- mLockState = STATE_RESET_LOCK;
- invalidate();
- }
-}
diff --git a/core/java/com/android/internal/widget/multiwaveview/Ease.java b/core/java/com/android/internal/widget/multiwaveview/Ease.java
deleted file mode 100644
index 7f90c44..0000000
--- a/core/java/com/android/internal/widget/multiwaveview/Ease.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.widget.multiwaveview;
-
-import android.animation.TimeInterpolator;
-
-class Ease {
- private static final float DOMAIN = 1.0f;
- private static final float DURATION = 1.0f;
- private static final float START = 0.0f;
-
- static class Linear {
- public static final TimeInterpolator easeNone = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return input;
- }
- };
- }
-
- static class Cubic {
- public static final TimeInterpolator easeIn = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return DOMAIN*(input/=DURATION)*input*input + START;
- }
- };
- public static final TimeInterpolator easeOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return DOMAIN*((input=input/DURATION-1)*input*input + 1) + START;
- }
- };
- public static final TimeInterpolator easeInOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return ((input/=DURATION/2) < 1.0f) ?
- (DOMAIN/2*input*input*input + START)
- : (DOMAIN/2*((input-=2)*input*input + 2) + START);
- }
- };
- }
-
- static class Quad {
- public static final TimeInterpolator easeIn = new TimeInterpolator() {
- public float getInterpolation (float input) {
- return DOMAIN*(input/=DURATION)*input + START;
- }
- };
- public static final TimeInterpolator easeOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return -DOMAIN *(input/=DURATION)*(input-2) + START;
- }
- };
- public static final TimeInterpolator easeInOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return ((input/=DURATION/2) < 1) ?
- (DOMAIN/2*input*input + START)
- : (-DOMAIN/2 * ((--input)*(input-2) - 1) + START);
- }
- };
- }
-
- static class Quart {
- public static final TimeInterpolator easeIn = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return DOMAIN*(input/=DURATION)*input*input*input + START;
- }
- };
- public static final TimeInterpolator easeOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return -DOMAIN * ((input=input/DURATION-1)*input*input*input - 1) + START;
- }
- };
- public static final TimeInterpolator easeInOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return ((input/=DURATION/2) < 1) ?
- (DOMAIN/2*input*input*input*input + START)
- : (-DOMAIN/2 * ((input-=2)*input*input*input - 2) + START);
- }
- };
- }
-
- static class Quint {
- public static final TimeInterpolator easeIn = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return DOMAIN*(input/=DURATION)*input*input*input*input + START;
- }
- };
- public static final TimeInterpolator easeOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return DOMAIN*((input=input/DURATION-1)*input*input*input*input + 1) + START;
- }
- };
- public static final TimeInterpolator easeInOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return ((input/=DURATION/2) < 1) ?
- (DOMAIN/2*input*input*input*input*input + START)
- : (DOMAIN/2*((input-=2)*input*input*input*input + 2) + START);
- }
- };
- }
-
- static class Sine {
- public static final TimeInterpolator easeIn = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return -DOMAIN * (float) Math.cos(input/DURATION * (Math.PI/2)) + DOMAIN + START;
- }
- };
- public static final TimeInterpolator easeOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return DOMAIN * (float) Math.sin(input/DURATION * (Math.PI/2)) + START;
- }
- };
- public static final TimeInterpolator easeInOut = new TimeInterpolator() {
- public float getInterpolation(float input) {
- return -DOMAIN/2 * ((float)Math.cos(Math.PI*input/DURATION) - 1.0f) + START;
- }
- };
- }
-
-}
diff --git a/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java b/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
deleted file mode 100644
index 11ac19e..0000000
--- a/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
+++ /dev/null
@@ -1,1383 +0,0 @@
-/*
- * Copyright (C) 2012 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.internal.widget.multiwaveview;
-
-import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.media.AudioAttributes;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.os.Vibrator;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.accessibility.AccessibilityManager;
-
-import com.android.internal.R;
-
-import java.util.ArrayList;
-
-/**
- * A re-usable widget containing a center, outer ring and wave animation.
- */
-public class GlowPadView extends View {
- private static final String TAG = "GlowPadView";
- private static final boolean DEBUG = false;
-
- // Wave state machine
- private static final int STATE_IDLE = 0;
- private static final int STATE_START = 1;
- private static final int STATE_FIRST_TOUCH = 2;
- private static final int STATE_TRACKING = 3;
- private static final int STATE_SNAP = 4;
- private static final int STATE_FINISH = 5;
-
- // Animation properties.
- private static final float SNAP_MARGIN_DEFAULT = 20.0f; // distance to ring before we snap to it
-
- public interface OnTriggerListener {
- int NO_HANDLE = 0;
- int CENTER_HANDLE = 1;
- public void onGrabbed(View v, int handle);
- public void onReleased(View v, int handle);
- public void onTrigger(View v, int target);
- public void onGrabbedStateChange(View v, int handle);
- public void onFinishFinalAnimation();
- }
-
- // Tuneable parameters for animation
- private static final int WAVE_ANIMATION_DURATION = 1000;
- private static final int RETURN_TO_HOME_DELAY = 1200;
- private static final int RETURN_TO_HOME_DURATION = 200;
- private static final int HIDE_ANIMATION_DELAY = 200;
- private static final int HIDE_ANIMATION_DURATION = 200;
- private static final int SHOW_ANIMATION_DURATION = 200;
- private static final int SHOW_ANIMATION_DELAY = 50;
- private static final int INITIAL_SHOW_HANDLE_DURATION = 200;
- private static final int REVEAL_GLOW_DELAY = 0;
- private static final int REVEAL_GLOW_DURATION = 0;
-
- private static final float TAP_RADIUS_SCALE_ACCESSIBILITY_ENABLED = 1.3f;
- private static final float TARGET_SCALE_EXPANDED = 1.0f;
- private static final float TARGET_SCALE_COLLAPSED = 0.8f;
- private static final float RING_SCALE_EXPANDED = 1.0f;
- private static final float RING_SCALE_COLLAPSED = 0.5f;
-
- private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
- .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
- .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
- .build();
-
- private ArrayList<TargetDrawable> mTargetDrawables = new ArrayList<TargetDrawable>();
- private AnimationBundle mWaveAnimations = new AnimationBundle();
- private AnimationBundle mTargetAnimations = new AnimationBundle();
- private AnimationBundle mGlowAnimations = new AnimationBundle();
- private ArrayList<String> mTargetDescriptions;
- private ArrayList<String> mDirectionDescriptions;
- private OnTriggerListener mOnTriggerListener;
- private TargetDrawable mHandleDrawable;
- private TargetDrawable mOuterRing;
- private Vibrator mVibrator;
-
- private int mFeedbackCount = 3;
- private int mVibrationDuration = 0;
- private int mGrabbedState;
- private int mActiveTarget = -1;
- private float mGlowRadius;
- private float mWaveCenterX;
- private float mWaveCenterY;
- private int mMaxTargetHeight;
- private int mMaxTargetWidth;
- private float mRingScaleFactor = 1f;
- private boolean mAllowScaling;
-
- private float mOuterRadius = 0.0f;
- private float mSnapMargin = 0.0f;
- private float mFirstItemOffset = 0.0f;
- private boolean mMagneticTargets = false;
- private boolean mDragging;
- private int mNewTargetResources;
-
- private class AnimationBundle extends ArrayList<Tweener> {
- private static final long serialVersionUID = 0xA84D78726F127468L;
- private boolean mSuspended;
-
- public void start() {
- if (mSuspended) return; // ignore attempts to start animations
- final int count = size();
- for (int i = 0; i < count; i++) {
- Tweener anim = get(i);
- anim.animator.start();
- }
- }
-
- public void cancel() {
- final int count = size();
- for (int i = 0; i < count; i++) {
- Tweener anim = get(i);
- anim.animator.cancel();
- }
- clear();
- }
-
- public void stop() {
- final int count = size();
- for (int i = 0; i < count; i++) {
- Tweener anim = get(i);
- anim.animator.end();
- }
- clear();
- }
-
- public void setSuspended(boolean suspend) {
- mSuspended = suspend;
- }
- };
-
- private AnimatorListener mResetListener = new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animator) {
- switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY);
- dispatchOnFinishFinalAnimation();
- }
- };
-
- private AnimatorListener mResetListenerWithPing = new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animator) {
- ping();
- switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY);
- dispatchOnFinishFinalAnimation();
- }
- };
-
- private AnimatorUpdateListener mUpdateListener = new AnimatorUpdateListener() {
- public void onAnimationUpdate(ValueAnimator animation) {
- invalidate();
- }
- };
-
- private boolean mAnimatingTargets;
- private AnimatorListener mTargetUpdateListener = new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animator) {
- if (mNewTargetResources != 0) {
- internalSetTargetResources(mNewTargetResources);
- mNewTargetResources = 0;
- hideTargets(false, false);
- }
- mAnimatingTargets = false;
- }
- };
- private int mTargetResourceId;
- private int mTargetDescriptionsResourceId;
- private int mDirectionDescriptionsResourceId;
- private boolean mAlwaysTrackFinger;
- private int mHorizontalInset;
- private int mVerticalInset;
- private int mGravity = Gravity.TOP;
- private boolean mInitialLayout = true;
- private Tweener mBackgroundAnimator;
- private PointCloud mPointCloud;
- private float mInnerRadius;
- private int mPointerId;
-
- public GlowPadView(Context context) {
- this(context, null);
- }
-
- public GlowPadView(Context context, AttributeSet attrs) {
- super(context, attrs);
- Resources res = context.getResources();
-
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GlowPadView);
- mInnerRadius = a.getDimension(R.styleable.GlowPadView_innerRadius, mInnerRadius);
- mOuterRadius = a.getDimension(R.styleable.GlowPadView_outerRadius, mOuterRadius);
- mSnapMargin = a.getDimension(R.styleable.GlowPadView_snapMargin, mSnapMargin);
- mFirstItemOffset = (float) Math.toRadians(
- a.getFloat(R.styleable.GlowPadView_firstItemOffset,
- (float) Math.toDegrees(mFirstItemOffset)));
- mVibrationDuration = a.getInt(R.styleable.GlowPadView_vibrationDuration,
- mVibrationDuration);
- mFeedbackCount = a.getInt(R.styleable.GlowPadView_feedbackCount,
- mFeedbackCount);
- mAllowScaling = a.getBoolean(R.styleable.GlowPadView_allowScaling, false);
- TypedValue handle = a.peekValue(R.styleable.GlowPadView_handleDrawable);
- mHandleDrawable = new TargetDrawable(res, handle != null ? handle.resourceId : 0);
- mHandleDrawable.setState(TargetDrawable.STATE_INACTIVE);
- mOuterRing = new TargetDrawable(res,
- getResourceId(a, R.styleable.GlowPadView_outerRingDrawable));
-
- mAlwaysTrackFinger = a.getBoolean(R.styleable.GlowPadView_alwaysTrackFinger, false);
- mMagneticTargets = a.getBoolean(R.styleable.GlowPadView_magneticTargets, mMagneticTargets);
-
- int pointId = getResourceId(a, R.styleable.GlowPadView_pointDrawable);
- Drawable pointDrawable = pointId != 0 ? context.getDrawable(pointId) : null;
- mGlowRadius = a.getDimension(R.styleable.GlowPadView_glowRadius, 0.0f);
-
- mPointCloud = new PointCloud(pointDrawable);
- mPointCloud.makePointCloud(mInnerRadius, mOuterRadius);
- mPointCloud.glowManager.setRadius(mGlowRadius);
-
- TypedValue outValue = new TypedValue();
-
- // Read array of target drawables
- if (a.getValue(R.styleable.GlowPadView_targetDrawables, outValue)) {
- internalSetTargetResources(outValue.resourceId);
- }
- if (mTargetDrawables == null || mTargetDrawables.size() == 0) {
- throw new IllegalStateException("Must specify at least one target drawable");
- }
-
- // Read array of target descriptions
- if (a.getValue(R.styleable.GlowPadView_targetDescriptions, outValue)) {
- final int resourceId = outValue.resourceId;
- if (resourceId == 0) {
- throw new IllegalStateException("Must specify target descriptions");
- }
- setTargetDescriptionsResourceId(resourceId);
- }
-
- // Read array of direction descriptions
- if (a.getValue(R.styleable.GlowPadView_directionDescriptions, outValue)) {
- final int resourceId = outValue.resourceId;
- if (resourceId == 0) {
- throw new IllegalStateException("Must specify direction descriptions");
- }
- setDirectionDescriptionsResourceId(resourceId);
- }
-
- mGravity = a.getInt(R.styleable.GlowPadView_gravity, Gravity.TOP);
-
- a.recycle();
-
- setVibrateEnabled(mVibrationDuration > 0);
-
- assignDefaultsIfNeeded();
- }
-
- private int getResourceId(TypedArray a, int id) {
- TypedValue tv = a.peekValue(id);
- return tv == null ? 0 : tv.resourceId;
- }
-
- private void dump() {
- Log.v(TAG, "Outer Radius = " + mOuterRadius);
- Log.v(TAG, "SnapMargin = " + mSnapMargin);
- Log.v(TAG, "FeedbackCount = " + mFeedbackCount);
- Log.v(TAG, "VibrationDuration = " + mVibrationDuration);
- Log.v(TAG, "GlowRadius = " + mGlowRadius);
- Log.v(TAG, "WaveCenterX = " + mWaveCenterX);
- Log.v(TAG, "WaveCenterY = " + mWaveCenterY);
- }
-
- public void suspendAnimations() {
- mWaveAnimations.setSuspended(true);
- mTargetAnimations.setSuspended(true);
- mGlowAnimations.setSuspended(true);
- }
-
- public void resumeAnimations() {
- mWaveAnimations.setSuspended(false);
- mTargetAnimations.setSuspended(false);
- mGlowAnimations.setSuspended(false);
- mWaveAnimations.start();
- mTargetAnimations.start();
- mGlowAnimations.start();
- }
-
- @Override
- protected int getSuggestedMinimumWidth() {
- // View should be large enough to contain the background + handle and
- // target drawable on either edge.
- return (int) (Math.max(mOuterRing.getWidth(), 2 * mOuterRadius) + mMaxTargetWidth);
- }
-
- @Override
- protected int getSuggestedMinimumHeight() {
- // View should be large enough to contain the unlock ring + target and
- // target drawable on either edge
- return (int) (Math.max(mOuterRing.getHeight(), 2 * mOuterRadius) + mMaxTargetHeight);
- }
-
- /**
- * This gets the suggested width accounting for the ring's scale factor.
- */
- protected int getScaledSuggestedMinimumWidth() {
- return (int) (mRingScaleFactor * Math.max(mOuterRing.getWidth(), 2 * mOuterRadius)
- + mMaxTargetWidth);
- }
-
- /**
- * This gets the suggested height accounting for the ring's scale factor.
- */
- protected int getScaledSuggestedMinimumHeight() {
- return (int) (mRingScaleFactor * Math.max(mOuterRing.getHeight(), 2 * mOuterRadius)
- + mMaxTargetHeight);
- }
-
- private int resolveMeasured(int measureSpec, int desired)
- {
- int result = 0;
- int specSize = MeasureSpec.getSize(measureSpec);
- switch (MeasureSpec.getMode(measureSpec)) {
- case MeasureSpec.UNSPECIFIED:
- result = desired;
- break;
- case MeasureSpec.AT_MOST:
- result = Math.min(specSize, desired);
- break;
- case MeasureSpec.EXACTLY:
- default:
- result = specSize;
- }
- return result;
- }
-
- private void switchToState(int state, float x, float y) {
- switch (state) {
- case STATE_IDLE:
- deactivateTargets();
- hideGlow(0, 0, 0.0f, null);
- startBackgroundAnimation(0, 0.0f);
- mHandleDrawable.setState(TargetDrawable.STATE_INACTIVE);
- mHandleDrawable.setAlpha(1.0f);
- break;
-
- case STATE_START:
- startBackgroundAnimation(0, 0.0f);
- break;
-
- case STATE_FIRST_TOUCH:
- mHandleDrawable.setAlpha(0.0f);
- deactivateTargets();
- showTargets(true);
- startBackgroundAnimation(INITIAL_SHOW_HANDLE_DURATION, 1.0f);
- setGrabbedState(OnTriggerListener.CENTER_HANDLE);
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- announceTargets();
- }
- break;
-
- case STATE_TRACKING:
- mHandleDrawable.setAlpha(0.0f);
- showGlow(REVEAL_GLOW_DURATION , REVEAL_GLOW_DELAY, 1.0f, null);
- break;
-
- case STATE_SNAP:
- // TODO: Add transition states (see list_selector_background_transition.xml)
- mHandleDrawable.setAlpha(0.0f);
- showGlow(REVEAL_GLOW_DURATION , REVEAL_GLOW_DELAY, 0.0f, null);
- break;
-
- case STATE_FINISH:
- doFinish();
- break;
- }
- }
-
- private void showGlow(int duration, int delay, float finalAlpha,
- AnimatorListener finishListener) {
- mGlowAnimations.cancel();
- mGlowAnimations.add(Tweener.to(mPointCloud.glowManager, duration,
- "ease", Ease.Cubic.easeIn,
- "delay", delay,
- "alpha", finalAlpha,
- "onUpdate", mUpdateListener,
- "onComplete", finishListener));
- mGlowAnimations.start();
- }
-
- private void hideGlow(int duration, int delay, float finalAlpha,
- AnimatorListener finishListener) {
- mGlowAnimations.cancel();
- mGlowAnimations.add(Tweener.to(mPointCloud.glowManager, duration,
- "ease", Ease.Quart.easeOut,
- "delay", delay,
- "alpha", finalAlpha,
- "x", 0.0f,
- "y", 0.0f,
- "onUpdate", mUpdateListener,
- "onComplete", finishListener));
- mGlowAnimations.start();
- }
-
- private void deactivateTargets() {
- final int count = mTargetDrawables.size();
- for (int i = 0; i < count; i++) {
- TargetDrawable target = mTargetDrawables.get(i);
- target.setState(TargetDrawable.STATE_INACTIVE);
- }
- mActiveTarget = -1;
- }
-
- /**
- * Dispatches a trigger event to listener. Ignored if a listener is not set.
- * @param whichTarget the target that was triggered.
- */
- private void dispatchTriggerEvent(int whichTarget) {
- vibrate();
- if (mOnTriggerListener != null) {
- mOnTriggerListener.onTrigger(this, whichTarget);
- }
- }
-
- private void dispatchOnFinishFinalAnimation() {
- if (mOnTriggerListener != null) {
- mOnTriggerListener.onFinishFinalAnimation();
- }
- }
-
- private void doFinish() {
- final int activeTarget = mActiveTarget;
- final boolean targetHit = activeTarget != -1;
-
- if (targetHit) {
- if (DEBUG) Log.v(TAG, "Finish with target hit = " + targetHit);
-
- highlightSelected(activeTarget);
-
- // Inform listener of any active targets. Typically only one will be active.
- hideGlow(RETURN_TO_HOME_DURATION, RETURN_TO_HOME_DELAY, 0.0f, mResetListener);
- dispatchTriggerEvent(activeTarget);
- if (!mAlwaysTrackFinger) {
- // Force ring and targets to finish animation to final expanded state
- mTargetAnimations.stop();
- }
- } else {
- // Animate handle back to the center based on current state.
- hideGlow(HIDE_ANIMATION_DURATION, 0, 0.0f, mResetListenerWithPing);
- hideTargets(true, false);
- }
-
- setGrabbedState(OnTriggerListener.NO_HANDLE);
- }
-
- private void highlightSelected(int activeTarget) {
- // Highlight the given target and fade others
- mTargetDrawables.get(activeTarget).setState(TargetDrawable.STATE_ACTIVE);
- hideUnselected(activeTarget);
- }
-
- private void hideUnselected(int active) {
- for (int i = 0; i < mTargetDrawables.size(); i++) {
- if (i != active) {
- mTargetDrawables.get(i).setAlpha(0.0f);
- }
- }
- }
-
- private void hideTargets(boolean animate, boolean expanded) {
- mTargetAnimations.cancel();
- // Note: these animations should complete at the same time so that we can swap out
- // the target assets asynchronously from the setTargetResources() call.
- mAnimatingTargets = animate;
- final int duration = animate ? HIDE_ANIMATION_DURATION : 0;
- final int delay = animate ? HIDE_ANIMATION_DELAY : 0;
-
- final float targetScale = expanded ?
- TARGET_SCALE_EXPANDED : TARGET_SCALE_COLLAPSED;
- final int length = mTargetDrawables.size();
- final TimeInterpolator interpolator = Ease.Cubic.easeOut;
- for (int i = 0; i < length; i++) {
- TargetDrawable target = mTargetDrawables.get(i);
- target.setState(TargetDrawable.STATE_INACTIVE);
- mTargetAnimations.add(Tweener.to(target, duration,
- "ease", interpolator,
- "alpha", 0.0f,
- "scaleX", targetScale,
- "scaleY", targetScale,
- "delay", delay,
- "onUpdate", mUpdateListener));
- }
-
- float ringScaleTarget = expanded ?
- RING_SCALE_EXPANDED : RING_SCALE_COLLAPSED;
- ringScaleTarget *= mRingScaleFactor;
- mTargetAnimations.add(Tweener.to(mOuterRing, duration,
- "ease", interpolator,
- "alpha", 0.0f,
- "scaleX", ringScaleTarget,
- "scaleY", ringScaleTarget,
- "delay", delay,
- "onUpdate", mUpdateListener,
- "onComplete", mTargetUpdateListener));
-
- mTargetAnimations.start();
- }
-
- private void showTargets(boolean animate) {
- mTargetAnimations.stop();
- mAnimatingTargets = animate;
- final int delay = animate ? SHOW_ANIMATION_DELAY : 0;
- final int duration = animate ? SHOW_ANIMATION_DURATION : 0;
- final int length = mTargetDrawables.size();
- for (int i = 0; i < length; i++) {
- TargetDrawable target = mTargetDrawables.get(i);
- target.setState(TargetDrawable.STATE_INACTIVE);
- mTargetAnimations.add(Tweener.to(target, duration,
- "ease", Ease.Cubic.easeOut,
- "alpha", 1.0f,
- "scaleX", 1.0f,
- "scaleY", 1.0f,
- "delay", delay,
- "onUpdate", mUpdateListener));
- }
-
- float ringScale = mRingScaleFactor * RING_SCALE_EXPANDED;
- mTargetAnimations.add(Tweener.to(mOuterRing, duration,
- "ease", Ease.Cubic.easeOut,
- "alpha", 1.0f,
- "scaleX", ringScale,
- "scaleY", ringScale,
- "delay", delay,
- "onUpdate", mUpdateListener,
- "onComplete", mTargetUpdateListener));
-
- mTargetAnimations.start();
- }
-
- private void vibrate() {
- final boolean hapticEnabled = Settings.System.getIntForUser(
- mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 1,
- UserHandle.USER_CURRENT) != 0;
- if (mVibrator != null && hapticEnabled) {
- mVibrator.vibrate(mVibrationDuration, VIBRATION_ATTRIBUTES);
- }
- }
-
- private ArrayList<TargetDrawable> loadDrawableArray(int resourceId) {
- Resources res = getContext().getResources();
- TypedArray array = res.obtainTypedArray(resourceId);
- final int count = array.length();
- ArrayList<TargetDrawable> drawables = new ArrayList<TargetDrawable>(count);
- for (int i = 0; i < count; i++) {
- TypedValue value = array.peekValue(i);
- TargetDrawable target = new TargetDrawable(res, value != null ? value.resourceId : 0);
- drawables.add(target);
- }
- array.recycle();
- return drawables;
- }
-
- private void internalSetTargetResources(int resourceId) {
- final ArrayList<TargetDrawable> targets = loadDrawableArray(resourceId);
- mTargetDrawables = targets;
- mTargetResourceId = resourceId;
-
- int maxWidth = mHandleDrawable.getWidth();
- int maxHeight = mHandleDrawable.getHeight();
- final int count = targets.size();
- for (int i = 0; i < count; i++) {
- TargetDrawable target = targets.get(i);
- maxWidth = Math.max(maxWidth, target.getWidth());
- maxHeight = Math.max(maxHeight, target.getHeight());
- }
- if (mMaxTargetWidth != maxWidth || mMaxTargetHeight != maxHeight) {
- mMaxTargetWidth = maxWidth;
- mMaxTargetHeight = maxHeight;
- requestLayout(); // required to resize layout and call updateTargetPositions()
- } else {
- updateTargetPositions(mWaveCenterX, mWaveCenterY);
- updatePointCloudPosition(mWaveCenterX, mWaveCenterY);
- }
- }
-
- /**
- * Loads an array of drawables from the given resourceId.
- *
- * @param resourceId
- */
- public void setTargetResources(int resourceId) {
- if (mAnimatingTargets) {
- // postpone this change until we return to the initial state
- mNewTargetResources = resourceId;
- } else {
- internalSetTargetResources(resourceId);
- }
- }
-
- public int getTargetResourceId() {
- return mTargetResourceId;
- }
-
- /**
- * Sets the resource id specifying the target descriptions for accessibility.
- *
- * @param resourceId The resource id.
- */
- public void setTargetDescriptionsResourceId(int resourceId) {
- mTargetDescriptionsResourceId = resourceId;
- if (mTargetDescriptions != null) {
- mTargetDescriptions.clear();
- }
- }
-
- /**
- * Gets the resource id specifying the target descriptions for accessibility.
- *
- * @return The resource id.
- */
- public int getTargetDescriptionsResourceId() {
- return mTargetDescriptionsResourceId;
- }
-
- /**
- * Sets the resource id specifying the target direction descriptions for accessibility.
- *
- * @param resourceId The resource id.
- */
- public void setDirectionDescriptionsResourceId(int resourceId) {
- mDirectionDescriptionsResourceId = resourceId;
- if (mDirectionDescriptions != null) {
- mDirectionDescriptions.clear();
- }
- }
-
- /**
- * Gets the resource id specifying the target direction descriptions.
- *
- * @return The resource id.
- */
- public int getDirectionDescriptionsResourceId() {
- return mDirectionDescriptionsResourceId;
- }
-
- /**
- * Enable or disable vibrate on touch.
- *
- * @param enabled
- */
- public void setVibrateEnabled(boolean enabled) {
- if (enabled && mVibrator == null) {
- mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
- } else {
- mVibrator = null;
- }
- }
-
- /**
- * Starts wave animation.
- *
- */
- public void ping() {
- if (mFeedbackCount > 0) {
- boolean doWaveAnimation = true;
- final AnimationBundle waveAnimations = mWaveAnimations;
-
- // Don't do a wave if there's already one in progress
- if (waveAnimations.size() > 0 && waveAnimations.get(0).animator.isRunning()) {
- long t = waveAnimations.get(0).animator.getCurrentPlayTime();
- if (t < WAVE_ANIMATION_DURATION/2) {
- doWaveAnimation = false;
- }
- }
-
- if (doWaveAnimation) {
- startWaveAnimation();
- }
- }
- }
-
- private void stopAndHideWaveAnimation() {
- mWaveAnimations.cancel();
- mPointCloud.waveManager.setAlpha(0.0f);
- }
-
- private void startWaveAnimation() {
- mWaveAnimations.cancel();
- mPointCloud.waveManager.setAlpha(1.0f);
- mPointCloud.waveManager.setRadius(mHandleDrawable.getWidth()/2.0f);
- mWaveAnimations.add(Tweener.to(mPointCloud.waveManager, WAVE_ANIMATION_DURATION,
- "ease", Ease.Quad.easeOut,
- "delay", 0,
- "radius", 2.0f * mOuterRadius,
- "onUpdate", mUpdateListener,
- "onComplete",
- new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animator) {
- mPointCloud.waveManager.setRadius(0.0f);
- mPointCloud.waveManager.setAlpha(0.0f);
- }
- }));
- mWaveAnimations.start();
- }
-
- /**
- * Resets the widget to default state and cancels all animation. If animate is 'true', will
- * animate objects into place. Otherwise, objects will snap back to place.
- *
- * @param animate
- */
- public void reset(boolean animate) {
- mGlowAnimations.stop();
- mTargetAnimations.stop();
- startBackgroundAnimation(0, 0.0f);
- stopAndHideWaveAnimation();
- hideTargets(animate, false);
- hideGlow(0, 0, 0.0f, null);
- Tweener.reset();
- }
-
- private void startBackgroundAnimation(int duration, float alpha) {
- final Drawable background = getBackground();
- if (mAlwaysTrackFinger && background != null) {
- if (mBackgroundAnimator != null) {
- mBackgroundAnimator.animator.cancel();
- }
- mBackgroundAnimator = Tweener.to(background, duration,
- "ease", Ease.Cubic.easeIn,
- "alpha", (int)(255.0f * alpha),
- "delay", SHOW_ANIMATION_DELAY);
- mBackgroundAnimator.animator.start();
- }
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- final int action = event.getActionMasked();
- boolean handled = false;
- switch (action) {
- case MotionEvent.ACTION_POINTER_DOWN:
- case MotionEvent.ACTION_DOWN:
- if (DEBUG) Log.v(TAG, "*** DOWN ***");
- handleDown(event);
- handleMove(event);
- handled = true;
- break;
-
- case MotionEvent.ACTION_MOVE:
- if (DEBUG) Log.v(TAG, "*** MOVE ***");
- handleMove(event);
- handled = true;
- break;
-
- case MotionEvent.ACTION_POINTER_UP:
- case MotionEvent.ACTION_UP:
- if (DEBUG) Log.v(TAG, "*** UP ***");
- handleMove(event);
- handleUp(event);
- handled = true;
- break;
-
- case MotionEvent.ACTION_CANCEL:
- if (DEBUG) Log.v(TAG, "*** CANCEL ***");
- handleMove(event);
- handleCancel(event);
- handled = true;
- break;
-
- }
- invalidate();
- return handled ? true : super.onTouchEvent(event);
- }
-
- private void updateGlowPosition(float x, float y) {
- float dx = x - mOuterRing.getX();
- float dy = y - mOuterRing.getY();
- dx *= 1f / mRingScaleFactor;
- dy *= 1f / mRingScaleFactor;
- mPointCloud.glowManager.setX(mOuterRing.getX() + dx);
- mPointCloud.glowManager.setY(mOuterRing.getY() + dy);
- }
-
- private void handleDown(MotionEvent event) {
- int actionIndex = event.getActionIndex();
- float eventX = event.getX(actionIndex);
- float eventY = event.getY(actionIndex);
- switchToState(STATE_START, eventX, eventY);
- if (!trySwitchToFirstTouchState(eventX, eventY)) {
- mDragging = false;
- } else {
- mPointerId = event.getPointerId(actionIndex);
- updateGlowPosition(eventX, eventY);
- }
- }
-
- private void handleUp(MotionEvent event) {
- if (DEBUG && mDragging) Log.v(TAG, "** Handle RELEASE");
- int actionIndex = event.getActionIndex();
- if (event.getPointerId(actionIndex) == mPointerId) {
- switchToState(STATE_FINISH, event.getX(actionIndex), event.getY(actionIndex));
- }
- }
-
- private void handleCancel(MotionEvent event) {
- if (DEBUG && mDragging) Log.v(TAG, "** Handle CANCEL");
-
- // Drop the active target if canceled.
- mActiveTarget = -1;
-
- int actionIndex = event.findPointerIndex(mPointerId);
- actionIndex = actionIndex == -1 ? 0 : actionIndex;
- switchToState(STATE_FINISH, event.getX(actionIndex), event.getY(actionIndex));
- }
-
- private void handleMove(MotionEvent event) {
- int activeTarget = -1;
- final int historySize = event.getHistorySize();
- ArrayList<TargetDrawable> targets = mTargetDrawables;
- int ntargets = targets.size();
- float x = 0.0f;
- float y = 0.0f;
- float activeAngle = 0.0f;
- int actionIndex = event.findPointerIndex(mPointerId);
-
- if (actionIndex == -1) {
- return; // no data for this pointer
- }
-
- for (int k = 0; k < historySize + 1; k++) {
- float eventX = k < historySize ? event.getHistoricalX(actionIndex, k)
- : event.getX(actionIndex);
- float eventY = k < historySize ? event.getHistoricalY(actionIndex, k)
- : event.getY(actionIndex);
- // tx and ty are relative to wave center
- float tx = eventX - mWaveCenterX;
- float ty = eventY - mWaveCenterY;
- float touchRadius = (float) Math.hypot(tx, ty);
- final float scale = touchRadius > mOuterRadius ? mOuterRadius / touchRadius : 1.0f;
- float limitX = tx * scale;
- float limitY = ty * scale;
- double angleRad = Math.atan2(-ty, tx);
-
- if (!mDragging) {
- trySwitchToFirstTouchState(eventX, eventY);
- }
-
- if (mDragging) {
- // For multiple targets, snap to the one that matches
- final float snapRadius = mRingScaleFactor * mOuterRadius - mSnapMargin;
- final float snapDistance2 = snapRadius * snapRadius;
- // Find first target in range
- for (int i = 0; i < ntargets; i++) {
- TargetDrawable target = targets.get(i);
-
- double targetMinRad = mFirstItemOffset + (i - 0.5) * 2 * Math.PI / ntargets;
- double targetMaxRad = mFirstItemOffset + (i + 0.5) * 2 * Math.PI / ntargets;
- if (target.isEnabled()) {
- boolean angleMatches =
- (angleRad > targetMinRad && angleRad <= targetMaxRad) ||
- (angleRad + 2 * Math.PI > targetMinRad &&
- angleRad + 2 * Math.PI <= targetMaxRad) ||
- (angleRad - 2 * Math.PI > targetMinRad &&
- angleRad - 2 * Math.PI <= targetMaxRad);
- if (angleMatches && (dist2(tx, ty) > snapDistance2)) {
- activeTarget = i;
- activeAngle = (float) -angleRad;
- }
- }
- }
- }
- x = limitX;
- y = limitY;
- }
-
- if (!mDragging) {
- return;
- }
-
- if (activeTarget != -1) {
- switchToState(STATE_SNAP, x,y);
- updateGlowPosition(x, y);
- } else {
- switchToState(STATE_TRACKING, x, y);
- updateGlowPosition(x, y);
- }
-
- if (mActiveTarget != activeTarget) {
- // Defocus the old target
- if (mActiveTarget != -1) {
- TargetDrawable target = targets.get(mActiveTarget);
- if (target.hasState(TargetDrawable.STATE_FOCUSED)) {
- target.setState(TargetDrawable.STATE_INACTIVE);
- }
- if (mMagneticTargets) {
- updateTargetPosition(mActiveTarget, mWaveCenterX, mWaveCenterY);
- }
- }
- // Focus the new target
- if (activeTarget != -1) {
- TargetDrawable target = targets.get(activeTarget);
- if (target.hasState(TargetDrawable.STATE_FOCUSED)) {
- target.setState(TargetDrawable.STATE_FOCUSED);
- }
- if (mMagneticTargets) {
- updateTargetPosition(activeTarget, mWaveCenterX, mWaveCenterY, activeAngle);
- }
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- String targetContentDescription = getTargetDescription(activeTarget);
- announceForAccessibility(targetContentDescription);
- }
- }
- }
- mActiveTarget = activeTarget;
- }
-
- @Override
- public boolean onHoverEvent(MotionEvent event) {
- if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
- final int action = event.getAction();
- switch (action) {
- case MotionEvent.ACTION_HOVER_ENTER:
- event.setAction(MotionEvent.ACTION_DOWN);
- break;
- case MotionEvent.ACTION_HOVER_MOVE:
- event.setAction(MotionEvent.ACTION_MOVE);
- break;
- case MotionEvent.ACTION_HOVER_EXIT:
- event.setAction(MotionEvent.ACTION_UP);
- break;
- }
- onTouchEvent(event);
- event.setAction(action);
- }
- super.onHoverEvent(event);
- return true;
- }
-
- /**
- * Sets the current grabbed state, and dispatches a grabbed state change
- * event to our listener.
- */
- private void setGrabbedState(int newState) {
- if (newState != mGrabbedState) {
- if (newState != OnTriggerListener.NO_HANDLE) {
- vibrate();
- }
- mGrabbedState = newState;
- if (mOnTriggerListener != null) {
- if (newState == OnTriggerListener.NO_HANDLE) {
- mOnTriggerListener.onReleased(this, OnTriggerListener.CENTER_HANDLE);
- } else {
- mOnTriggerListener.onGrabbed(this, OnTriggerListener.CENTER_HANDLE);
- }
- mOnTriggerListener.onGrabbedStateChange(this, newState);
- }
- }
- }
-
- private boolean trySwitchToFirstTouchState(float x, float y) {
- final float tx = x - mWaveCenterX;
- final float ty = y - mWaveCenterY;
- if (mAlwaysTrackFinger || dist2(tx,ty) <= getScaledGlowRadiusSquared()) {
- if (DEBUG) Log.v(TAG, "** Handle HIT");
- switchToState(STATE_FIRST_TOUCH, x, y);
- updateGlowPosition(tx, ty);
- mDragging = true;
- return true;
- }
- return false;
- }
-
- private void assignDefaultsIfNeeded() {
- if (mOuterRadius == 0.0f) {
- mOuterRadius = Math.max(mOuterRing.getWidth(), mOuterRing.getHeight())/2.0f;
- }
- if (mSnapMargin == 0.0f) {
- mSnapMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- SNAP_MARGIN_DEFAULT, getContext().getResources().getDisplayMetrics());
- }
- if (mInnerRadius == 0.0f) {
- mInnerRadius = mHandleDrawable.getWidth() / 10.0f;
- }
- }
-
- private void computeInsets(int dx, int dy) {
- final int layoutDirection = getLayoutDirection();
- final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
-
- switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.LEFT:
- mHorizontalInset = 0;
- break;
- case Gravity.RIGHT:
- mHorizontalInset = dx;
- break;
- case Gravity.CENTER_HORIZONTAL:
- default:
- mHorizontalInset = dx / 2;
- break;
- }
- switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) {
- case Gravity.TOP:
- mVerticalInset = 0;
- break;
- case Gravity.BOTTOM:
- mVerticalInset = dy;
- break;
- case Gravity.CENTER_VERTICAL:
- default:
- mVerticalInset = dy / 2;
- break;
- }
- }
-
- /**
- * Given the desired width and height of the ring and the allocated width and height, compute
- * how much we need to scale the ring.
- */
- private float computeScaleFactor(int desiredWidth, int desiredHeight,
- int actualWidth, int actualHeight) {
-
- // Return unity if scaling is not allowed.
- if (!mAllowScaling) return 1f;
-
- final int layoutDirection = getLayoutDirection();
- final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
-
- float scaleX = 1f;
- float scaleY = 1f;
-
- // We use the gravity as a cue for whether we want to scale on a particular axis.
- // We only scale to fit horizontally if we're not pinned to the left or right. Likewise,
- // we only scale to fit vertically if we're not pinned to the top or bottom. In these
- // cases, we want the ring to hang off the side or top/bottom, respectively.
- switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
- case Gravity.LEFT:
- case Gravity.RIGHT:
- break;
- case Gravity.CENTER_HORIZONTAL:
- default:
- if (desiredWidth > actualWidth) {
- scaleX = (1f * actualWidth - mMaxTargetWidth) /
- (desiredWidth - mMaxTargetWidth);
- }
- break;
- }
- switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) {
- case Gravity.TOP:
- case Gravity.BOTTOM:
- break;
- case Gravity.CENTER_VERTICAL:
- default:
- if (desiredHeight > actualHeight) {
- scaleY = (1f * actualHeight - mMaxTargetHeight) /
- (desiredHeight - mMaxTargetHeight);
- }
- break;
- }
- return Math.min(scaleX, scaleY);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int minimumWidth = getSuggestedMinimumWidth();
- final int minimumHeight = getSuggestedMinimumHeight();
- int computedWidth = resolveMeasured(widthMeasureSpec, minimumWidth);
- int computedHeight = resolveMeasured(heightMeasureSpec, minimumHeight);
-
- mRingScaleFactor = computeScaleFactor(minimumWidth, minimumHeight,
- computedWidth, computedHeight);
-
- int scaledWidth = getScaledSuggestedMinimumWidth();
- int scaledHeight = getScaledSuggestedMinimumHeight();
-
- computeInsets(computedWidth - scaledWidth, computedHeight - scaledHeight);
- setMeasuredDimension(computedWidth, computedHeight);
- }
-
- private float getRingWidth() {
- return mRingScaleFactor * Math.max(mOuterRing.getWidth(), 2 * mOuterRadius);
- }
-
- private float getRingHeight() {
- return mRingScaleFactor * Math.max(mOuterRing.getHeight(), 2 * mOuterRadius);
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- final int width = right - left;
- final int height = bottom - top;
-
- // Target placement width/height. This puts the targets on the greater of the ring
- // width or the specified outer radius.
- final float placementWidth = getRingWidth();
- final float placementHeight = getRingHeight();
- float newWaveCenterX = mHorizontalInset
- + Math.max(width, mMaxTargetWidth + placementWidth) / 2;
- float newWaveCenterY = mVerticalInset
- + Math.max(height, + mMaxTargetHeight + placementHeight) / 2;
-
- if (mInitialLayout) {
- stopAndHideWaveAnimation();
- hideTargets(false, false);
- mInitialLayout = false;
- }
-
- mOuterRing.setPositionX(newWaveCenterX);
- mOuterRing.setPositionY(newWaveCenterY);
-
- mPointCloud.setScale(mRingScaleFactor);
-
- mHandleDrawable.setPositionX(newWaveCenterX);
- mHandleDrawable.setPositionY(newWaveCenterY);
-
- updateTargetPositions(newWaveCenterX, newWaveCenterY);
- updatePointCloudPosition(newWaveCenterX, newWaveCenterY);
- updateGlowPosition(newWaveCenterX, newWaveCenterY);
-
- mWaveCenterX = newWaveCenterX;
- mWaveCenterY = newWaveCenterY;
-
- if (DEBUG) dump();
- }
-
- private void updateTargetPosition(int i, float centerX, float centerY) {
- final float angle = getAngle(getSliceAngle(), i);
- updateTargetPosition(i, centerX, centerY, angle);
- }
-
- private void updateTargetPosition(int i, float centerX, float centerY, float angle) {
- final float placementRadiusX = getRingWidth() / 2;
- final float placementRadiusY = getRingHeight() / 2;
- if (i >= 0) {
- ArrayList<TargetDrawable> targets = mTargetDrawables;
- final TargetDrawable targetIcon = targets.get(i);
- targetIcon.setPositionX(centerX);
- targetIcon.setPositionY(centerY);
- targetIcon.setX(placementRadiusX * (float) Math.cos(angle));
- targetIcon.setY(placementRadiusY * (float) Math.sin(angle));
- }
- }
-
- private void updateTargetPositions(float centerX, float centerY) {
- updateTargetPositions(centerX, centerY, false);
- }
-
- private void updateTargetPositions(float centerX, float centerY, boolean skipActive) {
- final int size = mTargetDrawables.size();
- final float alpha = getSliceAngle();
- // Reposition the target drawables if the view changed.
- for (int i = 0; i < size; i++) {
- if (!skipActive || i != mActiveTarget) {
- updateTargetPosition(i, centerX, centerY, getAngle(alpha, i));
- }
- }
- }
-
- private float getAngle(float alpha, int i) {
- return mFirstItemOffset + alpha * i;
- }
-
- private float getSliceAngle() {
- return (float) (-2.0f * Math.PI / mTargetDrawables.size());
- }
-
- private void updatePointCloudPosition(float centerX, float centerY) {
- mPointCloud.setCenter(centerX, centerY);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- mPointCloud.draw(canvas);
- mOuterRing.draw(canvas);
- final int ntargets = mTargetDrawables.size();
- for (int i = 0; i < ntargets; i++) {
- TargetDrawable target = mTargetDrawables.get(i);
- if (target != null) {
- target.draw(canvas);
- }
- }
- mHandleDrawable.draw(canvas);
- }
-
- public void setOnTriggerListener(OnTriggerListener listener) {
- mOnTriggerListener = listener;
- }
-
- private float square(float d) {
- return d * d;
- }
-
- private float dist2(float dx, float dy) {
- return dx*dx + dy*dy;
- }
-
- private float getScaledGlowRadiusSquared() {
- final float scaledTapRadius;
- if (AccessibilityManager.getInstance(mContext).isEnabled()) {
- scaledTapRadius = TAP_RADIUS_SCALE_ACCESSIBILITY_ENABLED * mGlowRadius;
- } else {
- scaledTapRadius = mGlowRadius;
- }
- return square(scaledTapRadius);
- }
-
- private void announceTargets() {
- StringBuilder utterance = new StringBuilder();
- final int targetCount = mTargetDrawables.size();
- for (int i = 0; i < targetCount; i++) {
- String targetDescription = getTargetDescription(i);
- String directionDescription = getDirectionDescription(i);
- if (!TextUtils.isEmpty(targetDescription)
- && !TextUtils.isEmpty(directionDescription)) {
- String text = String.format(directionDescription, targetDescription);
- utterance.append(text);
- }
- }
- if (utterance.length() > 0) {
- announceForAccessibility(utterance.toString());
- }
- }
-
- private String getTargetDescription(int index) {
- if (mTargetDescriptions == null || mTargetDescriptions.isEmpty()) {
- mTargetDescriptions = loadDescriptions(mTargetDescriptionsResourceId);
- if (mTargetDrawables.size() != mTargetDescriptions.size()) {
- Log.w(TAG, "The number of target drawables must be"
- + " equal to the number of target descriptions.");
- return null;
- }
- }
- return mTargetDescriptions.get(index);
- }
-
- private String getDirectionDescription(int index) {
- if (mDirectionDescriptions == null || mDirectionDescriptions.isEmpty()) {
- mDirectionDescriptions = loadDescriptions(mDirectionDescriptionsResourceId);
- if (mTargetDrawables.size() != mDirectionDescriptions.size()) {
- Log.w(TAG, "The number of target drawables must be"
- + " equal to the number of direction descriptions.");
- return null;
- }
- }
- return mDirectionDescriptions.get(index);
- }
-
- private ArrayList<String> loadDescriptions(int resourceId) {
- TypedArray array = getContext().getResources().obtainTypedArray(resourceId);
- final int count = array.length();
- ArrayList<String> targetContentDescriptions = new ArrayList<String>(count);
- for (int i = 0; i < count; i++) {
- String contentDescription = array.getString(i);
- targetContentDescriptions.add(contentDescription);
- }
- array.recycle();
- return targetContentDescriptions;
- }
-
- public int getResourceIdForTarget(int index) {
- final TargetDrawable drawable = mTargetDrawables.get(index);
- return drawable == null ? 0 : drawable.getResourceId();
- }
-
- public void setEnableTarget(int resourceId, boolean enabled) {
- for (int i = 0; i < mTargetDrawables.size(); i++) {
- final TargetDrawable target = mTargetDrawables.get(i);
- if (target.getResourceId() == resourceId) {
- target.setEnabled(enabled);
- break; // should never be more than one match
- }
- }
- }
-
- /**
- * Gets the position of a target in the array that matches the given resource.
- * @param resourceId
- * @return the index or -1 if not found
- */
- public int getTargetPosition(int resourceId) {
- for (int i = 0; i < mTargetDrawables.size(); i++) {
- final TargetDrawable target = mTargetDrawables.get(i);
- if (target.getResourceId() == resourceId) {
- return i; // should never be more than one match
- }
- }
- return -1;
- }
-
- private boolean replaceTargetDrawables(Resources res, int existingResourceId,
- int newResourceId) {
- if (existingResourceId == 0 || newResourceId == 0) {
- return false;
- }
-
- boolean result = false;
- final ArrayList<TargetDrawable> drawables = mTargetDrawables;
- final int size = drawables.size();
- for (int i = 0; i < size; i++) {
- final TargetDrawable target = drawables.get(i);
- if (target != null && target.getResourceId() == existingResourceId) {
- target.setDrawable(res, newResourceId);
- result = true;
- }
- }
-
- if (result) {
- requestLayout(); // in case any given drawable's size changes
- }
-
- return result;
- }
-
- /**
- * Searches the given package for a resource to use to replace the Drawable on the
- * target with the given resource id
- * @param component of the .apk that contains the resource
- * @param name of the metadata in the .apk
- * @param existingResId the resource id of the target to search for
- * @return true if found in the given package and replaced at least one target Drawables
- */
- public boolean replaceTargetDrawablesIfPresent(ComponentName component, String name,
- int existingResId) {
- if (existingResId == 0) return false;
-
- boolean replaced = false;
- if (component != null) {
- try {
- PackageManager packageManager = mContext.getPackageManager();
- // Look for the search icon specified in the activity meta-data
- Bundle metaData = packageManager.getActivityInfo(
- component, PackageManager.GET_META_DATA).metaData;
- if (metaData != null) {
- int iconResId = metaData.getInt(name);
- if (iconResId != 0) {
- Resources res = packageManager.getResourcesForActivity(component);
- replaced = replaceTargetDrawables(res, existingResId, iconResId);
- }
- }
- } catch (NameNotFoundException e) {
- Log.w(TAG, "Failed to swap drawable; "
- + component.flattenToShortString() + " not found", e);
- } catch (Resources.NotFoundException nfe) {
- Log.w(TAG, "Failed to swap drawable from "
- + component.flattenToShortString(), nfe);
- }
- }
- if (!replaced) {
- // Restore the original drawable
- replaceTargetDrawables(mContext.getResources(), existingResId, existingResId);
- }
- return replaced;
- }
-}
diff --git a/core/java/com/android/internal/widget/multiwaveview/PointCloud.java b/core/java/com/android/internal/widget/multiwaveview/PointCloud.java
deleted file mode 100644
index 6f26b99..0000000
--- a/core/java/com/android/internal/widget/multiwaveview/PointCloud.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (C) 2012 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.internal.widget.multiwaveview;
-
-import java.util.ArrayList;
-
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.drawable.Drawable;
-import android.util.Log;
-
-public class PointCloud {
- private static final float MIN_POINT_SIZE = 2.0f;
- private static final float MAX_POINT_SIZE = 4.0f;
- private static final int INNER_POINTS = 8;
- private static final String TAG = "PointCloud";
- private ArrayList<Point> mPointCloud = new ArrayList<Point>();
- private Drawable mDrawable;
- private float mCenterX;
- private float mCenterY;
- private Paint mPaint;
- private float mScale = 1.0f;
- private static final float PI = (float) Math.PI;
-
- // These allow us to have multiple concurrent animations.
- WaveManager waveManager = new WaveManager();
- GlowManager glowManager = new GlowManager();
- private float mOuterRadius;
-
- public class WaveManager {
- private float radius = 50;
- private float alpha = 0.0f;
-
- public void setRadius(float r) {
- radius = r;
- }
-
- public float getRadius() {
- return radius;
- }
-
- public void setAlpha(float a) {
- alpha = a;
- }
-
- public float getAlpha() {
- return alpha;
- }
- };
-
- public class GlowManager {
- private float x;
- private float y;
- private float radius = 0.0f;
- private float alpha = 0.0f;
-
- public void setX(float x1) {
- x = x1;
- }
-
- public float getX() {
- return x;
- }
-
- public void setY(float y1) {
- y = y1;
- }
-
- public float getY() {
- return y;
- }
-
- public void setAlpha(float a) {
- alpha = a;
- }
-
- public float getAlpha() {
- return alpha;
- }
-
- public void setRadius(float r) {
- radius = r;
- }
-
- public float getRadius() {
- return radius;
- }
- }
-
- class Point {
- float x;
- float y;
- float radius;
-
- public Point(float x2, float y2, float r) {
- x = (float) x2;
- y = (float) y2;
- radius = r;
- }
- }
-
- public PointCloud(Drawable drawable) {
- mPaint = new Paint();
- mPaint.setFilterBitmap(true);
- mPaint.setColor(Color.rgb(255, 255, 255)); // TODO: make configurable
- mPaint.setAntiAlias(true);
- mPaint.setDither(true);
-
- mDrawable = drawable;
- if (mDrawable != null) {
- drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
- }
- }
-
- public void setCenter(float x, float y) {
- mCenterX = x;
- mCenterY = y;
- }
-
- public void makePointCloud(float innerRadius, float outerRadius) {
- if (innerRadius == 0) {
- Log.w(TAG, "Must specify an inner radius");
- return;
- }
- mOuterRadius = outerRadius;
- mPointCloud.clear();
- final float pointAreaRadius = (outerRadius - innerRadius);
- final float ds = (2.0f * PI * innerRadius / INNER_POINTS);
- final int bands = (int) Math.round(pointAreaRadius / ds);
- final float dr = pointAreaRadius / bands;
- float r = innerRadius;
- for (int b = 0; b <= bands; b++, r += dr) {
- float circumference = 2.0f * PI * r;
- final int pointsInBand = (int) (circumference / ds);
- float eta = PI/2.0f;
- float dEta = 2.0f * PI / pointsInBand;
- for (int i = 0; i < pointsInBand; i++) {
- float x = r * (float) Math.cos(eta);
- float y = r * (float) Math.sin(eta);
- eta += dEta;
- mPointCloud.add(new Point(x, y, r));
- }
- }
- }
-
- public void setScale(float scale) {
- mScale = scale;
- }
-
- public float getScale() {
- return mScale;
- }
-
- public int getAlphaForPoint(Point point) {
- // Contribution from positional glow
- float glowDistance = (float) Math.hypot(glowManager.x - point.x, glowManager.y - point.y);
- float glowAlpha = 0.0f;
- if (glowDistance < glowManager.radius) {
- float cosf = (float) Math.cos(PI * 0.25f * glowDistance / glowManager.radius);
- glowAlpha = glowManager.alpha * Math.max(0.0f, (float) Math.pow(cosf, 10.0f));
- }
-
- // Compute contribution from Wave
- float radius = (float) Math.hypot(point.x, point.y);
- float waveAlpha = 0.0f;
- if (radius < waveManager.radius * 2) {
- float distanceToWaveRing = (radius - waveManager.radius);
- float cosf = (float) Math.cos(PI * 0.5f * distanceToWaveRing / waveManager.radius);
- waveAlpha = waveManager.alpha * Math.max(0.0f, (float) Math.pow(cosf, 6.0f));
- }
- return (int) (Math.max(glowAlpha, waveAlpha) * 255);
- }
-
- private float interp(float min, float max, float f) {
- return min + (max - min) * f;
- }
-
- public void draw(Canvas canvas) {
- ArrayList<Point> points = mPointCloud;
- canvas.save(Canvas.MATRIX_SAVE_FLAG);
- canvas.scale(mScale, mScale, mCenterX, mCenterY);
- for (int i = 0; i < points.size(); i++) {
- Point point = points.get(i);
- final float pointSize = interp(MAX_POINT_SIZE, MIN_POINT_SIZE,
- point.radius / mOuterRadius);
- final float px = point.x + mCenterX;
- final float py = point.y + mCenterY;
- int alpha = getAlphaForPoint(point);
-
- if (alpha == 0) continue;
-
- if (mDrawable != null) {
- canvas.save(Canvas.MATRIX_SAVE_FLAG);
- final float cx = mDrawable.getIntrinsicWidth() * 0.5f;
- final float cy = mDrawable.getIntrinsicHeight() * 0.5f;
- final float s = pointSize / MAX_POINT_SIZE;
- canvas.scale(s, s, px, py);
- canvas.translate(px - cx, py - cy);
- mDrawable.setAlpha(alpha);
- mDrawable.draw(canvas);
- canvas.restore();
- } else {
- mPaint.setAlpha(alpha);
- canvas.drawCircle(px, py, pointSize, mPaint);
- }
- }
- canvas.restore();
- }
-
-}
diff --git a/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java b/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java
deleted file mode 100644
index 5a4c441..0000000
--- a/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.widget.multiwaveview;
-
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.StateListDrawable;
-import android.util.Log;
-
-public class TargetDrawable {
- private static final String TAG = "TargetDrawable";
- private static final boolean DEBUG = false;
-
- public static final int[] STATE_ACTIVE =
- { android.R.attr.state_enabled, android.R.attr.state_active };
- public static final int[] STATE_INACTIVE =
- { android.R.attr.state_enabled, -android.R.attr.state_active };
- public static final int[] STATE_FOCUSED =
- { android.R.attr.state_enabled, -android.R.attr.state_active,
- android.R.attr.state_focused };
-
- private float mTranslationX = 0.0f;
- private float mTranslationY = 0.0f;
- private float mPositionX = 0.0f;
- private float mPositionY = 0.0f;
- private float mScaleX = 1.0f;
- private float mScaleY = 1.0f;
- private float mAlpha = 1.0f;
- private Drawable mDrawable;
- private boolean mEnabled = true;
- private final int mResourceId;
-
- public TargetDrawable(Resources res, int resId) {
- mResourceId = resId;
- setDrawable(res, resId);
- }
-
- public void setDrawable(Resources res, int resId) {
- // Note we explicitly don't set mResourceId to resId since we allow the drawable to be
- // swapped at runtime and want to re-use the existing resource id for identification.
- Drawable drawable = resId == 0 ? null : res.getDrawable(resId);
- // Mutate the drawable so we can animate shared drawable properties.
- mDrawable = drawable != null ? drawable.mutate() : null;
- resizeDrawables();
- setState(STATE_INACTIVE);
- }
-
- public TargetDrawable(TargetDrawable other) {
- mResourceId = other.mResourceId;
- // Mutate the drawable so we can animate shared drawable properties.
- mDrawable = other.mDrawable != null ? other.mDrawable.mutate() : null;
- resizeDrawables();
- setState(STATE_INACTIVE);
- }
-
- public void setState(int [] state) {
- if (mDrawable instanceof StateListDrawable) {
- StateListDrawable d = (StateListDrawable) mDrawable;
- d.setState(state);
- }
- }
-
- public boolean hasState(int [] state) {
- if (mDrawable instanceof StateListDrawable) {
- StateListDrawable d = (StateListDrawable) mDrawable;
- // TODO: this doesn't seem to work
- return d.getStateDrawableIndex(state) != -1;
- }
- return false;
- }
-
- /**
- * Returns true if the drawable is a StateListDrawable and is in the focused state.
- *
- * @return
- */
- public boolean isActive() {
- if (mDrawable instanceof StateListDrawable) {
- StateListDrawable d = (StateListDrawable) mDrawable;
- int[] states = d.getState();
- for (int i = 0; i < states.length; i++) {
- if (states[i] == android.R.attr.state_focused) {
- return true;
- }
- }
- }
- return false;
- }
-
- /**
- * Returns true if this target is enabled. Typically an enabled target contains a valid
- * drawable in a valid state. Currently all targets with valid drawables are valid.
- *
- * @return
- */
- public boolean isEnabled() {
- return mDrawable != null && mEnabled;
- }
-
- /**
- * Makes drawables in a StateListDrawable all the same dimensions.
- * If not a StateListDrawable, then justs sets the bounds to the intrinsic size of the
- * drawable.
- */
- private void resizeDrawables() {
- if (mDrawable instanceof StateListDrawable) {
- StateListDrawable d = (StateListDrawable) mDrawable;
- int maxWidth = 0;
- int maxHeight = 0;
- for (int i = 0; i < d.getStateCount(); i++) {
- Drawable childDrawable = d.getStateDrawable(i);
- maxWidth = Math.max(maxWidth, childDrawable.getIntrinsicWidth());
- maxHeight = Math.max(maxHeight, childDrawable.getIntrinsicHeight());
- }
- if (DEBUG) Log.v(TAG, "union of childDrawable rects " + d + " to: "
- + maxWidth + "x" + maxHeight);
- d.setBounds(0, 0, maxWidth, maxHeight);
- for (int i = 0; i < d.getStateCount(); i++) {
- Drawable childDrawable = d.getStateDrawable(i);
- if (DEBUG) Log.v(TAG, "sizing drawable " + childDrawable + " to: "
- + maxWidth + "x" + maxHeight);
- childDrawable.setBounds(0, 0, maxWidth, maxHeight);
- }
- } else if (mDrawable != null) {
- mDrawable.setBounds(0, 0,
- mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight());
- }
- }
-
- public void setX(float x) {
- mTranslationX = x;
- }
-
- public void setY(float y) {
- mTranslationY = y;
- }
-
- public void setScaleX(float x) {
- mScaleX = x;
- }
-
- public void setScaleY(float y) {
- mScaleY = y;
- }
-
- public void setAlpha(float alpha) {
- mAlpha = alpha;
- }
-
- public float getX() {
- return mTranslationX;
- }
-
- public float getY() {
- return mTranslationY;
- }
-
- public float getScaleX() {
- return mScaleX;
- }
-
- public float getScaleY() {
- return mScaleY;
- }
-
- public float getAlpha() {
- return mAlpha;
- }
-
- public void setPositionX(float x) {
- mPositionX = x;
- }
-
- public void setPositionY(float y) {
- mPositionY = y;
- }
-
- public float getPositionX() {
- return mPositionX;
- }
-
- public float getPositionY() {
- return mPositionY;
- }
-
- public int getWidth() {
- return mDrawable != null ? mDrawable.getIntrinsicWidth() : 0;
- }
-
- public int getHeight() {
- return mDrawable != null ? mDrawable.getIntrinsicHeight() : 0;
- }
-
- public void draw(Canvas canvas) {
- if (mDrawable == null || !mEnabled) {
- return;
- }
- canvas.save(Canvas.MATRIX_SAVE_FLAG);
- canvas.scale(mScaleX, mScaleY, mPositionX, mPositionY);
- canvas.translate(mTranslationX + mPositionX, mTranslationY + mPositionY);
- canvas.translate(-0.5f * getWidth(), -0.5f * getHeight());
- mDrawable.setAlpha((int) Math.round(mAlpha * 255f));
- mDrawable.draw(canvas);
- canvas.restore();
- }
-
- public void setEnabled(boolean enabled) {
- mEnabled = enabled;
- }
-
- public int getResourceId() {
- return mResourceId;
- }
-}
diff --git a/core/java/com/android/internal/widget/multiwaveview/Tweener.java b/core/java/com/android/internal/widget/multiwaveview/Tweener.java
deleted file mode 100644
index d559d9d..0000000
--- a/core/java/com/android/internal/widget/multiwaveview/Tweener.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.widget.multiwaveview;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map.Entry;
-
-import android.animation.Animator.AnimatorListener;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.util.Log;
-
-class Tweener {
- private static final String TAG = "Tweener";
- private static final boolean DEBUG = false;
-
- ObjectAnimator animator;
- private static HashMap<Object, Tweener> sTweens = new HashMap<Object, Tweener>();
-
- public Tweener(ObjectAnimator anim) {
- animator = anim;
- }
-
- private static void remove(Animator animator) {
- Iterator<Entry<Object, Tweener>> iter = sTweens.entrySet().iterator();
- while (iter.hasNext()) {
- Entry<Object, Tweener> entry = iter.next();
- if (entry.getValue().animator == animator) {
- if (DEBUG) Log.v(TAG, "Removing tweener " + sTweens.get(entry.getKey())
- + " sTweens.size() = " + sTweens.size());
- iter.remove();
- break; // an animator can only be attached to one object
- }
- }
- }
-
- public static Tweener to(Object object, long duration, Object... vars) {
- long delay = 0;
- AnimatorUpdateListener updateListener = null;
- AnimatorListener listener = null;
- TimeInterpolator interpolator = null;
-
- // Iterate through arguments and discover properties to animate
- ArrayList<PropertyValuesHolder> props = new ArrayList<PropertyValuesHolder>(vars.length/2);
- for (int i = 0; i < vars.length; i+=2) {
- if (!(vars[i] instanceof String)) {
- throw new IllegalArgumentException("Key must be a string: " + vars[i]);
- }
- String key = (String) vars[i];
- Object value = vars[i+1];
- if ("simultaneousTween".equals(key)) {
- // TODO
- } else if ("ease".equals(key)) {
- interpolator = (TimeInterpolator) value; // TODO: multiple interpolators?
- } else if ("onUpdate".equals(key) || "onUpdateListener".equals(key)) {
- updateListener = (AnimatorUpdateListener) value;
- } else if ("onComplete".equals(key) || "onCompleteListener".equals(key)) {
- listener = (AnimatorListener) value;
- } else if ("delay".equals(key)) {
- delay = ((Number) value).longValue();
- } else if ("syncWith".equals(key)) {
- // TODO
- } else if (value instanceof float[]) {
- props.add(PropertyValuesHolder.ofFloat(key,
- ((float[])value)[0], ((float[])value)[1]));
- } else if (value instanceof int[]) {
- props.add(PropertyValuesHolder.ofInt(key,
- ((int[])value)[0], ((int[])value)[1]));
- } else if (value instanceof Number) {
- float floatValue = ((Number)value).floatValue();
- props.add(PropertyValuesHolder.ofFloat(key, floatValue));
- } else {
- throw new IllegalArgumentException(
- "Bad argument for key \"" + key + "\" with value " + value.getClass());
- }
- }
-
- // Re-use existing tween, if present
- Tweener tween = sTweens.get(object);
- ObjectAnimator anim = null;
- if (tween == null) {
- anim = ObjectAnimator.ofPropertyValuesHolder(object,
- props.toArray(new PropertyValuesHolder[props.size()]));
- tween = new Tweener(anim);
- sTweens.put(object, tween);
- if (DEBUG) Log.v(TAG, "Added new Tweener " + tween);
- } else {
- anim = sTweens.get(object).animator;
- replace(props, object); // Cancel all animators for given object
- }
-
- if (interpolator != null) {
- anim.setInterpolator(interpolator);
- }
-
- // Update animation with properties discovered in loop above
- anim.setStartDelay(delay);
- anim.setDuration(duration);
- if (updateListener != null) {
- anim.removeAllUpdateListeners(); // There should be only one
- anim.addUpdateListener(updateListener);
- }
- if (listener != null) {
- anim.removeAllListeners(); // There should be only one.
- anim.addListener(listener);
- }
- anim.addListener(mCleanupListener);
-
- return tween;
- }
-
- Tweener from(Object object, long duration, Object... vars) {
- // TODO: for v of vars
- // toVars[v] = object[v]
- // object[v] = vars[v]
- return Tweener.to(object, duration, vars);
- }
-
- // Listener to watch for completed animations and remove them.
- private static AnimatorListener mCleanupListener = new AnimatorListenerAdapter() {
-
- @Override
- public void onAnimationEnd(Animator animation) {
- remove(animation);
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- remove(animation);
- }
- };
-
- public static void reset() {
- if (DEBUG) {
- Log.v(TAG, "Reset()");
- if (sTweens.size() > 0) {
- Log.v(TAG, "Cleaning up " + sTweens.size() + " animations");
- }
- }
- sTweens.clear();
- }
-
- private static void replace(ArrayList<PropertyValuesHolder> props, Object... args) {
- for (final Object killobject : args) {
- Tweener tween = sTweens.get(killobject);
- if (tween != null) {
- tween.animator.cancel();
- if (props != null) {
- tween.animator.setValues(
- props.toArray(new PropertyValuesHolder[props.size()]));
- } else {
- sTweens.remove(tween);
- }
- }
- }
- }
-}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index cb9b421..1dd10e1 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -87,6 +87,7 @@
android_graphics_Canvas.cpp \
android_graphics_Picture.cpp \
android/graphics/AutoDecodeCancel.cpp \
+ android/graphics/AvoidXfermode.cpp \
android/graphics/Bitmap.cpp \
android/graphics/BitmapFactory.cpp \
android/graphics/Camera.cpp \
diff --git a/core/jni/android/graphics/AvoidXfermode.cpp b/core/jni/android/graphics/AvoidXfermode.cpp
new file mode 100644
index 0000000..9ca1f26
--- /dev/null
+++ b/core/jni/android/graphics/AvoidXfermode.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "AvoidXfermode.h"
+#include "SkColorPriv.h"
+#include "SkReadBuffer.h"
+#include "SkWriteBuffer.h"
+#include "SkString.h"
+
+AvoidXfermode::AvoidXfermode(SkColor opColor, U8CPU tolerance, Mode mode) {
+ if (tolerance > 255) {
+ tolerance = 255;
+ }
+ fTolerance = SkToU8(tolerance);
+ fOpColor = opColor;
+ fDistMul = (256 << 14) / (tolerance + 1);
+ fMode = mode;
+}
+
+SkFlattenable* AvoidXfermode::CreateProc(SkReadBuffer& buffer) {
+ const SkColor color = buffer.readColor();
+ const unsigned tolerance = buffer.readUInt();
+ const unsigned mode = buffer.readUInt();
+ return Create(color, tolerance, (Mode)mode);
+}
+
+void AvoidXfermode::flatten(SkWriteBuffer& buffer) const {
+ buffer.writeColor(fOpColor);
+ buffer.writeUInt(fTolerance);
+ buffer.writeUInt(fMode);
+}
+
+// returns 0..31
+static unsigned color_dist16(uint16_t c, unsigned r, unsigned g, unsigned b) {
+ SkASSERT(r <= SK_R16_MASK);
+ SkASSERT(g <= SK_G16_MASK);
+ SkASSERT(b <= SK_B16_MASK);
+
+ unsigned dr = SkAbs32(SkGetPackedR16(c) - r);
+ unsigned dg = SkAbs32(SkGetPackedG16(c) - g) >> (SK_G16_BITS - SK_R16_BITS);
+ unsigned db = SkAbs32(SkGetPackedB16(c) - b);
+
+ return SkMax32(dr, SkMax32(dg, db));
+}
+
+// returns 0..255
+static unsigned color_dist32(SkPMColor c, U8CPU r, U8CPU g, U8CPU b) {
+ SkASSERT(r <= 0xFF);
+ SkASSERT(g <= 0xFF);
+ SkASSERT(b <= 0xFF);
+
+ unsigned dr = SkAbs32(SkGetPackedR32(c) - r);
+ unsigned dg = SkAbs32(SkGetPackedG32(c) - g);
+ unsigned db = SkAbs32(SkGetPackedB32(c) - b);
+
+ return SkMax32(dr, SkMax32(dg, db));
+}
+
+static int scale_dist_14(int dist, uint32_t mul, uint32_t sub) {
+ int tmp = dist * mul - sub;
+ int result = (tmp + (1 << 13)) >> 14;
+
+ return result;
+}
+
+static inline unsigned Accurate255To256(unsigned x) {
+ return x + (x >> 7);
+}
+
+void AvoidXfermode::xfer32(SkPMColor dst[], const SkPMColor src[], int count,
+ const SkAlpha aa[]) const {
+ unsigned opR = SkColorGetR(fOpColor);
+ unsigned opG = SkColorGetG(fOpColor);
+ unsigned opB = SkColorGetB(fOpColor);
+ uint32_t mul = fDistMul;
+ uint32_t sub = (fDistMul - (1 << 14)) << 8;
+
+ int MAX, mask;
+
+ if (kTargetColor_Mode == fMode) {
+ mask = -1;
+ MAX = 255;
+ } else {
+ mask = 0;
+ MAX = 0;
+ }
+
+ for (int i = 0; i < count; i++) {
+ int d = color_dist32(dst[i], opR, opG, opB);
+ // now reverse d if we need to
+ d = MAX + (d ^ mask) - mask;
+ SkASSERT((unsigned)d <= 255);
+ d = Accurate255To256(d);
+
+ d = scale_dist_14(d, mul, sub);
+ SkASSERT(d <= 256);
+
+ if (d > 0) {
+ if (aa) {
+ d = SkAlphaMul(d, Accurate255To256(*aa++));
+ if (0 == d) {
+ continue;
+ }
+ }
+ dst[i] = SkFourByteInterp256(src[i], dst[i], d);
+ }
+ }
+}
+
+static inline U16CPU SkBlend3216(SkPMColor src, U16CPU dst, unsigned scale) {
+ SkASSERT(scale <= 32);
+ scale <<= 3;
+
+ return SkPackRGB16( SkAlphaBlend(SkPacked32ToR16(src), SkGetPackedR16(dst), scale),
+ SkAlphaBlend(SkPacked32ToG16(src), SkGetPackedG16(dst), scale),
+ SkAlphaBlend(SkPacked32ToB16(src), SkGetPackedB16(dst), scale));
+}
+
+void AvoidXfermode::xfer16(uint16_t dst[], const SkPMColor src[], int count,
+ const SkAlpha aa[]) const {
+ unsigned opR = SkColorGetR(fOpColor) >> (8 - SK_R16_BITS);
+ unsigned opG = SkColorGetG(fOpColor) >> (8 - SK_G16_BITS);
+ unsigned opB = SkColorGetB(fOpColor) >> (8 - SK_R16_BITS);
+ uint32_t mul = fDistMul;
+ uint32_t sub = (fDistMul - (1 << 14)) << SK_R16_BITS;
+
+ int MAX, mask;
+
+ if (kTargetColor_Mode == fMode) {
+ mask = -1;
+ MAX = 31;
+ } else {
+ mask = 0;
+ MAX = 0;
+ }
+
+ for (int i = 0; i < count; i++) {
+ int d = color_dist16(dst[i], opR, opG, opB);
+ // now reverse d if we need to
+ d = MAX + (d ^ mask) - mask;
+ SkASSERT((unsigned)d <= 31);
+ // convert from 0..31 to 0..32
+ d += d >> 4;
+ d = scale_dist_14(d, mul, sub);
+ SkASSERT(d <= 32);
+
+ if (d > 0) {
+ if (aa) {
+ d = SkAlphaMul(d, Accurate255To256(*aa++));
+ if (0 == d) {
+ continue;
+ }
+ }
+ dst[i] = SkBlend3216(src[i], dst[i], d);
+ }
+ }
+}
+
+void AvoidXfermode::xferA8(SkAlpha dst[], const SkPMColor src[], int count,
+ const SkAlpha aa[]) const {
+}
+
+#ifndef SK_IGNORE_TO_STRING
+void AvoidXfermode::toString(SkString* str) const {
+ str->append("AvoidXfermode: opColor: ");
+ str->appendHex(fOpColor);
+ str->appendf("distMul: %d ", fDistMul);
+
+ static const char* gModeStrings[] = { "Avoid", "Target" };
+
+ str->appendf("mode: %s", gModeStrings[fMode]);
+}
+#endif
diff --git a/core/jni/android/graphics/AvoidXfermode.h b/core/jni/android/graphics/AvoidXfermode.h
new file mode 100644
index 0000000..318d7be
--- /dev/null
+++ b/core/jni/android/graphics/AvoidXfermode.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef AvoidXfermode_DEFINED
+#define AvoidXfermode_DEFINED
+
+#include "SkColor.h"
+#include "SkTypes.h"
+#include "SkXfermode.h"
+
+/** \class AvoidXfermode
+
+ This xfermode will draw the src everywhere except on top of the specified
+ color.
+*/
+class AvoidXfermode : public SkXfermode {
+public:
+ enum Mode {
+ kAvoidColor_Mode, //!< draw everywhere except on the opColor
+ kTargetColor_Mode //!< draw only on top of the opColor
+ };
+
+ /** This xfermode draws, or doesn't draw, based on the destination's
+ distance from an op-color.
+
+ There are two modes, and each mode interprets a tolerance value.
+
+ Avoid: In this mode, drawing is allowed only on destination pixels that
+ are different from the op-color.
+ Tolerance near 0: avoid any colors even remotely similar to the op-color
+ Tolerance near 255: avoid only colors nearly identical to the op-color
+
+ Target: In this mode, drawing only occurs on destination pixels that
+ are similar to the op-color
+ Tolerance near 0: draw only on colors that are nearly identical to the op-color
+ Tolerance near 255: draw on any colors even remotely similar to the op-color
+ */
+ static AvoidXfermode* Create(SkColor opColor, U8CPU tolerance, Mode mode) {
+ return SkNEW_ARGS(AvoidXfermode, (opColor, tolerance, mode));
+ }
+
+ // overrides from SkXfermode
+ void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
+ const SkAlpha aa[]) const override;
+ void xfer16(uint16_t dst[], const SkPMColor src[], int count,
+ const SkAlpha aa[]) const override;
+ void xferA8(SkAlpha dst[], const SkPMColor src[], int count,
+ const SkAlpha aa[]) const override;
+
+ SK_TO_STRING_OVERRIDE()
+ SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(AvoidXfermode)
+
+protected:
+ AvoidXfermode(SkColor opColor, U8CPU tolerance, Mode mode);
+ void flatten(SkWriteBuffer&) const override;
+
+private:
+ SkColor fOpColor;
+ uint32_t fDistMul; // x.14 cached from fTolerance
+ uint8_t fTolerance;
+ Mode fMode;
+
+ typedef SkXfermode INHERITED;
+};
+
+#endif
diff --git a/core/jni/android/graphics/Camera.cpp b/core/jni/android/graphics/Camera.cpp
index b572604..036ece1 100644
--- a/core/jni/android/graphics/Camera.cpp
+++ b/core/jni/android/graphics/Camera.cpp
@@ -96,7 +96,7 @@
}
static void Camera_applyToCanvas(JNIEnv* env, jobject obj, jlong canvasHandle) {
- SkCanvas* canvas = reinterpret_cast<android::Canvas*>(canvasHandle)->getSkCanvas();
+ SkCanvas* canvas = reinterpret_cast<android::Canvas*>(canvasHandle)->asSkCanvas();
jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
v->applyToCanvas(canvas);
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 9996ce1..dde1393 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -366,7 +366,7 @@
if (!canvasHandle) {
return NULL;
}
- SkCanvas* c = reinterpret_cast<android::Canvas*>(canvasHandle)->getSkCanvas();
+ SkCanvas* c = reinterpret_cast<android::Canvas*>(canvasHandle)->asSkCanvas();
SkASSERT(c);
return c;
}
@@ -511,18 +511,15 @@
///////////////////////////////////////////////////////////////////////////////
-static bool computeAllocationSize(const SkImageInfo& info, size_t* size, size_t* rowBytes) {
- int32_t rowBytes32 = SkToS32(info.minRowBytes());
- int64_t bigSize = (int64_t)info.height() * rowBytes32;
- if (rowBytes32 < 0 || !sk_64_isS32(bigSize)) {
- return false; // allocation will be too large
- }
+static bool computeAllocationSize(const SkBitmap& bitmap, size_t* size) {
+ int32_t rowBytes32 = SkToS32(bitmap.rowBytes());
+ int64_t bigSize = (int64_t)bitmap.height() * rowBytes32;
+ if (rowBytes32 < 0 || !sk_64_isS32(bigSize)) {
+ return false; // allocation will be too large
+ }
- *size = sk_64_asS32(bigSize);
- *rowBytes = rowBytes32;
-
- SkASSERT(*size >= info.getSafeSize(*rowBytes));
- return true;
+ *size = sk_64_asS32(bigSize);
+ return true;
}
jbyteArray GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
@@ -533,11 +530,15 @@
return NULL;
}
- size_t size, rowBytes;
- if (!computeAllocationSize(info, &size, &rowBytes)) {
+ size_t size;
+ if (!computeAllocationSize(*bitmap, &size)) {
return NULL;
}
+ // we must respect the rowBytes value already set on the bitmap instead of
+ // attempting to compute our own.
+ const size_t rowBytes = bitmap->rowBytes();
+
jbyteArray arrayObj = (jbyteArray) env->CallObjectMethod(gVMRuntime,
gVMRuntime_newNonMovableArray,
gByte_class, size);
@@ -580,11 +581,15 @@
return NULL;
}
- size_t size, rowBytes;
- if (!computeAllocationSize(info, &size, &rowBytes)) {
+ size_t size;
+ if (!computeAllocationSize(*bitmap, &size)) {
return false;
}
+ // we must respect the rowBytes value already set on the bitmap instead of
+ // attempting to compute our own.
+ const size_t rowBytes = bitmap->rowBytes();
+
void* addr = sk_malloc_flags(size, 0);
if (NULL == addr) {
return false;
diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp
index 543323c..3f8bfe2 100644
--- a/core/jni/android/graphics/NinePatch.cpp
+++ b/core/jni/android/graphics/NinePatch.cpp
@@ -120,7 +120,7 @@
static void drawF(JNIEnv* env, jobject, jlong canvasHandle, jobject boundsRectF,
jlong bitmapHandle, jlong chunkHandle, jlong paintHandle,
jint destDensity, jint srcDensity) {
- SkCanvas* canvas = reinterpret_cast<Canvas*>(canvasHandle)->getSkCanvas();
+ SkCanvas* canvas = reinterpret_cast<Canvas*>(canvasHandle)->asSkCanvas();
const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
Res_png_9patch* chunk = reinterpret_cast<Res_png_9patch*>(chunkHandle);
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
@@ -139,7 +139,7 @@
static void drawI(JNIEnv* env, jobject, jlong canvasHandle, jobject boundsRect,
jlong bitmapHandle, jlong chunkHandle, jlong paintHandle,
jint destDensity, jint srcDensity) {
- SkCanvas* canvas = reinterpret_cast<Canvas*>(canvasHandle)->getSkCanvas();
+ SkCanvas* canvas = reinterpret_cast<Canvas*>(canvasHandle)->asSkCanvas();
const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
Res_png_9patch* chunk = reinterpret_cast<Res_png_9patch*>(chunkHandle);
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
diff --git a/core/jni/android/graphics/NinePatchPeeker.cpp b/core/jni/android/graphics/NinePatchPeeker.cpp
index 2644888..6c32a21 100644
--- a/core/jni/android/graphics/NinePatchPeeker.cpp
+++ b/core/jni/android/graphics/NinePatchPeeker.cpp
@@ -24,7 +24,9 @@
if (!strcmp("npTc", tag) && length >= sizeof(Res_png_9patch)) {
Res_png_9patch* patch = (Res_png_9patch*) data;
size_t patchSize = patch->serializedSize();
- assert(length == patchSize);
+ if (length != patchSize) {
+ return false;
+ }
// You have to copy the data because it is owned by the png reader
Res_png_9patch* patchNew = (Res_png_9patch*) malloc(patchSize);
memcpy(patchNew, patch, patchSize);
diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp
index c249012..12bfaa2 100644
--- a/core/jni/android/graphics/Picture.cpp
+++ b/core/jni/android/graphics/Picture.cpp
@@ -101,7 +101,7 @@
}
validate();
if (NULL != mPicture.get()) {
- mPicture.get()->playback(canvas->getSkCanvas());
+ mPicture.get()->playback(canvas->asSkCanvas());
}
}
diff --git a/core/jni/android/graphics/Xfermode.cpp b/core/jni/android/graphics/Xfermode.cpp
index 5a3883a..4a424ae 100644
--- a/core/jni/android/graphics/Xfermode.cpp
+++ b/core/jni/android/graphics/Xfermode.cpp
@@ -18,7 +18,7 @@
#include "GraphicsJNI.h"
#include "core_jni_helpers.h"
-#include "SkAvoidXfermode.h"
+#include "AvoidXfermode.h"
#include "SkPixelXorXfermode.h"
namespace android {
@@ -35,8 +35,8 @@
static jlong avoid_create(JNIEnv* env, jobject, jint opColor,
jint tolerance, jint modeHandle)
{
- SkAvoidXfermode::Mode mode = static_cast<SkAvoidXfermode::Mode>(modeHandle);
- return reinterpret_cast<jlong>(SkAvoidXfermode::Create(opColor, tolerance, mode));
+ AvoidXfermode::Mode mode = static_cast<AvoidXfermode::Mode>(modeHandle);
+ return reinterpret_cast<jlong>(AvoidXfermode::Create(opColor, tolerance, mode));
}
static jlong pixelxor_create(JNIEnv* env, jobject, jint opColor)
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 085cc81..6094fd1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -183,8 +183,6 @@
<protected-broadcast android:name="android.intent.action.HEADSET_PLUG" />
<protected-broadcast android:name="android.media.action.HDMI_AUDIO_PLUG" />
- <protected-broadcast android:name="android.media.action.USB_AUDIO_ACCESSORY_PLUG" />
- <protected-broadcast android:name="android.media.action.USB_AUDIO_DEVICE_PLUG" />
<protected-broadcast android:name="android.media.AUDIO_BECOMING_NOISY" />
<protected-broadcast android:name="android.media.RINGER_MODE_CHANGED" />
diff --git a/core/res/res/layout/dialog_custom_title_material.xml b/core/res/res/layout/dialog_custom_title_material.xml
index 248a05e..50ed910 100644
--- a/core/res/res/layout/dialog_custom_title_material.xml
+++ b/core/res/res/layout/dialog_custom_title_material.xml
@@ -27,6 +27,8 @@
android:layout_weight="0"
android:gravity="center_vertical|start"
style="?attr/windowTitleBackgroundStyle" />
+ <View android:layout_width="match_parent"
+ android:layout_height="0dp" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/core/res/res/layout/dialog_title_icons_material.xml b/core/res/res/layout/dialog_title_icons_material.xml
index 62af096..3866ca7 100644
--- a/core/res/res/layout/dialog_title_icons_material.xml
+++ b/core/res/res/layout/dialog_title_icons_material.xml
@@ -48,6 +48,9 @@
android:layout_marginStart="8dip" />
</LinearLayout>
+ <View android:layout_width="match_parent"
+ android:layout_height="0dp" />
+
<FrameLayout
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_weight="1"
diff --git a/core/res/res/layout/dialog_title_material.xml b/core/res/res/layout/dialog_title_material.xml
index 339d569..1ea7f6e 100644
--- a/core/res/res/layout/dialog_title_material.xml
+++ b/core/res/res/layout/dialog_title_material.xml
@@ -32,6 +32,8 @@
android:paddingStart="?attr/dialogPreferredPadding"
android:paddingEnd="?attr/dialogPreferredPadding"
android:paddingTop="@dimen/dialog_padding_top_material" />
+ <View android:layout_width="match_parent"
+ android:layout_height="0dp" />
<FrameLayout
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_weight="1"
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index 3fdcaf7..ea22b15 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -16,12 +16,9 @@
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:internal="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/status_bar_latest_event_content"
android:layout_width="match_parent"
android:layout_height="64dp"
- internal:layout_minHeight="64dp"
- internal:layout_maxHeight="64dp"
>
<include layout="@layout/notification_template_icon_group"
android:layout_width="@dimen/notification_large_icon_width"
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 935424a..2a3ee90 100644
--- a/core/res/res/layout/notification_template_material_big_base.xml
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -16,12 +16,9 @@
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:internal="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/status_bar_latest_event_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- internal:layout_minHeight="65dp"
- internal:layout_maxHeight="unbounded"
>
<include layout="@layout/notification_template_icon_group"
android:layout_width="@dimen/notification_large_icon_width"
diff --git a/core/res/res/layout/notification_template_material_big_picture.xml b/core/res/res/layout/notification_template_material_big_picture.xml
index 302e651..f1a9549 100644
--- a/core/res/res/layout/notification_template_material_big_picture.xml
+++ b/core/res/res/layout/notification_template_material_big_picture.xml
@@ -16,12 +16,9 @@
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:internal="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/status_bar_latest_event_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
- internal:layout_minHeight="65dp"
- internal:layout_maxHeight="unbounded"
>
<ImageView
android:id="@+id/big_picture"
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 d0c10b2..f657f04 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -16,12 +16,9 @@
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:internal="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/status_bar_latest_event_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- internal:layout_minHeight="65dp"
- internal:layout_maxHeight="unbounded"
>
<include layout="@layout/notification_template_icon_group"
android:layout_width="@dimen/notification_large_icon_width"
diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml
index ac448ee..d292d4e 100644
--- a/core/res/res/layout/notification_template_material_inbox.xml
+++ b/core/res/res/layout/notification_template_material_inbox.xml
@@ -16,12 +16,9 @@
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:internal="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/status_bar_latest_event_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- internal:layout_minHeight="65dp"
- internal:layout_maxHeight="unbounded"
>
<include layout="@layout/notification_template_icon_group"
android:layout_width="@dimen/notification_large_icon_width"
diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml
index 69020a4..0292d28 100644
--- a/core/res/res/layout/notification_template_material_media.xml
+++ b/core/res/res/layout/notification_template_material_media.xml
@@ -16,14 +16,11 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:internal="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/status_bar_latest_event_content"
android:layout_width="match_parent"
android:layout_height="64dp"
android:orientation="horizontal"
android:background="#00000000"
- internal:layout_minHeight="64dp"
- internal:layout_maxHeight="64dp"
>
<include layout="@layout/notification_template_icon_group"
android:layout_width="@dimen/notification_large_icon_width"
diff --git a/core/res/res/transition/popup_window_enter.xml b/core/res/res/transition/popup_window_enter.xml
new file mode 100644
index 0000000..92d4c1e
--- /dev/null
+++ b/core/res/res/transition/popup_window_enter.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
+ android:transitionOrdering="together">
+ <transition class="com.android.internal.transition.EpicenterClipReveal"
+ android:interpolator="@android:interpolator/accelerate_cubic"
+ android:startDelay="50"
+ android:duration="300" />
+ <fade
+ android:interpolator="@android:interpolator/linear"
+ android:duration="100" />
+</transitionSet>
diff --git a/core/res/res/transition/popup_window_exit.xml b/core/res/res/transition/popup_window_exit.xml
new file mode 100644
index 0000000..5cb9f0b
--- /dev/null
+++ b/core/res/res/transition/popup_window_exit.xml
@@ -0,0 +1,17 @@
+<?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.
+-->
+<fade xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:interpolator/linear" />
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
new file mode 100644
index 0000000..3435474
--- /dev/null
+++ b/core/res/res/values-television/config.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for TV products. Do not translate. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Flags enabling default window features. See Window.java -->
+ <bool name="config_defaultWindowFeatureOptionsPanel">false</bool>
+</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 551c044..559d750 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4338,6 +4338,10 @@
<attr name="popupAnimationStyle" format="reference" />
<!-- Whether the popup window should overlap its anchor view. -->
<attr name="overlapAnchor" format="boolean" />
+ <!-- Transition used to move views into the popup window. -->
+ <attr name="popupEnterTransition" format="reference" />
+ <!-- Transition used to move views out of the popup window. -->
+ <attr name="popupExitTransition" format="reference" />
</declare-styleable>
<declare-styleable name="ListPopupWindow">
<!-- Amount of pixels by which the drop down should be offset vertically. -->
@@ -7034,75 +7038,16 @@
<!-- =============================== -->
<eat-comment />
<declare-styleable name="GlowPadView">
- <!-- Reference to an array resource that be shown as targets around a circle. -->
- <attr name="targetDrawables" format="reference" />
-
- <!-- Reference to an array resource that be used as description for the targets around the circle. -->
+ <!-- Reference to an array resource that be used as description for the targets around the circle.
+ {@deprecated Removed.} -->
<attr name="targetDescriptions" format="reference" />
- <!-- Reference to an array resource that be used to announce the directions with targets around the circle. -->
+ <!-- Reference to an array resource that be used to announce the directions with targets around the circle.
+ {@deprecated Removed.}-->
<attr name="directionDescriptions" format="reference" />
-
- <!-- Sets a drawable as the center. -->
- <attr name="handleDrawable" format="reference" />
-
- <!-- Drawable to use for wave ripple animation. -->
- <attr name="outerRingDrawable" format="reference"/>
-
- <!-- Drawble used for drawing points -->
- <attr name="pointDrawable" format="reference" />
-
- <!-- Inner radius of glow area. -->
- <attr name="innerRadius"/>
-
- <!-- Outer radius of glow area. Target icons will be drawn on this circle. -->
- <attr name="outerRadius" format="dimension" />
-
- <!-- Radius of glow under finger. -->
- <attr name="glowRadius" format="dimension" />
-
- <!-- Tactile feedback duration for actions. Set to '0' for no vibration. -->
- <attr name="vibrationDuration" format="integer" />
-
- <!-- How close we need to be before snapping to a target. -->
- <attr name="snapMargin" format="dimension" />
-
- <!-- Number of waves/chevrons to show in animation. -->
- <attr name="feedbackCount" format="integer" />
-
- <!-- Used when the handle shouldn't wait to be hit before following the finger -->
- <attr name="alwaysTrackFinger" format="boolean" />
-
- <!-- Location along the circle of the first item, in degrees.-->
- <attr name="firstItemOffset" format="float" />
-
- <!-- Causes targets to snap to the finger location on activation. -->
- <attr name="magneticTargets" format="boolean" />
-
- <attr name="gravity" />
-
- <!-- Determine whether the glow pad is allowed to scale to fit the bounds indicated
- by its parent. If this is set to false, no scaling will occur. If this is set to true
- scaling will occur to fit for any axis in which gravity is set to center. -->
- <attr name="allowScaling" format="boolean" />
</declare-styleable>
<!-- =============================== -->
- <!-- SizeAdaptiveLayout class attributes -->
- <!-- =============================== -->
- <eat-comment />
- <declare-styleable name="SizeAdaptiveLayout_Layout">
- <!-- The maximum valid height for this item. -->
- <attr name="layout_maxHeight" format="dimension">
- <!-- Indicates that the view may be resized arbitrarily large. -->
- <enum name="unbounded" value="-1" />
- </attr>
- <!-- The minimum valid height for this item. -->
- <attr name="layout_minHeight" format="dimension" />
- </declare-styleable>
- <declare-styleable name="SizeAdaptiveLayout" />
-
- <!-- =============================== -->
<!-- Location package class attributes -->
<!-- =============================== -->
<eat-comment />
@@ -7316,8 +7261,34 @@
<declare-styleable name="Switch">
<!-- Drawable to use as the "thumb" that switches back and forth. -->
<attr name="thumb" />
+ <!-- Tint to apply to the thumb. -->
+ <attr name="thumbTint" />
+ <!-- Blending mode used to apply the thumb tint. -->
+ <attr name="thumbTintMode" />
<!-- Drawable to use as the "track" that the switch thumb slides within. -->
<attr name="track" format="reference" />
+ <!-- Tint to apply to the track. -->
+ <attr name="trackTint" format="color" />
+ <!-- Blending mode used to apply the track tint. -->
+ <attr name="trackTintMode">
+ <!-- The tint is drawn on top of the drawable.
+ [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+ <enum name="src_over" value="3" />
+ <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+ color channels are thrown out. [Sa * Da, Sc * Da] -->
+ <enum name="src_in" value="5" />
+ <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+ channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+ <enum name="src_atop" value="9" />
+ <!-- Multiplies the color and alpha channels of the drawable with those of
+ the tint. [Sa * Da, Sc * Dc] -->
+ <enum name="multiply" value="14" />
+ <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+ <enum name="screen" value="15" />
+ <!-- Combines the tint and drawable color and alpha channels, clamping the
+ result to valid color values. Saturate(S + D) -->
+ <enum name="add" value="16" />
+ </attr>
<!-- Text to use when the switch is in the checked/"on" state. -->
<attr name="textOn" />
<!-- Text to use when the switch is in the unchecked/"off" state. -->
@@ -7509,11 +7480,6 @@
<enum name="pageDeleteDropTarget" value="7" />
</attr>
- <declare-styleable name="SlidingChallengeLayout_Layout">
- <attr name="layout_childType" />
- <attr name="layout_maxHeight" />
- </declare-styleable>
-
<!-- Attributes that can be used with <code><FragmentBreadCrumbs></code>
tags. -->
<declare-styleable name="FragmentBreadCrumbs">
@@ -7522,27 +7488,6 @@
<attr name="itemColor" format="color|reference" />
</declare-styleable>
- <declare-styleable name="MultiPaneChallengeLayout">
- <!-- Influences how layout_centerWithinArea behaves -->
- <attr name="orientation" />
- </declare-styleable>
-
- <declare-styleable name="MultiPaneChallengeLayout_Layout">
- <!-- Percentage of the screen this child should consume or center within.
- If 0/default, the view will be measured by standard rules
- as if this were a FrameLayout. -->
- <attr name="layout_centerWithinArea" format="float" />
- <attr name="layout_childType" />
- <attr name="layout_gravity" />
- <attr name="layout_maxWidth" format="dimension" />
- <attr name="layout_maxHeight" />
- </declare-styleable>
-
- <declare-styleable name="KeyguardSecurityViewFlipper_Layout">
- <attr name="layout_maxWidth" />
- <attr name="layout_maxHeight" />
- </declare-styleable>
-
<declare-styleable name="Toolbar">
<attr name="titleTextAppearance" format="reference" />
<attr name="subtitleTextAppearance" format="reference" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 68c1046..f2d9de8 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -424,6 +424,10 @@
<!-- Integer indicating wpa_supplicant scan interval in milliseconds -->
<integer translatable="false" name="config_wifi_supplicant_scan_interval">15000</integer>
+ <!-- Integer indicating amount of time failed networks areblacklisted for the purpose
+ of network switching in milliseconds -->
+ <integer translatable="false" name="config_wifi_network_switching_blacklist_time">172800000</integer>
+
<!-- Integer indicating wpa_supplicant scan interval when p2p is connected in milliseconds -->
<integer translatable="false" name="config_wifi_scan_interval_p2p_connected">60000</integer>
@@ -2021,4 +2025,7 @@
<!-- Use ERI text for network name on CDMA LTE -->
<bool name="config_LTE_eri_for_network_name">true</bool>
+
+ <!-- Whether to start in touch mode -->
+ <bool name="config_defaultInTouchMode">true</bool>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 9f49b08..461f9a0 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1759,7 +1759,9 @@
<public type="attr" name="actionModeSplitBackground" id="0x0101039d" />
<public type="attr" name="textAppearanceListItem" id="0x0101039e" />
<public type="attr" name="textAppearanceListItemSmall" id="0x0101039f" />
+ <!-- @deprecated Removed. -->
<public type="attr" name="targetDescriptions" id="0x010103a0" />
+ <!-- @deprecated Removed. -->
<public type="attr" name="directionDescriptions" id="0x010103a1" />
<public type="attr" name="overridesImplicitlyEnabledSubtype" id="0x010103a2" />
<public type="attr" name="listPreferredItemPaddingLeft" id="0x010103a3" />
@@ -2606,6 +2608,9 @@
=============================================================== -->
<eat-comment />
+ <public type="attr" name="trackTint" />
+ <public type="attr" name="trackTintMode" />
+
<public type="style" name="Widget.Material.Button.Colored" />
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f51e82c9..a32aa3e 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3397,6 +3397,7 @@
<!-- This is used to express that something occurred within the last X days (e.g., Last 7 days). -->
<plurals name="last_num_days">
+ <item quantity="one">Last <xliff:g id="count">%d</xliff:g> day</item>
<item quantity="other">Last <xliff:g id="count">%d</xliff:g> days</item>
</plurals>
@@ -3748,6 +3749,9 @@
<xliff:g id="number" example="123">%1$d</xliff:g> of
<xliff:g id="number" example="123">%2$d</xliff:g>.</string>
+ <!-- [CHAR LIMIT=NONE] Message shown in upgrading dialog for each .apk pre boot broadcast -->
+ <string name="android_preparing_apk">Preparing <xliff:g id="appname">%1$s</xliff:g>.</string>
+
<!-- [CHAR LIMIT=NONE] Message to show in upgrading dialog when reached the point of starting apps. -->
<string name="android_upgrading_starting_apps">Starting apps.</string>
@@ -5188,4 +5192,9 @@
<string name="stk_cc_ss_to_ussd">SS request is modified to USSD request.</string>
<string name="stk_cc_ss_to_ss">SS request is modified to new SS request.</string>
+ <!-- Manufacturer name for USB MIDI Peripheral port -->
+ <string name="usb_midi_peripheral_manufacturer_name">Android</string>
+ <!-- Model name for USB MIDI Peripheral port -->
+ <string name="usb_midi_peripheral_model_name">USB Peripheral Port</string>
+
</resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 4329809..48645ed 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -770,6 +770,9 @@
<item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
<item name="popupBackground">@drawable/popup_background_material</item>
<item name="popupElevation">@dimen/floating_window_z</item>
+ <item name="popupAnimationStyle">@empty</item>
+ <item name="popupEnterTransition">@transition/popup_window_enter</item>
+ <item name="popupExitTransition">@transition/popup_window_exit</item>
<item name="dropDownVerticalOffset">0dip</item>
<item name="dropDownHorizontalOffset">0dip</item>
<item name="overlapAnchor">true</item>
@@ -780,11 +783,7 @@
</style>
<style name="Widget.Material.Spinner.DropDown"/>
-
- <style name="Widget.Material.Spinner.DropDown.ActionBar">
- <item name="background">@drawable/spinner_background_material</item>
- <item name="overlapAnchor">true</item>
- </style>
+ <style name="Widget.Material.Spinner.DropDown.ActionBar" />
<style name="Widget.Material.Spinner.Underlined">
<item name="background">@drawable/spinner_textfield_background_material</item>
@@ -847,7 +846,9 @@
<item name="dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
<item name="popupBackground">@drawable/popup_background_material</item>
<item name="popupElevation">@dimen/floating_window_z</item>
- <item name="popupAnimationStyle">@style/Animation.Material.Popup</item>
+ <item name="popupAnimationStyle">@empty</item>
+ <item name="popupEnterTransition">@transition/popup_window_enter</item>
+ <item name="popupExitTransition">@transition/popup_window_exit</item>
<item name="dropDownVerticalOffset">0dip</item>
<item name="dropDownHorizontalOffset">0dip</item>
<item name="dropDownWidth">wrap_content</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 574c099..9c1fd07 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -327,6 +327,7 @@
<java-symbol type="integer" name="config_wifi_framework_scan_result_rssi_level_patchup_value" />
<java-symbol type="integer" name="config_wifi_framework_current_network_boost" />
<java-symbol type="string" name="config_wifi_random_mac_oui" />
+ <java-symbol type="integer" name="config_wifi_network_switching_blacklist_time" />
<java-symbol type="bool" name="editable_voicemailnumber" />
@@ -1513,6 +1514,7 @@
<java-symbol type="layout" name="screen_title" />
<java-symbol type="layout" name="screen_title_icons" />
<java-symbol type="string" name="system_ui_date_pattern" />
+ <java-symbol type="string" name="android_preparing_apk" />
<java-symbol type="string" name="android_start_title" />
<java-symbol type="string" name="android_upgrading_title" />
<java-symbol type="string" name="bugreport_title" />
@@ -2154,4 +2156,8 @@
<java-symbol type="bool" name="config_use_sim_language_file" />
<java-symbol type="bool" name="config_LTE_eri_for_network_name" />
+ <java-symbol type="bool" name="config_defaultInTouchMode" />
+
+ <java-symbol type="string" name="usb_midi_peripheral_manufacturer_name" />
+ <java-symbol type="string" name="usb_midi_peripheral_model_name" />
</resources>
diff --git a/core/tests/coretests/res/layout/size_adaptive.xml b/core/tests/coretests/res/layout/size_adaptive.xml
deleted file mode 100644
index 03d0574..0000000
--- a/core/tests/coretests/res/layout/size_adaptive.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-<com.android.internal.widget.SizeAdaptiveLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:internal="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/multi1"
- android:layout_width="match_parent"
- android:layout_height="64dp" >
-
- <include
- android:id="@+id/one_u"
- layout="@layout/size_adaptive_one_u"
- android:layout_width="fill_parent"
- android:layout_height="64dp"
- internal:layout_minHeight="64dp"
- internal:layout_maxHeight="64dp"
- />
-
- <include
- android:id="@+id/four_u"
- layout="@layout/size_adaptive_four_u"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- internal:layout_minHeight="65dp"
- internal:layout_maxHeight="unbounded"/>
-
-</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_color.xml b/core/tests/coretests/res/layout/size_adaptive_color.xml
deleted file mode 100644
index cdb7a59..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_color.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-<com.android.internal.widget.SizeAdaptiveLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:internal="http://schemas.android.com/apk/prv/res/android"
- android:background="#ffffff"
- android:id="@+id/multi1"
- android:layout_width="match_parent"
- android:layout_height="64dp" >
-
- <include
- android:id="@+id/one_u"
- layout="@layout/size_adaptive_one_u"
- android:layout_width="fill_parent"
- android:layout_height="64dp"
- internal:layout_minHeight="64dp"
- internal:layout_maxHeight="64dp"
- />
-
- <include
- android:id="@+id/four_u"
- layout="@layout/size_adaptive_four_u"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- internal:layout_minHeight="65dp"
- internal:layout_maxHeight="unbounded"/>
-
-</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_color_statelist.xml b/core/tests/coretests/res/layout/size_adaptive_color_statelist.xml
deleted file mode 100644
index d24df5b..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_color_statelist.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-<com.android.internal.widget.SizeAdaptiveLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:internal="http://schemas.android.com/apk/prv/res/android"
- android:background="@drawable/size_adaptive_statelist"
- android:id="@+id/multi1"
- android:layout_width="match_parent"
- android:layout_height="64dp" >
-
- <include
- android:id="@+id/one_u"
- layout="@layout/size_adaptive_one_u"
- android:layout_width="fill_parent"
- android:layout_height="64dp"
- internal:layout_minHeight="64dp"
- internal:layout_maxHeight="64dp"
- />
-
- <include
- android:id="@+id/four_u"
- layout="@layout/size_adaptive_four_u"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- internal:layout_minHeight="65dp"
- internal:layout_maxHeight="unbounded"/>
-
-</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_four_u.xml b/core/tests/coretests/res/layout/size_adaptive_four_u.xml
deleted file mode 100644
index 232b921..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_four_u.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/gridLayout4"
- android:layout_width="match_parent"
- android:layout_height="256dp"
- android:background="#000000"
- android:columnCount="2"
- android:padding="1dp" >
-
- <ImageView
- android:id="@+id/actor"
- android:layout_width="62dp"
- android:layout_height="62dp"
- android:layout_row="0"
- android:layout_column="0"
- android:layout_rowSpan="2"
- android:contentDescription="@string/actor"
- android:src="@drawable/abe" />
-
- <TextView
- android:layout_width="0dp"
- android:id="@+id/name"
- android:layout_row="0"
- android:layout_column="1"
- android:layout_gravity="fill_horizontal"
- android:padding="3dp"
- android:text="@string/actor"
- android:textColor="#ffffff"
- android:textStyle="bold" />
-
- <ImageView
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_row="1"
- android:layout_column="1"
- android:layout_gravity="fill_horizontal"
- android:padding="5dp"
- android:adjustViewBounds="true"
- android:background="#555555"
- android:scaleType="centerCrop"
- android:src="@drawable/gettysburg"
- android:contentDescription="@string/caption" />
-
- <TextView
- android:layout_width="0dp"
- android:id="@+id/note"
- android:layout_row="2"
- android:layout_column="1"
- android:layout_gravity="fill_horizontal"
- android:padding="3dp"
- android:singleLine="true"
- android:text="@string/first"
- android:textColor="#ffffff" />
-</GridLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_four_u_text.xml b/core/tests/coretests/res/layout/size_adaptive_four_u_text.xml
deleted file mode 100644
index 93a10de..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_four_u_text.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/gridLayout4"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="#000000"
- android:columnCount="2"
- android:padding="1dp"
- android:orientation="horizontal" >
-
- <ImageView
- android:id="@+id/actor"
- android:layout_width="62dp"
- android:layout_height="62dp"
- android:layout_row="0"
- android:layout_column="0"
- android:layout_rowSpan="2"
- android:contentDescription="@string/actor"
- android:src="@drawable/abe" />
-
- <TextView
- android:layout_width="0dp"
- android:id="@+id/name"
- android:layout_row="0"
- android:layout_column="1"
- android:layout_gravity="fill_horizontal"
- android:padding="3dp"
- android:text="@string/actor"
- android:textColor="#ffffff"
- android:textStyle="bold" />
-
- <TextView
- android:id="@+id/note"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_column="1"
- android:layout_gravity="fill_horizontal"
- android:layout_marginTop="5dp"
- android:layout_row="1"
- android:padding="3dp"
- android:singleLine="false"
- android:text="@string/first"
- android:textColor="#ffffff" />
-
- </GridLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_gappy.xml b/core/tests/coretests/res/layout/size_adaptive_gappy.xml
deleted file mode 100644
index d5e3b41..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_gappy.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-<com.android.internal.widget.SizeAdaptiveLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:internal="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/multi_with_gap"
- android:layout_width="match_parent"
- android:layout_height="64dp" >
-
- <include
- android:id="@+id/one_u"
- layout="@layout/size_adaptive_one_u"
- android:layout_width="fill_parent"
- android:layout_height="64dp"
- internal:layout_minHeight="64dp"
- internal:layout_maxHeight="64dp"
- />
-
- <include
- android:id="@+id/four_u"
- layout="@layout/size_adaptive_four_u"
- android:layout_width="fill_parent"
- android:layout_height="256dp"
- internal:layout_minHeight="128dp"
- internal:layout_maxHeight="unbounded"/>
-
-</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_large_only.xml b/core/tests/coretests/res/layout/size_adaptive_large_only.xml
deleted file mode 100644
index cf58265..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_large_only.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-<com.android.internal.widget.SizeAdaptiveLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:internal="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/large_only_multi"
- android:layout_width="match_parent"
- android:layout_height="64dp" >
-
- <include
- android:id="@+id/four_u"
- layout="@layout/size_adaptive_four_u"
- android:layout_width="fill_parent"
- android:layout_height="256dp"
- internal:layout_minHeight="65dp"
- internal:layout_maxHeight="unbounded"/>
-
-</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_lies.xml b/core/tests/coretests/res/layout/size_adaptive_lies.xml
deleted file mode 100644
index 7de892e..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_lies.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-<com.android.internal.widget.SizeAdaptiveLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:internal="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/multi1"
- android:layout_width="match_parent"
- android:layout_height="64dp" >
-
- <include
- android:id="@+id/one_u"
- layout="@layout/size_adaptive_one_u"
- android:layout_width="fill_parent"
- android:layout_height="64dp"
- internal:layout_minHeight="64dp"
- internal:layout_maxHeight="64dp"
- />
-
- <include
- android:id="@+id/four_u"
- layout="@layout/size_adaptive_one_u"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- internal:layout_minHeight="65dp"
- internal:layout_maxHeight="unbounded"/>
-
-</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_one_u.xml b/core/tests/coretests/res/layout/size_adaptive_one_u.xml
deleted file mode 100644
index b6fe4a0..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_one_u.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/gridLayout1"
- android:layout_width="match_parent"
- android:layout_height="64dp"
- android:background="#000000"
- android:columnCount="3"
- android:padding="1dp"
- android:rowCount="2" >
-
- <ImageView
- android:id="@+id/actor"
- android:layout_width="62dp"
- android:layout_height="62dp"
- android:layout_column="0"
- android:layout_row="0"
- android:layout_rowSpan="2"
- android:contentDescription="@string/actor"
- android:src="@drawable/abe" />
-
- <TextView
- android:id="@+id/name"
- android:layout_gravity="fill"
- android:padding="3dp"
- android:text="@string/actor"
- android:textColor="#ffffff"
- android:textStyle="bold" />
-
- <ImageView
- android:layout_width="62dp"
- android:layout_height="62dp"
- android:layout_gravity="fill_vertical"
- android:layout_rowSpan="2"
- android:adjustViewBounds="true"
- android:background="#555555"
- android:padding="2dp"
- android:scaleType="fitXY"
- android:src="@drawable/gettysburg"
- android:contentDescription="@string/caption" />
-
- <TextView
- android:id="@+id/note"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_gravity="fill"
- android:layout_marginTop="5dp"
- android:padding="3dp"
- android:singleLine="true"
- android:text="@string/first"
- android:textColor="#ffffff" />
-
-</GridLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_one_u_text.xml b/core/tests/coretests/res/layout/size_adaptive_one_u_text.xml
deleted file mode 100644
index df54eb6..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_one_u_text.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/gridLayout1"
- android:layout_width="match_parent"
- android:layout_height="64dp"
- android:background="#000000"
- android:columnCount="2"
- android:padding="1dp"
- android:rowCount="2" >
-
- <ImageView
- android:id="@+id/actor"
- android:layout_width="62dp"
- android:layout_height="62dp"
- android:layout_column="0"
- android:layout_row="0"
- android:layout_rowSpan="2"
- android:contentDescription="@string/actor"
- android:src="@drawable/abe" />
-
- <TextView
- android:id="@+id/name"
- android:layout_gravity="fill"
- android:padding="3dp"
- android:text="@string/actor"
- android:textColor="#ffffff"
- android:textStyle="bold" />
-
- <TextView
- android:id="@+id/note"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_gravity="fill"
- android:layout_marginTop="5dp"
- android:padding="3dp"
- android:singleLine="true"
- android:text="@string/first"
- android:textColor="#ffffff" />
-
-</GridLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_overlapping.xml b/core/tests/coretests/res/layout/size_adaptive_overlapping.xml
deleted file mode 100644
index 4abe8b0..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_overlapping.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-<com.android.internal.widget.SizeAdaptiveLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:internal="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/multi_with_overlap"
- android:layout_width="match_parent"
- android:layout_height="64dp" >
-
- <include
- android:id="@+id/one_u"
- layout="@layout/size_adaptive_one_u"
- android:layout_width="fill_parent"
- android:layout_height="64dp"
- internal:layout_minHeight="64dp"
- internal:layout_maxHeight="64dp"
- />
-
- <include
- android:id="@+id/four_u"
- layout="@layout/size_adaptive_four_u"
- android:layout_width="fill_parent"
- android:layout_height="256dp"
- internal:layout_minHeight="64dp"
- internal:layout_maxHeight="256dp"/>
-
-</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_singleton.xml b/core/tests/coretests/res/layout/size_adaptive_singleton.xml
deleted file mode 100644
index eba387f..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_singleton.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-<com.android.internal.widget.SizeAdaptiveLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:internal="http://schemas.android.com/apk/prv/res/android"
- android:layout_width="match_parent"
- android:layout_height="64dp" >
-
- <include
- android:id="@+id/one_u"
- layout="@layout/size_adaptive_one_u_text"
- android:layout_width="fill_parent"
- android:layout_height="64dp"
- internal:layout_minHeight="64dp"
- internal:layout_maxHeight="64dp"
- />
-
-</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_text.xml b/core/tests/coretests/res/layout/size_adaptive_text.xml
deleted file mode 100644
index a9f0ba9..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_text.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-<com.android.internal.widget.SizeAdaptiveLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:internal="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/multi1"
- android:layout_width="match_parent"
- android:layout_height="64dp" >
-
- <include
- android:id="@+id/one_u"
- layout="@layout/size_adaptive_one_u_text"
- android:layout_width="fill_parent"
- android:layout_height="64dp"
- internal:layout_minHeight="64dp"
- internal:layout_maxHeight="64dp"
- />
-
- <include
- android:id="@+id/four_u"
- layout="@layout/size_adaptive_four_u_text"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- internal:layout_minHeight="65dp"
- internal:layout_maxHeight="unbounded"/>
-
-</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/res/layout/size_adaptive_three_way.xml b/core/tests/coretests/res/layout/size_adaptive_three_way.xml
deleted file mode 100644
index 1eb5396..0000000
--- a/core/tests/coretests/res/layout/size_adaptive_three_way.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-<com.android.internal.widget.SizeAdaptiveLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:internal="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/three_way_multi"
- android:layout_width="match_parent"
- android:layout_height="64dp" >
-
- <include
- android:id="@+id/one_u"
- layout="@layout/size_adaptive_one_u"
- android:layout_width="fill_parent"
- android:layout_height="64dp"
- internal:layout_minHeight="64dp"
- internal:layout_maxHeight="64dp"
- />
-
- <include
- android:id="@+id/two_u"
- layout="@layout/size_adaptive_four_u"
- android:layout_width="fill_parent"
- android:layout_height="128dp"
- internal:layout_minHeight="65dp"
- internal:layout_maxHeight="128dp"/>
-
- <include
- android:id="@+id/four_u"
- layout="@layout/size_adaptive_four_u"
- android:layout_width="fill_parent"
- android:layout_height="256dp"
- internal:layout_minHeight="129dp"
- internal:layout_maxHeight="unbounded"/>
-
-</com.android.internal.widget.SizeAdaptiveLayout>
diff --git a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
index e7f4bad..e5a92bf 100644
--- a/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
+++ b/core/tests/coretests/src/android/content/pm/ParceledListSliceTest.java
@@ -11,7 +11,7 @@
public void testSmallList() throws Exception {
final int objectCount = 100;
- List<SmallObject> list = new ArrayList<>();
+ List<SmallObject> list = new ArrayList<SmallObject>();
for (int i = 0; i < objectCount; i++) {
list.add(new SmallObject(i * 2, (i * 2) + 1));
}
@@ -20,7 +20,7 @@
Parcel parcel = Parcel.obtain();
try {
- parcel.writeParcelable(new ParceledListSlice<>(list), 0);
+ parcel.writeParcelable(new ParceledListSlice<SmallObject>(list), 0);
parcel.setDataPosition(0);
slice = parcel.readParcelable(getClass().getClassLoader());
} finally {
@@ -56,7 +56,7 @@
final int thresholdBytes = 256 * 1024;
final int objectCount = thresholdBytes / measureLargeObject();
- List<LargeObject> list = new ArrayList<>();
+ List<LargeObject> list = new ArrayList<LargeObject>();
for (int i = 0; i < objectCount; i++) {
list.add(new LargeObject(
i * 5,
@@ -71,7 +71,7 @@
Parcel parcel = Parcel.obtain();
try {
- parcel.writeParcelable(new ParceledListSlice<>(list), 0);
+ parcel.writeParcelable(new ParceledListSlice<LargeObject>(list), 0);
parcel.setDataPosition(0);
slice = parcel.readParcelable(getClass().getClassLoader());
} finally {
@@ -95,7 +95,7 @@
* Test that only homogeneous elements may be unparceled.
*/
public void testHomogeneousElements() throws Exception {
- List<BaseObject> list = new ArrayList<>();
+ List<BaseObject> list = new ArrayList<BaseObject>();
list.add(new LargeObject(0, 1, 2, 3, 4));
list.add(new SmallObject(5, 6));
list.add(new SmallObject(7, 8));
diff --git a/core/tests/coretests/src/com/android/internal/widget/SizeAdaptiveLayoutTest.java b/core/tests/coretests/src/com/android/internal/widget/SizeAdaptiveLayoutTest.java
deleted file mode 100644
index 18411b0..0000000
--- a/core/tests/coretests/src/com/android/internal/widget/SizeAdaptiveLayoutTest.java
+++ /dev/null
@@ -1,479 +0,0 @@
-/*
- * Copyright (C) 2012 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.internal.widget;
-
-import com.android.frameworks.coretests.R;
-
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import com.android.internal.widget.SizeAdaptiveLayout;
-
-
-public class SizeAdaptiveLayoutTest extends AndroidTestCase {
-
- private LayoutInflater mInflater;
- private int mOneU;
- private int mFourU;
- private SizeAdaptiveLayout mSizeAdaptiveLayout;
- private View mSmallView;
- private View mMediumView;
- private View mLargeView;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- // inflate the layout
- final Context context = getContext();
- mInflater = LayoutInflater.from(context);
- mOneU = 64;
- mFourU = 4 * mOneU;
- }
-
- private void inflate(int resource){
- mSizeAdaptiveLayout = (SizeAdaptiveLayout) mInflater.inflate(resource, null);
- mSizeAdaptiveLayout.onAttachedToWindow();
-
- mSmallView = mSizeAdaptiveLayout.findViewById(R.id.one_u);
- mMediumView = mSizeAdaptiveLayout.findViewById(R.id.two_u);
- mLargeView = mSizeAdaptiveLayout.findViewById(R.id.four_u);
- }
-
- /**
- * The name 'test preconditions' is a convention to signal that if this
- * test doesn't pass, the test case was not set up properly and it might
- * explain any and all failures in other tests. This is not guaranteed
- * to run before other tests, as junit uses reflection to find the tests.
- */
- @SmallTest
- public void testPreconditions() {
- assertNotNull(mInflater);
-
- inflate(R.layout.size_adaptive);
- assertNotNull(mSizeAdaptiveLayout);
- assertNotNull(mSmallView);
- assertNotNull(mLargeView);
- }
-
- @SmallTest
- public void testOpenLarge() {
- inflate(R.layout.size_adaptive);
- SizeAdaptiveLayout.LayoutParams lp =
- (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
- int height = (int) lp.minHeight + 10;
-
- measureAndLayout(height);
-
- assertEquals("4U should be visible",
- View.VISIBLE,
- mLargeView.getVisibility());
- assertEquals("1U should be gone",
- View.GONE,
- mSmallView.getVisibility());
- }
-
- @SmallTest
- public void testOpenSmall() {
- inflate(R.layout.size_adaptive);
- SizeAdaptiveLayout.LayoutParams lp =
- (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
- int height = (int) lp.minHeight;
-
- measureAndLayout(height);
-
- assertEquals("1U should be visible",
- View.VISIBLE,
- mSmallView.getVisibility());
- assertEquals("4U should be gone",
- View.GONE,
- mLargeView.getVisibility());
- }
-
- @SmallTest
- public void testOpenTooSmall() {
- inflate(R.layout.size_adaptive);
- SizeAdaptiveLayout.LayoutParams lp =
- (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
- int height = (int) lp.minHeight - 10;
-
- measureAndLayout(height);
-
- assertEquals("1U should be visible",
- View.VISIBLE,
- mSmallView.getVisibility());
- assertEquals("4U should be gone",
- View.GONE,
- mLargeView.getVisibility());
- }
-
- @SmallTest
- public void testOpenTooBig() {
- inflate(R.layout.size_adaptive);
- SizeAdaptiveLayout.LayoutParams lp =
- (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
- lp.maxHeight = 500;
- mLargeView.setLayoutParams(lp);
- int height = (int) (lp.minHeight + 10);
-
- measureAndLayout(height);
-
- assertEquals("4U should be visible",
- View.VISIBLE,
- mLargeView.getVisibility());
- assertEquals("1U should be gone",
- View.GONE,
- mSmallView.getVisibility());
- }
-
- @SmallTest
- public void testOpenWrapContent() {
- inflate(R.layout.size_adaptive_text);
- SizeAdaptiveLayout.LayoutParams lp =
- (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
- int height = (int) lp.minHeight + 10;
-
- // manually measure it, and lay it out
- int measureSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST);
- mSizeAdaptiveLayout.measure(500, measureSpec);
- assertTrue("should not be forced to 4U",
- mSizeAdaptiveLayout.getMeasuredHeight() < mFourU);
- }
-
- @SmallTest
- public void testOpenOneUOnlySmall() {
- inflate(R.layout.size_adaptive_singleton);
- assertNull("largeView should be NULL in the singleton layout", mLargeView);
-
- SizeAdaptiveLayout.LayoutParams lp =
- (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
- int height = (int) lp.minHeight - 10;
-
- measureAndLayout(height);
-
- assertEquals("1U should be visible",
- View.VISIBLE,
- mSmallView.getVisibility());
- }
-
- @SmallTest
- public void testOpenOneUOnlyLarge() {
- inflate(R.layout.size_adaptive_singleton);
- assertNull("largeView should be NULL in the singleton layout", mLargeView);
-
- SizeAdaptiveLayout.LayoutParams lp =
- (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
- int height = (int) lp.maxHeight + 10;
-
- measureAndLayout(height);
-
- assertEquals("1U should be visible",
- View.VISIBLE,
- mSmallView.getVisibility());
- }
-
- @SmallTest
- public void testOpenOneUOnlyJustRight() {
- inflate(R.layout.size_adaptive_singleton);
- assertNull("largeView should be NULL in the singleton layout", mLargeView);
-
- SizeAdaptiveLayout.LayoutParams lp =
- (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
- int height = (int) lp.minHeight;
-
- measureAndLayout(height);
-
- assertEquals("1U should be visible",
- View.VISIBLE,
- mSmallView.getVisibility());
- }
-
- @SmallTest
- public void testOpenFourUOnlySmall() {
- inflate(R.layout.size_adaptive_large_only);
- assertNull("smallView should be NULL in the singleton layout", mSmallView);
-
- SizeAdaptiveLayout.LayoutParams lp =
- (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
- int height = (int) lp.minHeight - 10;
-
- measureAndLayout(height);
-
- assertEquals("4U should be visible",
- View.VISIBLE,
- mLargeView.getVisibility());
- }
-
- @SmallTest
- public void testOpenFourUOnlyLarge() {
- inflate(R.layout.size_adaptive_large_only);
- assertNull("smallView should be NULL in the singleton layout", mSmallView);
-
- SizeAdaptiveLayout.LayoutParams lp =
- (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
- int height = (int) lp.maxHeight + 10;
-
- measureAndLayout(height);
-
- assertEquals("4U should be visible",
- View.VISIBLE,
- mLargeView.getVisibility());
- }
-
- @SmallTest
- public void testOpenFourUOnlyJustRight() {
- inflate(R.layout.size_adaptive_large_only);
- assertNull("smallView should be NULL in the singleton layout", mSmallView);
-
- SizeAdaptiveLayout.LayoutParams lp =
- (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
- int height = (int) lp.minHeight;
-
- measureAndLayout(height);
-
- assertEquals("4U should be visible",
- View.VISIBLE,
- mLargeView.getVisibility());
- }
-
- @SmallTest
- public void testOpenIntoAGap() {
- inflate(R.layout.size_adaptive_gappy);
-
- SizeAdaptiveLayout.LayoutParams smallParams =
- (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
- SizeAdaptiveLayout.LayoutParams largeParams =
- (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
- assertTrue("gappy layout should have a gap",
- smallParams.maxHeight + 10 < largeParams.minHeight);
- int height = (int) smallParams.maxHeight + 10;
-
- measureAndLayout(height);
-
- assertTrue("one and only one view should be visible",
- mLargeView.getVisibility() != mSmallView.getVisibility());
- // behavior is undefined in this case.
- }
-
- @SmallTest
- public void testOpenIntoAnOverlap() {
- inflate(R.layout.size_adaptive_overlapping);
-
- SizeAdaptiveLayout.LayoutParams smallParams =
- (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
- SizeAdaptiveLayout.LayoutParams largeParams =
- (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
- assertEquals("overlapping layout should overlap",
- smallParams.minHeight,
- largeParams.minHeight);
- int height = (int) smallParams.maxHeight;
-
- measureAndLayout(height);
-
- assertTrue("one and only one view should be visible",
- mLargeView.getVisibility() != mSmallView.getVisibility());
- assertEquals("1U should get priority in an overlap because it is first",
- View.VISIBLE,
- mSmallView.getVisibility());
- }
-
- @SmallTest
- public void testOpenThreeWayViewSmall() {
- inflate(R.layout.size_adaptive_three_way);
- assertNotNull("mMediumView should not be NULL in the three view layout", mMediumView);
-
- SizeAdaptiveLayout.LayoutParams lp =
- (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
- int height = (int) lp.minHeight;
-
- measureAndLayout(height);
-
- assertEquals("1U should be visible",
- View.VISIBLE,
- mSmallView.getVisibility());
- assertEquals("2U should be gone",
- View.GONE,
- mMediumView.getVisibility());
- assertEquals("4U should be gone",
- View.GONE,
- mLargeView.getVisibility());
- }
-
- @SmallTest
- public void testOpenThreeWayViewMedium() {
- inflate(R.layout.size_adaptive_three_way);
- assertNotNull("mMediumView should not be NULL in the three view layout", mMediumView);
-
- SizeAdaptiveLayout.LayoutParams lp =
- (SizeAdaptiveLayout.LayoutParams) mMediumView.getLayoutParams();
- int height = (int) lp.minHeight;
-
- measureAndLayout(height);
-
- assertEquals("1U should be gone",
- View.GONE,
- mSmallView.getVisibility());
- assertEquals("2U should be visible",
- View.VISIBLE,
- mMediumView.getVisibility());
- assertEquals("4U should be gone",
- View.GONE,
- mLargeView.getVisibility());
- }
-
- @SmallTest
- public void testOpenThreeWayViewLarge() {
- inflate(R.layout.size_adaptive_three_way);
- assertNotNull("mMediumView should not be NULL in the three view layout", mMediumView);
-
- SizeAdaptiveLayout.LayoutParams lp =
- (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
- int height = (int) lp.minHeight;
-
- measureAndLayout(height);
-
- assertEquals("1U should be gone",
- View.GONE,
- mSmallView.getVisibility());
- assertEquals("2U should be gone",
- View.GONE,
- mMediumView.getVisibility());
- assertEquals("4U should be visible",
- View.VISIBLE,
- mLargeView.getVisibility());
- }
-
- @SmallTest
- public void testResizeWithoutAnimation() {
- inflate(R.layout.size_adaptive);
-
- SizeAdaptiveLayout.LayoutParams largeParams =
- (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
- int startHeight = (int) largeParams.minHeight + 10;
- int endHeight = (int) largeParams.minHeight + 10;
-
- measureAndLayout(startHeight);
-
- assertEquals("4U should be visible",
- View.VISIBLE,
- mLargeView.getVisibility());
- assertFalse("There should be no animation on initial rendering.",
- mSizeAdaptiveLayout.getTransitionAnimation().isRunning());
-
- measureAndLayout(endHeight);
-
- assertEquals("4U should still be visible",
- View.VISIBLE,
- mLargeView.getVisibility());
- assertFalse("There should be no animation on scale within a view.",
- mSizeAdaptiveLayout.getTransitionAnimation().isRunning());
- }
-
- @SmallTest
- public void testResizeWithAnimation() {
- inflate(R.layout.size_adaptive);
-
- SizeAdaptiveLayout.LayoutParams smallParams =
- (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
- SizeAdaptiveLayout.LayoutParams largeParams =
- (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
- int startHeight = (int) largeParams.minHeight + 10;
- int endHeight = (int) smallParams.maxHeight;
-
- measureAndLayout(startHeight);
-
- assertEquals("4U should be visible",
- View.VISIBLE,
- mLargeView.getVisibility());
- assertFalse("There should be no animation on initial rendering.",
- mSizeAdaptiveLayout.getTransitionAnimation().isRunning());
-
- measureAndLayout(endHeight);
-
- assertEquals("1U should now be visible",
- View.VISIBLE,
- mSmallView.getVisibility());
- assertTrue("There should be an animation on scale between views.",
- mSizeAdaptiveLayout.getTransitionAnimation().isRunning());
- }
-
- @SmallTest
- public void testModestyPanelChangesColorWhite() {
- inflate(R.layout.size_adaptive_color);
- View panel = mSizeAdaptiveLayout.getModestyPanel();
- assertTrue("ModestyPanel should have a ColorDrawable background",
- panel.getBackground() instanceof ColorDrawable);
- ColorDrawable panelColor = (ColorDrawable) panel.getBackground();
- ColorDrawable salColor = (ColorDrawable) mSizeAdaptiveLayout.getBackground();
- assertEquals("ModestyPanel color should match the SizeAdaptiveLayout",
- panelColor.getColor(), salColor.getColor());
- }
-
- @SmallTest
- public void testModestyPanelTracksStateListColor() {
- inflate(R.layout.size_adaptive_color_statelist);
- View panel = mSizeAdaptiveLayout.getModestyPanel();
- assertEquals("ModestyPanel should have a ColorDrawable background" ,
- panel.getBackground().getClass(), ColorDrawable.class);
- ColorDrawable panelColor = (ColorDrawable) panel.getBackground();
- assertEquals("ModestyPanel color should match the SizeAdaptiveLayout",
- panelColor.getColor(), Color.RED);
- }
- @SmallTest
- public void testOpenSmallEvenWhenLargeIsActuallySmall() {
- inflate(R.layout.size_adaptive_lies);
- SizeAdaptiveLayout.LayoutParams lp =
- (SizeAdaptiveLayout.LayoutParams) mSmallView.getLayoutParams();
- int height = (int) lp.minHeight;
-
- measureAndLayout(height);
-
- assertEquals("1U should be visible",
- View.VISIBLE,
- mSmallView.getVisibility());
- assertTrue("1U should also have been measured",
- mSmallView.getMeasuredHeight() > 0);
- }
-
- @SmallTest
- public void testOpenLargeEvenWhenLargeIsActuallySmall() {
- inflate(R.layout.size_adaptive_lies);
- SizeAdaptiveLayout.LayoutParams lp =
- (SizeAdaptiveLayout.LayoutParams) mLargeView.getLayoutParams();
- int height = (int) lp.minHeight;
-
- measureAndLayout(height);
-
- assertEquals("4U should be visible",
- View.VISIBLE,
- mLargeView.getVisibility());
- assertTrue("4U should also have been measured",
- mLargeView.getMeasuredHeight() > 0);
- }
-
- private void measureAndLayout(int height) {
- // manually measure it, and lay it out
- int measureSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST);
- mSizeAdaptiveLayout.measure(500, measureSpec);
- mSizeAdaptiveLayout.layout(0, 0, 500, mSizeAdaptiveLayout.getMeasuredHeight());
- }
-}
diff --git a/docs/html/google/play-services/ads.jd b/docs/html/google/play-services/ads.jd
index e4f0b2c..2f915f3 100644
--- a/docs/html/google/play-services/ads.jd
+++ b/docs/html/google/play-services/ads.jd
@@ -98,10 +98,8 @@
serve banner and interstitial ads using the Google Mobile Ads APIs.</p>
<h4>3. Read the documentation</h4>
- <p>Read the <a class="external-link" href="https://www.google.com/adsense/localized-terms">AdSense
- Terms of Service</a> and the <a class="external-link"
- href="https://support.google.com/admob/topic/1307235?hl=en&ref_topic=1307209">AdMob
- publisher guidelines and policies</a>.</p>
+ <p>Your use of the Google Mobile Ads SDK is governed by the terms between you and Google that
+ govern your use of the Google product (AdSense/AdMob, AdX or DFP) with which you use the SDK.</p>
<p>For quick access while developing your Android apps, the <a
href="{@docRoot}reference/gms-packages.html">Google Mobile Ads API reference</a> is available here on
developer.android.com.</p>
diff --git a/docs/html/images/transitions/transition_sample_video.mp4 b/docs/html/images/transitions/transition_sample_video.mp4
new file mode 100644
index 0000000..37ae685
--- /dev/null
+++ b/docs/html/images/transitions/transition_sample_video.mp4
Binary files differ
diff --git a/docs/html/images/transitions/transition_sample_video.ogv b/docs/html/images/transitions/transition_sample_video.ogv
new file mode 100644
index 0000000..5598814
--- /dev/null
+++ b/docs/html/images/transitions/transition_sample_video.ogv
Binary files differ
diff --git a/docs/html/images/transitions/transition_sample_video.webm b/docs/html/images/transitions/transition_sample_video.webm
new file mode 100644
index 0000000..346ba8c
--- /dev/null
+++ b/docs/html/images/transitions/transition_sample_video.webm
Binary files differ
diff --git a/docs/html/images/transitions/transitions_diagram.png b/docs/html/images/transitions/transitions_diagram.png
new file mode 100644
index 0000000..9363940
--- /dev/null
+++ b/docs/html/images/transitions/transitions_diagram.png
Binary files differ
diff --git a/docs/html/images/tv/app-browse.png b/docs/html/images/tv/app-browse.png
new file mode 100644
index 0000000..7670713
--- /dev/null
+++ b/docs/html/images/tv/app-browse.png
Binary files differ
diff --git a/docs/html/images/tv/card-view.png b/docs/html/images/tv/card-view.png
new file mode 100644
index 0000000..5e907de
--- /dev/null
+++ b/docs/html/images/tv/card-view.png
Binary files differ
diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd
index e74d46a..af6f6b8 100644
--- a/docs/html/sdk/index.jd
+++ b/docs/html/sdk/index.jd
@@ -481,14 +481,14 @@
<ul>
<li>GNOME or KDE desktop</li>
-<li>GNU C Library (glibc) 2.11 or later</li>
+<li>GNU C Library (glibc) 2.15 or later</li>
<li>2 GB RAM minimum, 4 GB RAM recommended</li>
<li>400 MB hard disk space</li>
<li>At least 1 GB for Android SDK, emulator system images, and caches</li>
<li>1280 x 800 minimum screen resolution</li>
<li>Oracle® Java Development Kit (JDK) 7 </li>
</ul>
-<p>Tested on Ubuntu® 12.04, Precise Pangolin (64-bit distribution capable of running
+<p>Tested on Ubuntu® 14.04, Trusty Tahr (64-bit distribution capable of running
32-bit applications).</p>
diff --git a/docs/html/tools/building/plugin-for-gradle.jd b/docs/html/tools/building/plugin-for-gradle.jd
index 77cbfda..54a03fd 100644
--- a/docs/html/tools/building/plugin-for-gradle.jd
+++ b/docs/html/tools/building/plugin-for-gradle.jd
@@ -321,7 +321,7 @@
machine and on other machines where Android Studio is not installed.</p>
<p class="caution"><strong>Caution:</strong> When you create a project, only use the Gradle wrapper
-scripts and JAR from a trusted source, such as those generated by Android Studio. /p>
+scripts and JAR from a trusted source, such as those generated by Android Studio. </p>
<h2 id="buildVariants"> Build variants</h2>
diff --git a/docs/html/tools/devices/emulator.jd b/docs/html/tools/devices/emulator.jd
index 42240b9..5bdd4e2 100644
--- a/docs/html/tools/devices/emulator.jd
+++ b/docs/html/tools/devices/emulator.jd
@@ -122,7 +122,7 @@
mobile devices, including: </p>
<ul>
- <li>An ARMv5 CPU and the corresponding memory-management unit (MMU)</li>
+ <li>An ARMv5, ARMv7, or x86 CPU</li>
<li>A 16-bit LCD display</li>
<li>One or more keyboards (a Qwerty-based keyboard and associated Dpad/Phone
buttons)</li>
diff --git a/docs/html/training/basics/intents/sending.jd b/docs/html/training/basics/intents/sending.jd
index 4698ba1..b9463e4 100644
--- a/docs/html/training/basics/intents/sending.jd
+++ b/docs/html/training/basics/intents/sending.jd
@@ -153,7 +153,8 @@
<pre>
PackageManager packageManager = {@link android.content.Context#getPackageManager()};
-List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
+List<ResolveInfo> activities = packageManager.queryIntentActivities(intent,
+ PackageManager.MATCH_DEFAULT_ONLY);
boolean isIntentSafe = activities.size() > 0;
</pre>
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index 0fcfb9c..c59d8ff 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -437,6 +437,35 @@
</li>
</ul>
</li>
+
+ <li class="nav-section">
+ <div class="nav-section-header">
+ <a href="<?cs var:toroot?>training/transitions/index.html"
+ description=
+ "How to animate state changes in a view hierarchy using transitions."
+ >Animating Views Using Scenes and Transitions</a>
+ </div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>training/transitions/overview.html">
+ The Transitions Framework
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/transitions/scenes.html">
+ Creating a Scene
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/transitions/transitions.html">
+ Applying a Transition
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/transitions/custom-transitions.html">
+ Creating Custom Transitions
+ </a>
+ </li>
+
+ </ul>
+ </li>
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>training/animation/index.html"
description=
@@ -922,6 +951,10 @@
Creating a Catalog Browser</a>
</li>
<li>
+ <a href="<?cs var:toroot ?>training/tv/playback/card.html">
+ Providing a Card View</a>
+ </li>
+ <li>
<a href="<?cs var:toroot ?>training/tv/playback/details.html"
ja-lang="詳細ビューをビルドする">
Building a Details View</a>
@@ -1854,4 +1887,4 @@
buildToggleLists();
changeNavLang(getLangPref());
//-->
-</script>
+</script>
\ No newline at end of file
diff --git a/docs/html/training/transitions/custom-transitions.jd b/docs/html/training/transitions/custom-transitions.jd
new file mode 100644
index 0000000..b64daaeb
--- /dev/null
+++ b/docs/html/training/transitions/custom-transitions.jd
@@ -0,0 +1,189 @@
+page.title=Creating Custom Transitions
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#Extend">Extend the Transition Class</a></li>
+ <li><a href="#CaptureProperties">Capture View Property Values</a></li>
+ <li><a href="#CreateAnimator">Create a Custom Animator</a></li>
+ <li><a href="#Apply">Apply a Custom Transition</a></li>
+</ol>
+</div>
+</div>
+
+<p>A custom transition enables you to create an animation that is not available from any of
+the built-in transition classes. For example, you can define a custom transition that turns
+the foreground color of text and input fields to gray to indicate that the fields are disabled
+in the new screen. This type of change helps users see the fields you disabled.</p>
+
+<p>A custom transition, like one of the built-in transition types, applies animations to
+child views of both the starting and ending scenes. Unlike built-in transition types,
+however, you have to provide the code that captures property values and generates animations.
+You may also want to define a subset of target views for your animation.</p>
+
+<p>This lesson teaches you to capture property values and generate animations to create
+custom transitions.</p>
+
+
+
+<h2 id="Extend">Extend the Transition Class</h2>
+
+<p>To create a custom transition, add a class to your project that extends the {@link
+android.transition.Transition} class and override the methods shown in the following snippet:</p>
+
+<pre>
+public class CustomTransition extends Transition {
+
+ @Override
+ public void captureStartValues(TransitionValues values) {}
+
+ @Override
+ public void captureEndValues(TransitionValues values) {}
+
+ @Override
+ public Animator createAnimator(ViewGroup sceneRoot,
+ TransitionValues startValues,
+ TransitionValues endValues) {}
+}
+</pre>
+
+<p>The following sections explain how to override these methods.</p>
+
+
+
+<h2 id="CaptureProperties">Capture View Property Values</h2>
+
+<p>Transition animations use the property animation system described in
+<a href="{@docRoot}guide/topics/graphics/prop-animation.html">Property Animation</a>. Property
+animations change a view property between a starting and ending value over a specified
+period of time, so the framework needs to have both the starting and ending value of
+the property to construct the animation.</p>
+
+<p>However, a property animation usually needs only a small subset of all the view's property
+values. For example, a color animation needs color property values, while a movement
+animation needs position property values. Since the property values needed for an animation
+are specific to a transition, the transitions framework does not provide every property value
+to a transition. Instead, the framework invokes callback methods that allow a transition to
+capture only the property values it needs and store them in the framework.</p>
+
+
+<h3 id="StartingValues">Capturing Starting Values</h3>
+
+<p>To pass the starting view values to the framework, implement the
+{@link android.transition.Transition#captureStartValues captureStartValues(transitionValues)}
+method. The framework calls this method for every view in the starting scene. The method
+argument is a {@link android.transition.TransitionValues} object that contains a reference
+to the view and a {@link java.util.Map} instance in which you can store the view values you
+want. In your implementation, retrieve these property values and pass them back to the
+framework by storing them in the map.</p>
+
+<p>To ensure that the key for a property value does not conflict with other {@link
+android.transition.TransitionValues} keys, use the following naming scheme:</p>
+
+<pre>
+package_name:transition_name:property_name
+</pre>
+
+<p>The following snippet shows an implementation of the {@link
+android.transition.Transition#captureStartValues captureStartValues()} method:</p>
+
+<pre>
+public class CustomTransition extends Transition {
+
+ // Define a key for storing a property value in
+ // TransitionValues.values with the syntax
+ // package_name:transition_class:property_name to avoid collisions
+ private static final String PROPNAME_BACKGROUND =
+ "com.example.android.customtransition:CustomTransition:background";
+
+ @Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ // Call the convenience method captureValues
+ captureValues(transitionValues);
+ }
+
+
+ // For the view in transitionValues.view, get the values you
+ // want and put them in transitionValues.values
+ private void captureValues(TransitionValues transitionValues) {
+ // Get a reference to the view
+ View view = transitionValues.view;
+ // Store its background property in the values map
+ transitionValues.values.put(PROPNAME_BACKGROUND, view.getBackground());
+ }
+ ...
+}
+</pre>
+
+
+<h3 id="EndingValues">Capture Ending Values</h3>
+
+<p>The framework calls the {@link android.transition.Transition#captureEndValues} method
+once for every target view in the ending scene. In all other respects, {@link
+android.transition.Transition#captureEndValues captureEndValues()} works the same as {@link
+android.transition.Transition#captureStartValues captureStartValues()}.</p>
+
+<p>The following code snippet shows an implementation of the {@link
+android.transition.Transition#captureEndValues captureEndValues()} method:</p>
+
+<pre>
+@Override
+public void captureEndValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+}
+</pre>
+
+<p>In this example, both the {@link android.transition.Transition#captureStartValues
+captureStartValues()} and {@link android.transition.Transition#captureEndValues captureEndValues()}
+methods invoke <code>captureValues()</code> to retrieve and store values. The view property
+that <code>captureValues()</code> retrieves is the same, but it has different values in the
+starting and ending scenes. The framework maintains separate maps for the starting and ending
+states of a view.</p>
+
+
+
+<h2 id="CreateAnimator">Create a Custom Animator</h2>
+
+<p>To animate the changes to a view between its state in the starting scene and its state in
+the ending scene, you provide an animator by overriding the {@link
+android.transition.Transition#createAnimator createAnimator()} method. When the
+framework calls this method, it passes in the scene root view and the {@link
+android.transition.TransitionValues} objects that contain the starting and ending values
+you captured.</p>
+
+<p>The number of times the framework calls the {@link
+android.transition.Transition#createAnimator createAnimator()} method depends on the changes that
+occur between the starting and ending scenes. For example, consider a fade out/fade in animation
+implemented as a custom transition. If the starting scene has five targets of which two are
+removed from the ending scene, and the ending scene has the three targets from the starting
+scene plus a new target, then the framework calls {@link
+android.transition.Transition#createAnimator createAnimator()} six times: three of the calls
+animate the fading out and fading in of the targets that stay in both scene objects; two more calls
+animate the fading out of the targets removed from the ending scene; and one call
+animates the fading in of the new target in the ending scene.</p>
+
+<p>For target views that exist on both the starting and ending scenes, the framework provides
+a {@link android.transition.TransitionValues} object for both the <code>startValues</code> and
+<code>endValues</code> arguments. For target views that only exist in the starting or the
+ending scene, the framework provides a {@link android.transition.TransitionValues} object
+for the corresponding argument and <code>null</code> for the other.</p>
+
+<p>To implement the {@link android.transition.Transition#createAnimator} method when you create
+a custom transition, use the view property values you captured to create an {@link
+android.animation.Animator} object and return it to the framework. For an example implementation,
+see the <a
+href="{@docRoot}samples/CustomTransition/src/com.example.android.customtransition/ChangeColor.html">
+<code>ChangeColor</code></a> class in the <a href="{@docRoot}samples/CustomTransition/index.html">
+CustomTransition</a> sample. For more information about property animators, see
+<a href="{@docRoot}guide/topics/graphics/prop-animation.html">Property Animation</a>.</p>
+
+
+
+<h2 id="Apply">Apply a Custom Transition</h2>
+
+<p>Custom transitions work the same as built-in transitions. You can apply a custom transition
+using a transition manager, as described in <a
+href="{@docRoot}training/transitions/transitions.html#Apply">Applying a Transition</a>.</p>
diff --git a/docs/html/training/transitions/index.jd b/docs/html/training/transitions/index.jd
new file mode 100644
index 0000000..53faa01
--- /dev/null
+++ b/docs/html/training/transitions/index.jd
@@ -0,0 +1,80 @@
+page.title=Animating Views Using Scenes and Transitions
+
+@jd:body
+
+<!-- Sidebox -->
+<div id="tb-wrapper">
+<div id="tb">
+ <h2>Dependencies and Prerequisites</h2>
+ <ul>
+ <li>Android 4.4.2 (API level 19) or higher</li>
+ </ul>
+ <h2>You should also read</h2>
+ <ul>
+ <li><a href="{@docRoot}guide/topics/ui/how-android-draws.html">
+ How Android Draws Views</a></li>
+ </ul>
+ <h2>Try it out</h2>
+ <ul>
+ <li><a href="{@docRoot}samples/BasicTransition/index.html">BasicTransition</a> sample</li>
+ <li><a href="{@docRoot}samples/CustomTransition/index.html">CustomTransition</a> sample</li>
+ </ul>
+</div>
+</div>
+
+<!-- Video box -->
+<a class="notice-developers-video wide" href="http://www.youtube.com/watch?v=S3H7nJ4QaD8">
+<div>
+ <h3>Video</h3>
+ <p>DevBytes: Android 4.4 Transitions</p>
+</div>
+</a>
+
+<p>The user interface of an activity often changes in response to user input and other events.
+For example, an activity that contains a form where users can type search queries can hide
+the form when the user submits it and show a list of search results in its place.</p>
+
+<p>To provide visual continuity in these situations, you can animate changes between
+different view hierarchies in your user interface. These animations give users feedback on
+their actions and help them learn how your app works.</p>
+
+<p>Android includes the <em>transitions framework</em>, which enables you to easily
+animate changes between two view hierarchies. The framework animates the views at runtime by
+changing some of their property values over time. The framework includes built-in animations
+for common effects and lets you create custom animations and transition lifecycle callbacks.</p>
+
+<p>This class teaches you to use the built-in animations in the transitions framework to
+animate changes between view hierarchies. This class also covers how to create custom
+animations.</p>
+
+<p class="note"><strong>Note:</strong> For Android versions earlier than 4.4.2 (API level 19)
+but greater than or equal to Android 4.0 (API level 14), use the <code>animateLayoutChanges</code>
+attribute to animate layouts. To learn more, see
+<a href="{@docRoot}guide/topics/graphics/prop-animation.html">Property Animation</a> and
+<a href="{@docRoot}training/animation/layout.html">Animating Layout Changes</a>.</p>
+
+
+<h2>Lessons</h2>
+
+<dl>
+<dt><a href="{@docRoot}training/transitions/overview.html">
+The Transitions Framework</a></dt>
+<dd>
+ Learn the main features and components of the transitions framework.
+</dd>
+<dt><a href="{@docRoot}training/transitions/scenes.html">
+Creating a Scene</a></dt>
+<dd>
+ Learn how to create a scene to store the state of a view hierarchy.
+</dd>
+<dt><a href="{@docRoot}training/transitions/transitions.html">
+Applying a Transition</a></dt>
+<dd>
+ Learn how to apply a transition between two scenes of a view hierarchy.
+</dd>
+<dt><a href="{@docRoot}training/transitions/custom-transitions.html">
+Creating Custom Transitions</a></dt>
+<dd>
+ Learn how to create other animation effects not included in the transitions framework.
+</dd>
+</dl>
diff --git a/docs/html/training/transitions/overview.jd b/docs/html/training/transitions/overview.jd
new file mode 100644
index 0000000..044cf16
--- /dev/null
+++ b/docs/html/training/transitions/overview.jd
@@ -0,0 +1,165 @@
+page.title=The Transitions Framework
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+<h2>This lesson covers</h2>
+<ol>
+ <li><a href="#Overview">Overview</a></li>
+ <li><a href="#Scenes">Scenes</a></li>
+ <li><a href="#Transitions">Transitions</a></li>
+ <li><a href="#Limitations">Limitations</a></li>
+</ol>
+</div>
+</div>
+
+<p>Animating your app's user interface provides more than just visual appeal. Animations
+highlight changes and provide visual cues that help users learn how your app works.</p>
+
+<p>To help you animate a change between one view hierarchy and another, Android provides the
+transitions framework. This framework applies one or more animations to all the views in the
+hierarchies as it changes between them.</p>
+
+<p>The framework has the following features:</p>
+
+<dl>
+<dt><em>Group-level animations</em></dt>
+<dd>Applies one or more animation effects to all of the views in a view hierarchy.</dd>
+<dt><em>Transition-based animation</em></dt>
+<dd>Runs animations based on the changes between starting and ending view property values.</dd>
+<dt><em>Built-in animations</em></dt>
+<dd>Includes predefined animations for common effects such as fade out or movement.</dd>
+
+<!-- Figure 1 - Transitions video -->
+<div style="float:right;margin-left:30px;margin-top:10px">
+<div class="framed-nexus5-port-span-5" style="clear:left;">
+<video class="play-on-hover" height="442" autoplay="" poster="">
+<source src="{@docRoot}images/transitions/transition_sample_video.mp4" type="video/mp4">
+<source src="{@docRoot}images/transitions/transition_sample_video.ogv" type="video/ogg">
+<source src="{@docRoot}images/transitions/transition_sample_video.webm" type="video/webm">
+</video>
+</div>
+<p class="img-caption" style="margin-top:7px;margin-bottom:0px">
+<strong>Figure 1.</strong> Visual cues using user interface animation.</p>
+<div style="margin-top:5px;margin-bottom:20px;font-size:10pt" class="video-instructions"> </div>
+</div>
+
+<dt><em>Resource file support</em></dt>
+<dd>Loads view hierarchies and built-in animations from layout resource files.</dd>
+<dt><em>Lifecycle callbacks</em></dt>
+<dd>Defines callbacks that provide finer control over the animation and hierarchy change
+process.</dd>
+</dl>
+
+
+
+<h2 id="Overview">Overview</h2>
+
+<p>The example in Figure 1 shows how an animation provides visual cues to help the user. As the
+app changes from its search entry screen to its search results screen, it fades out views that
+are no longer in use and fades in new views.</p>
+
+<p>This animation is an example of using the transitions framework. The framework
+animates changes to all the views in two view hierarchies. A view hierarchy can be as simple
+as a single view or as complex as a {@link android.view.ViewGroup} containing an elaborate
+tree of views. The framework animates each view by changing one or more of its property values
+over time between the initial or <em>starting</em> view hierarchy and the final or <em>ending</em>
+view hierarchy.</p>
+
+<p>The transitions framework works in parallel with view hierarchies and animations. The
+purpose of the framework is to store the state of view hierarchies, change between these
+hierarchies in order to modify the appearance of the device screen, and animate the change by
+storing and applying animation definitions.</p>
+
+<p>The diagram in Figure 2 illustrates the relationship between view hierarchies, framework
+objects, and animations:</p>
+
+<!-- Figure 2 - diagram -->
+<img src="{@docRoot}images/transitions/transitions_diagram.png"
+ width="506" height="234" alt="" style="margin-top:7px" />
+<p class="img-caption"><strong>Figure 2.</strong> Relationships in the transitions framework.</p>
+
+<p>The transitions framework provides abstractions for scenes, transitions, and transition
+managers. These are described in detail in the following sections. To use the framework, you
+create scenes for the view hierarchies in your app that you plan to change between. Next, you
+create a transition for each animation you want to use. To start the animation between two
+view hierarchies, you use a transition manager specifying the transition to use and the ending
+scene. This procedure is described in detail in the remaining lessons in this class.</p>
+
+
+
+<h2 id="Scenes">Scenes</h2>
+
+<p>A scene stores the state of a view hierarchy, including all its views and their property
+values. A view hierarchy can be a simple view or a complex tree of views and child layouts.
+Storing the view hierarchy state in a scene enables you to transition into that state from
+another scene. The framework provides the {@link android.transition.Scene} class to represent
+a scene.</p>
+
+<p>The transitions framework lets you create scenes from layout resource files or from
+{@link android.view.ViewGroup} objects in your code. Creating a scene in your code is useful
+if you generated a view hierarchy dynamically or if you are modifying it at runtime.</p>
+
+<p>In most cases, you do not create a starting scene explicitly. If you have applied a
+transition, the framework uses the previous ending scene as the starting scene for any
+subsequent transitions. If you have not applied a transition, the framework collects information
+about the views from the current state of the screen.</p>
+
+<p>A scene can also define its own actions that run when you make a scene change. For example,
+this feature is useful for cleaning up view settings after you transition to a scene.</p>
+
+<p>In addition to the view hierarchy and its property values, a scene also stores a reference
+to the parent of the view hierarchy. This root view is called a <strong>scene root</strong>.
+Changes to the scene and animations that affect the scene occur within the scene root.</p>
+
+<p>To learn how to create scenes, see
+<a href="{@docRoot}training/transitions/scenes.html">Creating a Scene</a>.</p>
+
+
+
+<h2 id="Transitions">Transitions</h2>
+
+<p>In the transitions framework, animations create a series of frames that depict a change
+between the view hierarchies in the starting and ending scenes. Information about the animation
+is stored in a {@link android.transition.Transition} object. To run the animation, you apply the
+transition using a {@link android.transition.TransitionManager} instance. The framework can
+transition between two different scenes or transition to a different state for the current
+scene.</p>
+
+<p>The framework includes a set of built-in transitions for commonly-used animation effects,
+such as fading and resizing views. You can also define your own custom transitions to create
+an animation effect using the APIs in the animations framework. The transitions framework also
+enables you to combine different animation effects in a transition set that contains a group
+of individual built-in or custom transitions.</p>
+
+<p>The transition lifecycle is similar to the activity lifecycle, and it represents the
+transition states that the framework monitors between the start and the completion of an
+animation. At important lifecycle states, the framework invokes callback methods that you can
+implement to make adjustments to your user interface at different phases of the transition.</p>
+
+<p>To learn more about transitions, see
+<a href="{@docRoot}training/transitions/transitions.html">Applying a Transition</a> and
+<a href="{@docRoot}training/transitions/custom-transitions.html">Creating Custom
+Transitions</a>.</p>
+
+
+
+<h2 id="Limitations">Limitations</h2>
+
+<p>This section lists some known limitations of the transitions framework:</p>
+
+<ul>
+<li>Animations applied to a {@link android.view.SurfaceView} may not appear correctly.
+{@link android.view.SurfaceView} instances are updated from a non-UI thread, so the updates
+may be out of sync with the animations of other views.</li>
+<li>Some specific transition types may not produce the desired animation effect when applied
+to a {@link android.view.TextureView}.</li>
+<li>Classes that extend {@link android.widget.AdapterView}, such as
+{@link android.widget.ListView}, manage their child views in ways that are incompatible with
+the transitions framework. If you try to animate a view based on
+{@link android.widget.AdapterView}, the device display may hang.</li>
+<li>If you try to resize a {@link android.widget.TextView} with an animation, the text will
+pop to a new location before the object has completely resized. To avoid this problem, do not
+animate the resizing of views that contain text.</li>
+</ul>
diff --git a/docs/html/training/transitions/scenes.jd b/docs/html/training/transitions/scenes.jd
new file mode 100644
index 0000000..4bf7d0e
--- /dev/null
+++ b/docs/html/training/transitions/scenes.jd
@@ -0,0 +1,211 @@
+page.title=Creating a Scene
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#FromLayout">Create a Scene From a Layout Resource</a></li>
+ <li><a href="#FromCode">Create a Scene in Your Code</a></li>
+ <li><a href="#Actions">Create Scene Actions</a></li>
+</ol>
+</div>
+</div>
+
+<p>Scenes store the state of a view hierarchy, including all its views and their property
+values. The transitions framework can run animations between a starting and an ending scene.
+The starting scene is often determined automatically from the current state of the user
+interface. For the ending scene, the framework enables you to create a scene from a layout
+resource file or from a group of views in your code.</p>
+
+<p>This lesson shows you how to create scenes in your app and how to define scene actions.
+The next lesson shows you how to transition between two scenes.</p>
+
+<p class="note"><strong>Note:</strong> The framework can animate changes in a single view
+hierarchy without using scenes, as described in
+<a href="{@docRoot}training/transitions/transitions.html#NoScenes">Apply a Transition Without
+Scenes</a>. However, understanding this lesson is essential to work with transitions.</p>
+
+
+
+<h2 id="FromLayout">Create a Scene From a Layout Resource</h2>
+
+<p>You can create a {@link android.transition.Scene} instance directly from a layout resource
+file. Use this technique when the view hierarchy in the file is mostly static. The resulting
+scene represents the state of the view hierarchy at the time you created the
+{@link android.transition.Scene} instance. If you change the view hierarchy, you have to
+recreate the scene. The framework creates the scene from the entire view hierarchy in the
+file; you can not create a scene from part of a layout file.</p>
+
+<p>To create a {@link android.transition.Scene} instance from a layout resource file, retrieve
+the scene root from your layout as a {@link android.view.ViewGroup} instance and then call the
+{@link android.transition.Scene#getSceneForLayout Scene.getSceneForLayout()} method with the
+scene root and the resource ID of the layout file that contains the view hierarchy for the
+scene.</p>
+
+<h3>Define Layouts for Scenes</h3>
+
+<p>The code snippets in the rest of this section show you how to create two different scenes
+with the same scene root element. The snippets also demonstrate that you can load multiple
+unrelated {@link android.transition.Scene} objects without implying that they are related to
+each other.</p>
+
+<p>The example consists of the following layout definitions:</p>
+
+<ul>
+<li>The main layout of an activity with a text label and a child layout.</li>
+<li>A relative layout for the first scene with two text fields.</li>
+<li>A relative layout for the second scene with the same two text fields in different order.</li>
+</ul>
+
+<p>The example is designed so that all of the animation occurs within the child layout of the
+main layout for the activity. The text label in the main layout remains static.</p>
+
+<p>The main layout for the activity is defined as follows:</p>
+
+<p class="code-caption">res/layout/activity_main.xml</p>
+
+<pre>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/master_layout">
+ <TextView
+ android:id="@+id/title"
+ ...
+ android:text="Title"/>
+ <FrameLayout
+ android:id="@+id/scene_root">
+ <include layout="@layout/a_scene" />
+ </FrameLayout>
+</LinearLayout>
+</pre>
+
+<p>This layout definition contains a text field and a child layout for the scene root. The
+layout for the first scene is included in the main layout file. This allows the app to display
+it as part of the initial user interface and also to load it into a scene, since the framework
+can load only a whole layout file into a scene.</p>
+
+<p>The layout for the first scene is defined as follows:</p>
+
+<p class="code-caption">res/layout/a_scene.xml</p>
+
+<pre>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/scene_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+ <TextView
+ android:id="@+id/text_view1
+ android:text="Text Line 1" />
+ <TextView
+ android:id="@+id/text_view2
+ android:text="Text Line 2" />
+</RelativeLayout>
+</pre>
+
+<p>The layout for the second scene contains the same two text fields (with the same IDs)
+placed in a different order and is defined as follows:</p>
+
+<p class="code-caption">res/layout/another_scene.xml</p>
+
+<pre>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/scene_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+ <TextView
+ android:id="@+id/text_view2
+ android:text="Text Line 2" />
+ <TextView
+ android:id="@+id/text_view1
+ android:text="Text Line 1" />
+</RelativeLayout>
+</pre>
+
+<h3>Generate Scenes from Layouts</h3>
+
+<p>After you create definitions for the two relative layouts, you can obtain an scene for
+each of them. This enables you to later transition between the two UI configurations.
+To obtain a scene, you need a reference to the scene root and the layout resource ID.</p>
+
+<p>The following code snippet shows you how to get a reference to the scene root and create
+two {@link android.transition.Scene} objects from the layout files:</p>
+
+<pre>
+Scene mAScene;
+Scene mAnotherScene;
+
+// Create the scene root for the scenes in this app
+mSceneRoot = (ViewGroup) findViewById(R.id.scene_root);
+
+// Create the scenes
+mAScene = Scene.getSceneForLayout(mSceneRoot, R.layout.a_scene, this);
+mAnotherScene =
+ Scene.getSceneForLayout(mSceneRoot, R.layout.another_scene, this);
+</pre>
+
+<p>In the app, there are now two {@link android.transition.Scene} objects based on view
+hierarchies. Both scenes use the scene root defined by the
+{@link android.widget.FrameLayout} element in <code>res/layout/activity_main.xml</code>.</p>
+
+
+
+<h2 id="FromCode">Create a Scene in Your Code</h2>
+
+<p>You can also create a {@link android.transition.Scene} instance in your code from a
+{@link android.view.ViewGroup} object. Use this technique when you modify the view hierarchies
+directly in your code or when you generate them dynamically.</p>
+
+<p>To create a scene from a view hierarchy in your code, use the
+{@link android.transition.Scene#Scene(android.view.ViewGroup, android.view.View) Scene(sceneRoot, viewHierarchy)}
+constructor. Calling this constructor is equivalent to calling the
+{@link android.transition.Scene#getSceneForLayout Scene.getSceneForLayout()} method when you
+have already inflated a layout file.</p>
+
+<p>The following code snippet demonstrates how to create a {@link android.transition.Scene}
+instance from the scene root element and the view hierarchy for the scene in your code:</p>
+
+<pre>
+Scene mScene;
+
+// Obtain the scene root element
+mSceneRoot = (ViewGroup) mSomeLayoutElement;
+
+// Obtain the view hierarchy to add as a child of
+// the scene root when this scene is entered
+mViewHierarchy = (ViewGroup) someOtherLayoutElement;
+
+// Create a scene
+mScene = new Scene(mSceneRoot, mViewHierarchy);
+</pre>
+
+
+
+<h2 id="Actions">Create Scene Actions</h2>
+
+<p>The framework enables you to define custom scene actions that the system runs when entering
+or exiting a scene. In many cases, defining custom scene actions is not necessary, since the
+framework animates the change between scenes automatically.</p>
+
+<p>Scene actions are useful for handling these cases:</p>
+
+<ul>
+<li>Animate views that are not in the same hierarchy. You can animate views for both the
+starting and ending scenes using exit and entry scene actions.</li>
+<li>Animate views that the transitions framework cannot animate automatically, such as
+{@link android.widget.ListView} objects. For more information, see
+<a href="{@docRoot}training/transitions/overview.html#Limitations">Limitations</a>.</li>
+</ul>
+
+<p>To provide custom scene actions, define your actions as {@link java.lang.Runnable} objects
+and pass them to the {@link android.transition.Scene#setExitAction Scene.setExitAction()} or
+{@link android.transition.Scene#setEnterAction Scene.setEnterAction()} methods. The framework
+calls the {@link android.transition.Scene#setExitAction setExitAction()} method on the starting
+scene before running the transition animation and the {@link
+android.transition.Scene#setEnterAction setEnterAction()} method on the ending scene after
+running the transition animation.</p>
+
+<p class="note"><strong>Note:</strong> Do not use scene actions to pass data between views in
+the starting and ending scenes. For more information, see
+<a href="{@docRoot}training/transitions/transitions.html#Callbacks">Defining Transition
+Lifecycle Callbacks</a>.</p>
diff --git a/docs/html/training/transitions/transitions.jd b/docs/html/training/transitions/transitions.jd
new file mode 100644
index 0000000..489e291
--- /dev/null
+++ b/docs/html/training/transitions/transitions.jd
@@ -0,0 +1,315 @@
+page.title=Applying a Transition
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#Create">Create a Transition</a></li>
+ <li><a href="#Apply">Apply a Transition</a></li>
+ <li><a href="#Targets">Choose Specific Target Views</a></li>
+ <li><a href="#Multiple">Specify Multiple Transitions</a></li>
+ <li><a href="#NoScenes">Apply a Transition Without Scenes</a></li>
+ <li><a href="#Callbacks">Define Transition Lifecycle Callbacks</a></li>
+</ol>
+</div>
+</div>
+
+<p>In the transitions framework, animations create a series of frames that depict a change
+between the view hierarchies in the starting and ending scenes. The framework represents
+these animations as transition objects, which contain information about an animation. To
+run an animation, you provide the transition to use and the ending scene to a transition
+manager.</p>
+
+<p>This lesson teaches you run an animation between two scenes using built-in transitions
+to move, resize, and fade views. The next lesson shows you how to define custom transitions.</p>
+
+
+
+<h2 id="Create">Create a Transition</h2>
+
+<p>In the previous lesson, you learned how to create scenes that represent the state of
+different view hierarchies. Once you have defined the starting scene and the ending scene you
+want to change between, you need to create a {@link android.transition.Transition} object
+that defines an animation. The framework enables you to specify a built-in transition in a
+resource file and inflate it in your code or to create an instance of a built-in transition
+directly in your code.</p>
+
+<!-- Built in transition table -->
+<p class="table-caption" id="table1"><strong>Table 1.</strong> Built-in transition types.</p>
+<table>
+<tr>
+ <th scope="col">Class</th>
+ <th scope="col">Tag</th>
+ <th scope="col">Attributes</th>
+ <th scope="col">Effect</th>
+</tr>
+<tr>
+ <td><code><a href="/reference/android/transition/AutoTransition.html">AutoTransition</a></code></td>
+ <td><autoTransition/></td>
+ <td style="text-align=center;"> - </td>
+ <td>Default transition. Fade out, move and resize, and fade in views, in that order.</td>
+</tr>
+<tr>
+ <td><code><a href="/reference/android/transition/Fade.html">Fade</a></code></td>
+ <td><fade/></td>
+ <td><code>android:fadingMode="[fade_in |<br> fade_out |<br> fade_in_out]"</code></td>
+ <td>
+ <code>fade_in</code> fades in views<br>
+ <code>fade_out</code> fades out views<br>
+ <code>fade_in_out</code> (default) does a <code>fade_out</code> followed by a <code>fade_in</code>.
+ </td>
+</tr>
+<tr>
+ <td><code><a href="/reference/android/transition/ChangeBounds.html">ChangeBounds</a></code></td>
+ <td><changeBounds/></td>
+ <td style="text-align=center;"> - </td>
+ <td>Moves and resizes views.</td>
+</tr>
+</table>
+
+
+<h3 id="FromFile">Create a transition instance from a resource file</h3>
+
+<p>This technique enables you to modify your transition definition without having to change
+the code of your activity. This technique is also useful to separate complex transition
+definitions from your application code, as shown in <a href="#Multiple">Specify Multiple
+Transitions</a>.</p>
+
+<p>To specify a built-in transition in a resource file, follow these steps:</p>
+
+<ol>
+<li>Add the <code>res/transition/</code> directory to your project.</li>
+<li>Create a new XML resource file inside this directory.</li>
+<li>Add an XML node for one of the built-in transitions.</li>
+</ol>
+
+<p>For example, the following resource file specifies the {@link android.transition.Fade}
+transition:</p>
+
+<p class="code-caption">res/transition/fade_transition.xml</p>
+
+<pre>
+<fade xmlns:android="http://schemas.android.com/apk/res/android" />
+</pre>
+
+<p>The following code snippet shows how to inflate a {@link android.transition.Transition}
+instance inside your activity from a resource file:</p>
+
+<pre>
+Transition mFadeTransition =
+ TransitionInflater.from(this).
+ inflateTransition(R.transition.fade_transition);
+</pre>
+
+
+<h3 id="FromCode">Create a transition instance in your code</h3>
+
+<p>This technique is useful for creating transition objects dynamically if you modify the user
+interface in your code, and to create simple built-in transition instances with few or
+no parameters.</p>
+
+<p>To create an instance of a built-in transition, invoke one of the public constructors in
+the subclasses of the {@link android.transition.Transition} class. For example, the following
+code snippet creates an instance of the {@link android.transition.Fade} transition:</p>
+
+<pre>
+Transition mFadeTransition = new Fade();
+</pre>
+
+
+
+<h2 id="Apply">Apply a Transition</h2>
+
+<p>You typically apply a transition to change between different view hierarchies in response
+to an event, such as a user action. For example, consider a search app: when the user enters
+a search term and clicks the search button, the app changes to the scene that represents the
+results layout while applying a transition that fades out the search button and fades in the
+search results.</p>
+
+<p>To make a scene change while applying a transition in response to some event in your
+activity, call the {@link android.transition.TransitionManager#go TransitionManager.go()}
+static method with the ending scene and the transition instance to use for the animation,
+as shown in the following snippet:</p>
+
+<pre>
+TransitionManager.go(mEndingScene, mFadeTransition);
+</pre>
+
+<p>The framework changes the view hierarchy inside the scene root with the view hierarchy
+from the ending scene while running the animation specified by the transition instance. The
+starting scene is the ending scene from the last transition. If there was no previous
+transition, the starting scene is determined automatically from the current state of the
+user interface.</p>
+
+<p>If you do not specify a transition instance, the transition manager can apply an automatic
+transition that does something reasonable for most situations. For more information, see the
+API reference for the {@link android.transition.TransitionManager} class.</p>
+
+
+
+<h2 id="Targets">Choose Specific Target Views</h2>
+
+<p>The framework applies transitions to all views in the starting and ending scenes by
+default. In some cases, you may only want to apply an animation to a subset of views in a
+scene. For example, the framework does not support animating changes to
+{@link android.widget.ListView} objects, so you should not try to animate them during a
+transition. The framework enables you to select specific views you want to animate.</p>
+
+<p>Each view that the transition animates is called a <em>target</em>. You can only
+select targets that are part of the view hierarchy associated with a scene.</p>
+
+<p>To remove one or more views from the list of targets, call the {@link
+android.transition.Transition#removeTarget removeTarget()} method before starting
+the transition. To add only the views you specify to the list of targets, call the
+{@link android.transition.Transition#addTarget addTarget()} method. For more
+information, see the API reference for the {@link android.transition.Transition} class.</p>
+
+
+
+<h2 id="Multiple">Specify Multiple Transitions</h2>
+
+<p>To get the most impact from an animation, you should match it to the type of changes
+that occur between the scenes. For example, if you are removing some views and adding others
+between scenes, a fade out/fade in animation provides a noticeable indication that some views
+are no longer available. If you are moving views to different points on the screen, a better
+choice would be to animate the movement so that users notice the new location of the views.</p>
+
+<p>You do not have to choose only one animation, since the transitions framework enables you
+to combine animation effects in a transition set that contains a group of individual built-in
+or custom transitions.</p>
+
+<p>To define a transition set from a collection of transitions in XML, create a resource file
+in the <code>res/transitions/</code> directory and list the transitions under the
+<code>transitionSet</code> element. For example, the following snippet shows how to specify a
+transition set that has the same behaviour as the {@link android.transition.AutoTransition}
+class:</p>
+
+<pre>
+<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
+ android:transitionOrdering="sequential">
+ <fade android:fadingMode="fade_out" />
+ <changeBounds />
+ <fade android:fadingMode="fade_in" />
+</transitionSet>
+</pre>
+
+<p>To inflate the transition set into a {@link android.transition.TransitionSet} object in
+your code, call the {@link android.transition.TransitionInflater#from TransitionInflater.from()}
+method in your activity. The {@link android.transition.TransitionSet} class extends from the
+{@link android.transition.Transition} class, so you can use it with a transition manager just
+like any other {@link android.transition.Transition} instance.</p>
+
+
+
+<h2 id="NoScenes">Apply a Transition Without Scenes</h2>
+
+<p>Changing view hierarchies is not the only way to modify your user interface. You can also
+make changes by adding, modifying, and removing child views within the current hierarchy. For
+example, you can implement a search interaction with just a single layout. Start with the
+layout showing a search entry field and a search icon. To change the user interface to show
+the results, remove the search button when the user clicks it by calling the {@link
+android.view.ViewGroup#removeView ViewGroup.removeView()} method, and add the search results by
+calling {@link android.view.ViewGroup#addView ViewGroup.addView()} method.</p>
+
+<p>You may want to use this approach if the alternative is to have two hierarchies that are
+nearly identical. Rather than having to create and maintain two separate layout files for a
+minor difference in the user interface, you can have one layout file containing a view
+hierarchy that you modify in code.</p>
+
+<p>If you make changes within the current view hierarchy in this fashion, you do not need to
+create a scene. Instead, you can create and apply a transition between two states of a view
+hierarchy using a <em>delayed transition</em>. This feature of the transitions framework
+starts with the current view hierarchy state, records changes you make to its views, and applies
+a transition that animates the changes when the system redraws the user interface.</p>
+
+<p>To create a delayed transition within a single view hierarchy, follow these steps:</p>
+
+<ol>
+<li>When the event that triggers the transition occurs, call the {@link
+android.transition.TransitionManager#beginDelayedTransition
+TransitionManager.beginDelayedTransition()} method providing the parent view of all the views
+you want to change and the transition to use. The framework stores the current state of the
+child views and their property values.</li>
+<li>Make changes to the child views as required by your use case. The framework records
+the changes you make to the child views and their properties.</li>
+<li>When the system redraws the user interface according to your changes, the framework
+animates the changes between the original state and the new state.</li>
+</ol>
+
+<p>The following example shows how to animate the addition of a text view to a view hierarchy
+using a delayed transition. The first snippet shows the layout definition file:</p>
+
+<p class="code-caption">res/layout/activity_main.xml</p>
+
+<pre>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/mainLayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+ <EditText
+ android:id="@+id/inputText"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ ...
+</RelativeLayout>
+</pre>
+
+<p>The next snippet shows the code that animates the addition of the text view:</p>
+
+<p class="code-caption">MainActivity.java</p>
+
+<pre>
+private TextView mLabelText;
+private Fade mFade;
+private ViewGroup mRootView;
+...
+
+// Load the layout
+this.setContentView(R.layout.activity_main);
+...
+
+// Create a new TextView and set some View properties
+mLabelText = new TextView();
+mLabelText.setText("Label").setId("1");
+
+// Get the root view and create a transition
+mRootView = (ViewGroup) findViewById(R.id.mainLayout);
+mFade = new Fade(IN);
+
+// Start recording changes to the view hierarchy
+TransitionManager.beginDelayedTransition(mRootView, mFade);
+
+// Add the new TextView to the view hierarchy
+mRootView.addView(mLabelText);
+
+// When the system redraws the screen to show this update,
+// the framework will animate the addition as a fade in
+</pre>
+
+
+
+<h2 id="Callbacks">Define Transition Lifecycle Callbacks</h2>
+
+<p>The transition lifecycle is similar to the activity lifecycle. It represents the transition
+states that the framework monitors during the time between a call to the {@link
+android.transition.TransitionManager#go TransitionManager.go()} method and the completion of
+the animation. At important lifecycle states, the framework invokes callbacks defined by
+the {@link android.transition.Transition.TransitionListener TransitionListener}
+interface.</p>
+
+<p>Transition lifecycle callbacks are useful, for example, for copying a view property value
+from the starting view hierarchy to the ending view hierarchy during a scene change. You
+cannot simply copy the value from its starting view to the view in the ending view hierarchy,
+because the ending view hierarchy is not inflated until the transition is completed.
+Instead, you need to store the value in a variable and then copy it into the ending view
+hierarchy when the framework has finished the transition. To get notified when the transition
+is completed, you can implement the {@link
+android.transition.Transition.TransitionListener#onTransitionEnd
+TransitionListener.onTransitionEnd()} method in your activity.</p>
+
+<p>For more information, see the API reference for the {@link
+android.transition.Transition.TransitionListener TransitionListener} class.</p>
diff --git a/docs/html/training/tv/playback/card.jd b/docs/html/training/tv/playback/card.jd
new file mode 100644
index 0000000..8ac75fd
--- /dev/null
+++ b/docs/html/training/tv/playback/card.jd
@@ -0,0 +1,156 @@
+page.title=Providing a Card View
+page.tags="card"
+
+trainingnavtop=true
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+ <h2>This lesson teaches you to</h2>
+ <ol>
+ <li><a href="#presenter">Create a Card Presenter</a></li>
+ <li><a href="#card-view">Create a Card View</a></li>
+ </ol>
+ <h2>Try it out</h2>
+ <ul>
+ <li><a class="external-link" href="https://github.com/googlesamples/androidtv-Leanback">Android
+ Leanback sample app</a></li>
+ </ul>
+</div>
+</div>
+
+<p>In the previous lesson, you created a catalog browser, implemented in a browse fragment, that
+displays a list of media items. In this lesson, you create the card views for your media items and
+present them in the browse fragment.</p>
+
+<p>The {@link android.support.v17.leanback.widget.BaseCardView} class and subclasses display the meta
+data associated with a media item. The {@link android.support.v17.leanback.widget.ImageCardView}
+class used in this lesson displays an image for the content along with the media item's title.</p>
+
+<p>This lesson describes code from the <a href="https://github.com/googlesamples/androidtv-Leanback">
+Android Leanback sample app</a>, available on GitHub. Use this sample code to start your own
+app.</p>
+
+<img itemprop="image" src="{@docRoot}images/tv/app-browse.png" alt="App main screen"/>
+<p class="img-caption"><b>Figure 1.</b> The <a href="https://github.com/googlesamples/androidtv-Leanback">
+Leanback sample app</a> browse fragment with a card presenter displaying card view objects.</p>
+
+<h2 id="presenter">Create a Card Presenter</h2>
+
+<p>A {@link android.support.v17.leanback.widget.Presenter} generates views and binds objects to them
+on demand. In the browse fragment where your app presents its content to the user, you create a
+{@link android.support.v17.leanback.widget.Presenter} for the content cards and pass it to the adapter
+that adds the content to the screen. In the following code, the <code>CardPresenter</code> is created
+in the {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished(android.support.v4.content.Loader, java.lang.Object) onLoadFinished()}
+callback of the {@link android.support.v4.app.LoaderManager}.</p>
+
+<pre>
+@Override
+public void onLoadFinished(Loader<HashMap<String, List<Movie>>> arg0,
+ HashMap<String, List<Movie>> data) {
+
+ mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
+ CardPresenter cardPresenter = new CardPresenter();
+
+ int i = 0;
+
+ for (Map.Entry<String, List<Movie>> entry : data.entrySet()) {
+ ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
+ List<Movie> list = entry.getValue();
+
+ for (int j = 0; j < list.size(); j++) {
+ listRowAdapter.add(list.get(j));
+ }
+ HeaderItem header = new HeaderItem(i, entry.getKey(), null);
+ i++;
+ mRowsAdapter.add(new ListRow(header, listRowAdapter));
+ }
+
+ HeaderItem gridHeader = new HeaderItem(i, getString(R.string.more_samples),
+ null);
+
+ GridItemPresenter gridPresenter = new GridItemPresenter();
+ ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(gridPresenter);
+ gridRowAdapter.add(getString(R.string.grid_view));
+ gridRowAdapter.add(getString(R.string.error_fragment));
+ gridRowAdapter.add(getString(R.string.personal_settings));
+ mRowsAdapter.add(new ListRow(gridHeader, gridRowAdapter));
+
+ setAdapter(mRowsAdapter);
+
+ updateRecommendations();
+}
+</pre>
+
+<h2 id="card-view">Create a Card View</h2>
+
+<p>In this step, you build the card presenter with a view holder for the card view that describes
+your media content items. Note that each presenter must only create one view type. If you have two
+different card view types then you need two different card presenters.</p>
+
+<p>In the {@link android.support.v17.leanback.widget.Presenter}, implement an
+{@link android.support.v17.leanback.widget.Presenter#onCreateViewHolder(android.view.ViewGroup) onCreateViewHolder()}
+callback that creates a view holder that can be used to display a content item.</p>
+
+<pre>
+@Override
+public class CardPresenter extends Presenter {
+
+ private Context mContext;
+ private static int CARD_WIDTH = 313;
+ private static int CARD_HEIGHT = 176;
+ private Drawable mDefaultCardImage;
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent) {
+ mContext = parent.getContext();
+ mDefaultCardImage = mContext.getResources().getDrawable(R.drawable.movie);
+...
+</pre>
+
+<p>In the {@link android.support.v17.leanback.widget.Presenter#onCreateViewHolder(android.view.ViewGroup)
+onCreateViewHolder()} method, create a card view for content items. The sample below uses an
+{@link android.support.v17.leanback.widget.ImageCardView}.</p>
+
+<p>When a card is selected, the default behavior expands it to a larger size. If you want to designate
+a different color for the selected card, call {@link android.support.v17.leanback.widget.BaseCardView#setSelected(boolean)
+setSelected()}
+as shown here.</p>
+
+<pre>
+...
+ ImageCardView cardView = new ImageCardView(mContext) {
+ @Override
+ public void setSelected(boolean selected) {
+ int selected_background = mContext.getResources().getColor(R.color.detail_background);
+ int default_background = mContext.getResources().getColor(R.color.default_background);
+ int color = selected ? selected_background : default_background;
+ findViewById(R.id.info_field).setBackgroundColor(color);
+ super.setSelected(selected);
+ }
+ };
+...
+</pre>
+
+<p>When the user opens your app, the {@link android.support.v17.leanback.widget.Presenter.ViewHolder}
+displays the <code>CardView</code> objects for your content items. You need to set these to receive
+focus from the D-pad controller by calling {@link android.view.View#setFocusable(boolean) setFocusable(true)}
+and {@link android.view.View#setFocusableInTouchMode(boolean) setFocusableInTouchMode(true)}.</p>
+
+<pre>
+...
+ cardView.setFocusable(true);
+ cardView.setFocusableInTouchMode(true);
+ return new ViewHolder(cardView);
+}
+</pre>
+
+<p>When the user selects the {@link android.support.v17.leanback.widget.ImageCardView}, it expands
+to reveal its text area with the background color you specify, as shown in figure 2.</p>
+
+<img itemprop="image" src="{@docRoot}images/tv/card-view.png" alt="App card view"/>
+<p class="img-caption"><b>Figure 2.</b> The <a href="https://github.com/googlesamples/androidtv-Leanback">
+Leanback sample app</a> image card view when selected.</p>
+
+
diff --git a/docs/html/training/tv/playback/index.jd b/docs/html/training/tv/playback/index.jd
index 5427d48..0e9c5ec 100644
--- a/docs/html/training/tv/playback/index.jd
+++ b/docs/html/training/tv/playback/index.jd
@@ -56,6 +56,9 @@
<dd>Learn how to use the Leanback support library to build a browsing interface for media
catalogs.</dd>
+ <dt><b><a href="details.html">Providing a Card View</a></b></dt>
+ <dd>Learn how to use the Leanback support library to build a card view for content items.</dd>
+
<dt><b><a href="details.html">Building a Details View</a></b></dt>
<dd>Learn how to use the Leanback support library to build a details page for media items.</dd>
diff --git a/docs/html/training/tv/start/layouts.jd b/docs/html/training/tv/start/layouts.jd
index 177ea7a..a378096 100644
--- a/docs/html/training/tv/start/layouts.jd
+++ b/docs/html/training/tv/start/layouts.jd
@@ -119,8 +119,8 @@
<p>
Avoid screen elements being clipped due to overscan and by incorporating a 10% margin
- on all sides of your layout. This translates into a 27dp margin on the left and right edges and
- a 48dp margin on the top and bottom of your base layouts for activities. The following
+ on all sides of your layout. This translates into a 48dp margin on the left and right edges and
+ a 27dp margin on the top and bottom of your base layouts for activities. The following
example layout demonstrates how to set these margins in the root layout for a TV app:
</p>
diff --git a/graphics/java/android/graphics/AvoidXfermode.java b/graphics/java/android/graphics/AvoidXfermode.java
index 206c959..48ee6fa 100644
--- a/graphics/java/android/graphics/AvoidXfermode.java
+++ b/graphics/java/android/graphics/AvoidXfermode.java
@@ -23,7 +23,7 @@
@Deprecated
public class AvoidXfermode extends Xfermode {
- // these need to match the enum in SkAvoidXfermode.h on the native side
+ // these need to match the enum in AvoidXfermode.h on the native side
public enum Mode {
AVOID (0), //!< draw everywhere except on the opColor
TARGET (1); //!< draw only on top of the opColor
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index fa9af2a..39272b9 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -122,11 +122,6 @@
* @param canvas The picture is drawn to this canvas
*/
public void draw(Canvas canvas) {
- if (canvas.isHardwareAccelerated()) {
- throw new IllegalArgumentException(
- "Picture playback is only supported on software canvas.");
- }
-
if (mRecordingCanvas != null) {
endRecording();
}
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index 8b70a08..532ee5d 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -34,7 +34,6 @@
import android.graphics.Outline;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff.Mode;
-import android.graphics.drawable.Drawable.ConstantState;
import android.graphics.Rect;
import android.util.AttributeSet;
@@ -60,28 +59,49 @@
public class InsetDrawable extends Drawable implements Drawable.Callback {
private final Rect mTmpRect = new Rect();
- private final InsetState mState;
+ private InsetState mState;
+ private Drawable mDrawable;
private boolean mMutated;
- /*package*/ InsetDrawable() {
- this(null, null);
+ /**
+ * No-arg constructor used by drawable inflation.
+ */
+ InsetDrawable() {
+ this(new InsetState(), null);
}
+ /**
+ * Creates a new inset drawable with the specified inset.
+ *
+ * @param drawable The drawable to inset.
+ * @param inset Inset in pixels around the drawable.
+ */
public InsetDrawable(Drawable drawable, int inset) {
this(drawable, inset, inset, inset, inset);
}
- public InsetDrawable(Drawable drawable, int insetLeft, int insetTop,
- int insetRight, int insetBottom) {
- this(null, null);
+ /**
+ * Creates a new inset drawable with the specified insets.
+ *
+ * @param drawable The drawable to inset.
+ * @param insetLeft Left inset in pixels.
+ * @param insetTop Top inset in pixels.
+ * @param insetRight Right inset in pixels.
+ * @param insetBottom Bottom inset in pixels.
+ */
+ public InsetDrawable(Drawable drawable, int insetLeft, int insetTop,int insetRight,
+ int insetBottom) {
+ this(new InsetState(), null);
- mState.mDrawable = drawable;
+ mState.mDrawableState = drawable == null ? null : drawable.getConstantState();
mState.mInsetLeft = insetLeft;
mState.mInsetTop = insetTop;
mState.mInsetRight = insetRight;
mState.mInsetBottom = insetBottom;
+ mDrawable = drawable;
+
if (drawable != null) {
drawable.setCallback(this);
}
@@ -93,10 +113,6 @@
final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.InsetDrawable);
super.inflateWithAttributes(r, parser, a, R.styleable.InsetDrawable_visible);
- // Reset mDrawable to preserve old multiple-inflate behavior. This is
- // silly, but we have CTS tests that rely on it.
- mState.mDrawable = null;
-
updateStateFromTypedArray(a);
inflateChildElements(r, parser, attrs, theme);
verifyRequiredAttributes(a);
@@ -106,25 +122,26 @@
private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs,
Theme theme) throws XmlPullParserException, IOException {
// Load inner XML elements.
- if (mState.mDrawable == null) {
+ if (mDrawable == null) {
int type;
while ((type=parser.next()) == XmlPullParser.TEXT) {
}
if (type != XmlPullParser.START_TAG) {
- throw new XmlPullParserException(
- parser.getPositionDescription()
- + ": <inset> tag requires a 'drawable' attribute or "
- + "child tag defining a drawable");
+ throw new XmlPullParserException(parser.getPositionDescription()
+ + ": <inset> tag requires a 'drawable' attribute or "
+ + "child tag defining a drawable");
}
+
final Drawable dr = Drawable.createFromXmlInner(r, parser, attrs, theme);
- mState.mDrawable = dr;
+ mState.mDrawableState = dr.getConstantState();
+ mDrawable = dr;
dr.setCallback(this);
}
}
private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
// If we're not waiting on a theme, verify required attributes.
- if (mState.mDrawable == null && (mState.mThemeAttrs == null
+ if (mDrawable == null && (mState.mThemeAttrs == null
|| mState.mThemeAttrs[R.styleable.InsetDrawable_drawable] == 0)) {
throw new XmlPullParserException(a.getPositionDescription()
+ ": <inset> tag requires a 'drawable' attribute or "
@@ -148,7 +165,8 @@
case R.styleable.InsetDrawable_drawable:
final Drawable dr = a.getDrawable(attr);
if (dr != null) {
- state.mDrawable = dr;
+ mState.mDrawableState = dr.getConstantState();
+ mDrawable = dr;
dr.setCallback(this);
}
break;
@@ -198,8 +216,8 @@
}
}
- if (state.mDrawable != null && state.mDrawable.canApplyTheme()) {
- state.mDrawable.applyTheme(t);
+ if (mDrawable != null && mDrawable.canApplyTheme()) {
+ mDrawable.applyTheme(t);
}
}
@@ -234,27 +252,27 @@
@Override
public void draw(Canvas canvas) {
- mState.mDrawable.draw(canvas);
+ mDrawable.draw(canvas);
}
@Override
public int getChangingConfigurations() {
return super.getChangingConfigurations()
| mState.mChangingConfigurations
- | mState.mDrawable.getChangingConfigurations();
+ | mDrawable.getChangingConfigurations();
}
@Override
public boolean getPadding(Rect padding) {
- boolean pad = mState.mDrawable.getPadding(padding);
+ final boolean pad = mDrawable.getPadding(padding);
padding.left += mState.mInsetLeft;
padding.right += mState.mInsetRight;
padding.top += mState.mInsetTop;
padding.bottom += mState.mInsetBottom;
- return pad || (mState.mInsetLeft | mState.mInsetRight |
- mState.mInsetTop | mState.mInsetBottom) != 0;
+ return pad || (mState.mInsetLeft | mState.mInsetRight
+ | mState.mInsetTop | mState.mInsetBottom) != 0;
}
/** @hide */
@@ -269,61 +287,61 @@
@Override
public void setHotspot(float x, float y) {
- mState.mDrawable.setHotspot(x, y);
+ mDrawable.setHotspot(x, y);
}
@Override
public void setHotspotBounds(int left, int top, int right, int bottom) {
- mState.mDrawable.setHotspotBounds(left, top, right, bottom);
+ mDrawable.setHotspotBounds(left, top, right, bottom);
}
/** @hide */
@Override
public void getHotspotBounds(Rect outRect) {
- mState.mDrawable.getHotspotBounds(outRect);
+ mDrawable.getHotspotBounds(outRect);
}
@Override
public boolean setVisible(boolean visible, boolean restart) {
- mState.mDrawable.setVisible(visible, restart);
+ mDrawable.setVisible(visible, restart);
return super.setVisible(visible, restart);
}
@Override
public void setAlpha(int alpha) {
- mState.mDrawable.setAlpha(alpha);
+ mDrawable.setAlpha(alpha);
}
@Override
public int getAlpha() {
- return mState.mDrawable.getAlpha();
+ return mDrawable.getAlpha();
}
@Override
public void setColorFilter(ColorFilter cf) {
- mState.mDrawable.setColorFilter(cf);
+ mDrawable.setColorFilter(cf);
}
@Override
public void setTintList(ColorStateList tint) {
- mState.mDrawable.setTintList(tint);
+ mDrawable.setTintList(tint);
}
@Override
public void setTintMode(Mode tintMode) {
- mState.mDrawable.setTintMode(tintMode);
+ mDrawable.setTintMode(tintMode);
}
/** {@hide} */
@Override
public void setLayoutDirection(int layoutDirection) {
- mState.mDrawable.setLayoutDirection(layoutDirection);
+ mDrawable.setLayoutDirection(layoutDirection);
}
@Override
public int getOpacity() {
final InsetState state = mState;
- final int opacity = state.mDrawable.getOpacity();
+ final int opacity = mDrawable.getOpacity();
if (opacity == PixelFormat.OPAQUE && (state.mInsetLeft > 0 || state.mInsetTop > 0
|| state.mInsetRight > 0 || state.mInsetBottom > 0)) {
return PixelFormat.TRANSLUCENT;
@@ -333,19 +351,19 @@
@Override
public boolean isStateful() {
- return mState.mDrawable.isStateful();
+ return mDrawable.isStateful();
}
@Override
protected boolean onStateChange(int[] state) {
- boolean changed = mState.mDrawable.setState(state);
+ final boolean changed = mDrawable.setState(state);
onBoundsChange(getBounds());
return changed;
}
@Override
protected boolean onLevelChange(int level) {
- return mState.mDrawable.setLevel(level);
+ return mDrawable.setLevel(level);
}
@Override
@@ -358,24 +376,22 @@
r.right -= mState.mInsetRight;
r.bottom -= mState.mInsetBottom;
- mState.mDrawable.setBounds(r.left, r.top, r.right, r.bottom);
+ mDrawable.setBounds(r.left, r.top, r.right, r.bottom);
}
@Override
public int getIntrinsicWidth() {
- return mState.mDrawable.getIntrinsicWidth()
- + mState.mInsetLeft + mState.mInsetRight;
+ return mDrawable.getIntrinsicWidth() + mState.mInsetLeft + mState.mInsetRight;
}
@Override
public int getIntrinsicHeight() {
- return mState.mDrawable.getIntrinsicHeight()
- + mState.mInsetTop + mState.mInsetBottom;
+ return mDrawable.getIntrinsicHeight() + mState.mInsetTop + mState.mInsetBottom;
}
@Override
public void getOutline(@NonNull Outline outline) {
- mState.mDrawable.getOutline(outline);
+ mDrawable.getOutline(outline);
}
@Override
@@ -390,7 +406,9 @@
@Override
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
- mState.mDrawable.mutate();
+ mState = new InsetState(mState);
+ mDrawable.mutate();
+ mState.mDrawableState = mDrawable.getConstantState();
mMutated = true;
}
return this;
@@ -401,7 +419,7 @@
*/
public void clearMutated() {
super.clearMutated();
- mState.mDrawable.clearMutated();
+ mDrawable.clearMutated();
mMutated = false;
}
@@ -409,51 +427,53 @@
* Returns the drawable wrapped by this InsetDrawable. May be null.
*/
public Drawable getDrawable() {
- return mState.mDrawable;
+ return mDrawable;
}
- final static class InsetState extends ConstantState {
+ private static final class InsetState extends ConstantState {
int[] mThemeAttrs;
int mChangingConfigurations;
- Drawable mDrawable;
+ ConstantState mDrawableState;
int mInsetLeft = 0;
int mInsetTop = 0;
int mInsetRight = 0;
int mInsetBottom = 0;
- private boolean mCheckedConstantState;
- private boolean mCanConstantState;
+ public InsetState() {
+ // Empty constructor.
+ }
- InsetState(InsetState orig, InsetDrawable owner, Resources res) {
+ public InsetState(InsetState orig) {
if (orig != null) {
mThemeAttrs = orig.mThemeAttrs;
mChangingConfigurations = orig.mChangingConfigurations;
- if (res != null) {
- mDrawable = orig.mDrawable.getConstantState().newDrawable(res);
- } else {
- mDrawable = orig.mDrawable.getConstantState().newDrawable();
- }
- mDrawable.setCallback(owner);
- mDrawable.setLayoutDirection(orig.mDrawable.getLayoutDirection());
- mDrawable.setBounds(orig.mDrawable.getBounds());
- mDrawable.setLevel(orig.mDrawable.getLevel());
+ mDrawableState = orig.mDrawableState;
mInsetLeft = orig.mInsetLeft;
mInsetTop = orig.mInsetTop;
mInsetRight = orig.mInsetRight;
mInsetBottom = orig.mInsetBottom;
- mCheckedConstantState = mCanConstantState = true;
}
}
@Override
public boolean canApplyTheme() {
- return mThemeAttrs != null || (mDrawable != null && mDrawable.canApplyTheme())
+ return mThemeAttrs != null
+ || (mDrawableState != null && mDrawableState.canApplyTheme())
|| super.canApplyTheme();
}
@Override
+ public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
+ final ConstantState state = mDrawableState;
+ if (state != null) {
+ return state.addAtlasableBitmaps(atlasList);
+ }
+ return 0;
+ }
+
+ @Override
public Drawable newDrawable() {
return new InsetDrawable(this, null);
}
@@ -468,27 +488,30 @@
return mChangingConfigurations;
}
- boolean canConstantState() {
- if (!mCheckedConstantState) {
- mCanConstantState = mDrawable.getConstantState() != null;
- mCheckedConstantState = true;
- }
-
- return mCanConstantState;
- }
-
- @Override
- public int addAtlasableBitmaps(Collection<Bitmap> atlasList) {
- final ConstantState state = mDrawable.getConstantState();
- if (state != null) {
- return state.addAtlasableBitmaps(atlasList);
- }
- return 0;
+ public boolean canConstantState() {
+ return mDrawableState != null;
}
}
+ /**
+ * The one constructor to rule them all. This is called by all public
+ * constructors to set the state and initialize local properties.
+ */
private InsetDrawable(InsetState state, Resources res) {
- mState = new InsetState(state, this, res);
+ mState = state;
+
+ updateLocalState(res);
+ }
+
+ /**
+ * Initializes local dynamic properties from state. This should be called
+ * after significant state changes, e.g. from the One True Constructor and
+ * after inflating or applying a theme.
+ */
+ private void updateLocalState(Resources res) {
+ if (mState.mDrawableState != null) {
+ mDrawable = mState.mDrawableState.newDrawable(res);
+ }
}
}
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 421c5c1..8673219 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -118,9 +118,9 @@
yDivsOffset(0), colorsOffset(0) { }
int8_t wasDeserialized;
- int8_t numXDivs;
- int8_t numYDivs;
- int8_t numColors;
+ uint8_t numXDivs;
+ uint8_t numYDivs;
+ uint8_t numColors;
// The offset (from the start of this structure) to the xDivs & yDivs
// array for this 9patch. To get a pointer to this array, call
diff --git a/libs/hwui/Android.common.mk b/libs/hwui/Android.common.mk
index b1673fe..a05217f 100644
--- a/libs/hwui/Android.common.mk
+++ b/libs/hwui/Android.common.mk
@@ -5,12 +5,26 @@
-Wno-gnu-static-float-init
LOCAL_SRC_FILES := \
+ font/CacheTexture.cpp \
+ font/Font.cpp \
+ renderstate/Blend.cpp \
+ renderstate/MeshState.cpp \
+ renderstate/PixelBufferState.cpp \
+ renderstate/RenderState.cpp \
+ renderstate/Scissor.cpp \
+ renderstate/Stencil.cpp \
+ renderstate/TextureState.cpp \
+ renderthread/CanvasContext.cpp \
+ renderthread/DrawFrameTask.cpp \
+ renderthread/EglManager.cpp \
+ renderthread/RenderProxy.cpp \
+ renderthread/RenderTask.cpp \
+ renderthread/RenderThread.cpp \
+ renderthread/TimeLord.cpp \
+ thread/TaskManager.cpp \
utils/Blur.cpp \
utils/GLUtils.cpp \
utils/SortedListImpl.cpp \
- thread/TaskManager.cpp \
- font/CacheTexture.cpp \
- font/Font.cpp \
AmbientShadow.cpp \
AnimationContext.cpp \
Animator.cpp \
@@ -48,29 +62,18 @@
RenderBufferCache.cpp \
RenderNode.cpp \
RenderProperties.cpp \
- RenderState.cpp \
ResourceCache.cpp \
ShadowTessellator.cpp \
SkiaCanvas.cpp \
+ SkiaCanvasProxy.cpp \
SkiaShader.cpp \
Snapshot.cpp \
SpotShadow.cpp \
- Stencil.cpp \
TessellationCache.cpp \
Texture.cpp \
TextureCache.cpp \
TextDropShadowCache.cpp
-# RenderThread stuff
-LOCAL_SRC_FILES += \
- renderthread/CanvasContext.cpp \
- renderthread/DrawFrameTask.cpp \
- renderthread/EglManager.cpp \
- renderthread/RenderProxy.cpp \
- renderthread/RenderTask.cpp \
- renderthread/RenderThread.cpp \
- renderthread/TimeLord.cpp
-
intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,)
LOCAL_C_INCLUDES += \
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 107eb08..cef2c84 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -16,24 +16,23 @@
#define LOG_TAG "OpenGLRenderer"
+#include "Caches.h"
+
+#include "DisplayListRenderer.h"
+#include "GammaFontRenderer.h"
+#include "LayerRenderer.h"
+#include "Properties.h"
+#include "renderstate/RenderState.h"
+#include "ShadowTessellator.h"
+
#include <utils/Log.h>
#include <utils/String8.h>
-#include "Caches.h"
-#include "DisplayListRenderer.h"
-#include "GammaFontRenderer.h"
-#include "Properties.h"
-#include "LayerRenderer.h"
-#include "ShadowTessellator.h"
-#include "RenderState.h"
-
namespace android {
-
-using namespace uirenderer;
-ANDROID_SINGLETON_STATIC_INSTANCE(Caches);
-
namespace uirenderer {
+Caches* Caches::sInstance = nullptr;
+
///////////////////////////////////////////////////////////////////////////////
// Macros
///////////////////////////////////////////////////////////////////////////////
@@ -48,8 +47,13 @@
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
-Caches::Caches(): Singleton<Caches>(),
- mExtensions(Extensions::getInstance()), mInitialized(false), mRenderState(nullptr) {
+Caches::Caches(RenderState& renderState)
+ : patchCache(renderState)
+ , dither(*this)
+ , mRenderState(&renderState)
+ , mExtensions(Extensions::getInstance())
+ , mInitialized(false) {
+ INIT_LOGD("Creating OpenGL renderer caches");
init();
initFont();
initConstraints();
@@ -59,7 +63,8 @@
initTempProperties();
mDebugLevel = readDebugLevel();
- ALOGD("Enabling debug mode %d", mDebugLevel);
+ ALOGD_IF(mDebugLevel != kDebugDisabled,
+ "Enabling debug mode %d", mDebugLevel);
}
bool Caches::init() {
@@ -67,32 +72,8 @@
ATRACE_NAME("Caches::init");
- glGenBuffers(1, &meshBuffer);
- glBindBuffer(GL_ARRAY_BUFFER, meshBuffer);
- glBufferData(GL_ARRAY_BUFFER, sizeof(gMeshVertices), gMeshVertices, GL_STATIC_DRAW);
-
- mCurrentBuffer = meshBuffer;
- mCurrentIndicesBuffer = 0;
- mCurrentPositionPointer = this;
- mCurrentPositionStride = 0;
- mCurrentTexCoordsPointer = this;
- mCurrentPixelBuffer = 0;
-
- mTexCoordsArrayEnabled = false;
-
- glDisable(GL_SCISSOR_TEST);
- scissorEnabled = false;
- mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0;
-
- glActiveTexture(gTextureUnits[0]);
- mTextureUnit = 0;
mRegionMesh = nullptr;
- mMeshIndices = 0;
- mShadowStripsIndices = 0;
- blend = false;
- lastSrcMode = GL_ZERO;
- lastDstMode = GL_ZERO;
currentProgram = nullptr;
mFunctorsCount = 0;
@@ -101,11 +82,12 @@
debugOverdraw = false;
debugStencilClip = kStencilHide;
- patchCache.init(*this);
+ patchCache.init();
mInitialized = true;
- resetBoundTextures();
+ mPixelBufferState = new PixelBufferState();
+ mTextureState = new TextureState();
return true;
}
@@ -136,12 +118,6 @@
}
void Caches::initConstraints() {
- GLint maxTextureUnits;
- glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
- if (maxTextureUnits < REQUIRED_TEXTURE_UNITS_COUNT) {
- ALOGW("At least %d texture units are required!", REQUIRED_TEXTURE_UNITS_COUNT);
- }
-
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
}
@@ -219,17 +195,8 @@
void Caches::terminate() {
if (!mInitialized) return;
-
- glDeleteBuffers(1, &meshBuffer);
- mCurrentBuffer = 0;
-
- glDeleteBuffers(1, &mMeshIndices);
- mMeshIndices = 0;
mRegionMesh.release();
- glDeleteBuffers(1, &mShadowStripsIndices);
- mShadowStripsIndices = 0;
-
fboCache.clear();
programCache.clear();
@@ -239,6 +206,10 @@
clearGarbage();
+ delete mPixelBufferState;
+ mPixelBufferState = nullptr;
+ delete mTextureState;
+ mTextureState = nullptr;
mInitialized = false;
}
@@ -271,7 +242,7 @@
layerCache.getSize(), layerCache.getMaxSize(), layerCache.getCount());
if (mRenderState) {
int memused = 0;
- for (std::set<const Layer*>::iterator it = mRenderState->mActiveLayers.begin();
+ for (std::set<Layer*>::iterator it = mRenderState->mActiveLayers.begin();
it != mRenderState->mActiveLayers.end(); it++) {
const Layer* layer = *it;
log.appendFormat(" Layer size %dx%d; isTextureLayer()=%d; texid=%u fbo=%u; refs=%d\n",
@@ -369,281 +340,6 @@
}
///////////////////////////////////////////////////////////////////////////////
-// VBO
-///////////////////////////////////////////////////////////////////////////////
-
-bool Caches::bindMeshBuffer() {
- return bindMeshBuffer(meshBuffer);
-}
-
-bool Caches::bindMeshBuffer(const GLuint buffer) {
- if (mCurrentBuffer != buffer) {
- glBindBuffer(GL_ARRAY_BUFFER, buffer);
- mCurrentBuffer = buffer;
- return true;
- }
- return false;
-}
-
-bool Caches::unbindMeshBuffer() {
- if (mCurrentBuffer) {
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- mCurrentBuffer = 0;
- return true;
- }
- return false;
-}
-
-bool Caches::bindIndicesBufferInternal(const GLuint buffer) {
- if (mCurrentIndicesBuffer != buffer) {
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
- mCurrentIndicesBuffer = buffer;
- return true;
- }
- return false;
-}
-
-bool Caches::bindQuadIndicesBuffer() {
- if (!mMeshIndices) {
- std::unique_ptr<uint16_t[]> regionIndices(new uint16_t[gMaxNumberOfQuads * 6]);
- for (uint32_t i = 0; i < gMaxNumberOfQuads; i++) {
- uint16_t quad = i * 4;
- int index = i * 6;
- regionIndices[index ] = quad; // top-left
- regionIndices[index + 1] = quad + 1; // top-right
- regionIndices[index + 2] = quad + 2; // bottom-left
- regionIndices[index + 3] = quad + 2; // bottom-left
- regionIndices[index + 4] = quad + 1; // top-right
- regionIndices[index + 5] = quad + 3; // bottom-right
- }
-
- glGenBuffers(1, &mMeshIndices);
- bool force = bindIndicesBufferInternal(mMeshIndices);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, gMaxNumberOfQuads * 6 * sizeof(uint16_t),
- regionIndices.get(), GL_STATIC_DRAW);
- return force;
- }
-
- return bindIndicesBufferInternal(mMeshIndices);
-}
-
-bool Caches::bindShadowIndicesBuffer() {
- if (!mShadowStripsIndices) {
- std::unique_ptr<uint16_t[]> shadowIndices(new uint16_t[MAX_SHADOW_INDEX_COUNT]);
- ShadowTessellator::generateShadowIndices(shadowIndices.get());
- glGenBuffers(1, &mShadowStripsIndices);
- bool force = bindIndicesBufferInternal(mShadowStripsIndices);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_SHADOW_INDEX_COUNT * sizeof(uint16_t),
- shadowIndices.get(), GL_STATIC_DRAW);
- return force;
- }
-
- return bindIndicesBufferInternal(mShadowStripsIndices);
-}
-
-bool Caches::unbindIndicesBuffer() {
- if (mCurrentIndicesBuffer) {
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- mCurrentIndicesBuffer = 0;
- return true;
- }
- return false;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// PBO
-///////////////////////////////////////////////////////////////////////////////
-
-bool Caches::bindPixelBuffer(const GLuint buffer) {
- if (mCurrentPixelBuffer != buffer) {
- glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer);
- mCurrentPixelBuffer = buffer;
- return true;
- }
- return false;
-}
-
-bool Caches::unbindPixelBuffer() {
- if (mCurrentPixelBuffer) {
- glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
- mCurrentPixelBuffer = 0;
- return true;
- }
- return false;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Meshes and textures
-///////////////////////////////////////////////////////////////////////////////
-
-void Caches::bindPositionVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) {
- if (force || vertices != mCurrentPositionPointer || stride != mCurrentPositionStride) {
- GLuint slot = currentProgram->position;
- glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices);
- mCurrentPositionPointer = vertices;
- mCurrentPositionStride = stride;
- }
-}
-
-void Caches::bindTexCoordsVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) {
- if (force || vertices != mCurrentTexCoordsPointer || stride != mCurrentTexCoordsStride) {
- GLuint slot = currentProgram->texCoords;
- glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices);
- mCurrentTexCoordsPointer = vertices;
- mCurrentTexCoordsStride = stride;
- }
-}
-
-void Caches::resetVertexPointers() {
- mCurrentPositionPointer = this;
- mCurrentTexCoordsPointer = this;
-}
-
-void Caches::resetTexCoordsVertexPointer() {
- mCurrentTexCoordsPointer = this;
-}
-
-void Caches::enableTexCoordsVertexArray() {
- if (!mTexCoordsArrayEnabled) {
- glEnableVertexAttribArray(Program::kBindingTexCoords);
- mCurrentTexCoordsPointer = this;
- mTexCoordsArrayEnabled = true;
- }
-}
-
-void Caches::disableTexCoordsVertexArray() {
- if (mTexCoordsArrayEnabled) {
- glDisableVertexAttribArray(Program::kBindingTexCoords);
- mTexCoordsArrayEnabled = false;
- }
-}
-
-void Caches::activeTexture(GLuint textureUnit) {
- if (mTextureUnit != textureUnit) {
- glActiveTexture(gTextureUnits[textureUnit]);
- mTextureUnit = textureUnit;
- }
-}
-
-void Caches::resetActiveTexture() {
- mTextureUnit = -1;
-}
-
-void Caches::bindTexture(GLuint texture) {
- if (mBoundTextures[mTextureUnit] != texture) {
- glBindTexture(GL_TEXTURE_2D, texture);
- mBoundTextures[mTextureUnit] = texture;
- }
-}
-
-void Caches::bindTexture(GLenum target, GLuint texture) {
- if (target == GL_TEXTURE_2D) {
- bindTexture(texture);
- } else {
- // GLConsumer directly calls glBindTexture() with
- // target=GL_TEXTURE_EXTERNAL_OES, don't cache this target
- // since the cached state could be stale
- glBindTexture(target, texture);
- }
-}
-
-void Caches::deleteTexture(GLuint texture) {
- // When glDeleteTextures() is called on a currently bound texture,
- // OpenGL ES specifies that the texture is then considered unbound
- // Consider the following series of calls:
- //
- // glGenTextures -> creates texture name 2
- // glBindTexture(2)
- // glDeleteTextures(2) -> 2 is now unbound
- // glGenTextures -> can return 2 again
- //
- // If we don't call glBindTexture(2) after the second glGenTextures
- // call, any texture operation will be performed on the default
- // texture (name=0)
-
- unbindTexture(texture);
-
- glDeleteTextures(1, &texture);
-}
-
-void Caches::resetBoundTextures() {
- memset(mBoundTextures, 0, REQUIRED_TEXTURE_UNITS_COUNT * sizeof(GLuint));
-}
-
-void Caches::unbindTexture(GLuint texture) {
- for (int i = 0; i < REQUIRED_TEXTURE_UNITS_COUNT; i++) {
- if (mBoundTextures[i] == texture) {
- mBoundTextures[i] = 0;
- }
- }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Scissor
-///////////////////////////////////////////////////////////////////////////////
-
-bool Caches::setScissor(GLint x, GLint y, GLint width, GLint height) {
- if (scissorEnabled && (x != mScissorX || y != mScissorY ||
- width != mScissorWidth || height != mScissorHeight)) {
-
- if (x < 0) {
- width += x;
- x = 0;
- }
- if (y < 0) {
- height += y;
- y = 0;
- }
- if (width < 0) {
- width = 0;
- }
- if (height < 0) {
- height = 0;
- }
- glScissor(x, y, width, height);
-
- mScissorX = x;
- mScissorY = y;
- mScissorWidth = width;
- mScissorHeight = height;
-
- return true;
- }
- return false;
-}
-
-bool Caches::enableScissor() {
- if (!scissorEnabled) {
- glEnable(GL_SCISSOR_TEST);
- scissorEnabled = true;
- resetScissor();
- return true;
- }
- return false;
-}
-
-bool Caches::disableScissor() {
- if (scissorEnabled) {
- glDisable(GL_SCISSOR_TEST);
- scissorEnabled = false;
- return true;
- }
- return false;
-}
-
-void Caches::setScissorEnabled(bool enabled) {
- if (scissorEnabled != enabled) {
- if (enabled) glEnable(GL_SCISSOR_TEST);
- else glDisable(GL_SCISSOR_TEST);
- scissorEnabled = enabled;
- }
-}
-
-void Caches::resetScissor() {
- mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0;
-}
-
-///////////////////////////////////////////////////////////////////////////////
// Tiling
///////////////////////////////////////////////////////////////////////////////
@@ -682,7 +378,7 @@
TextureVertex* Caches::getRegionMesh() {
// Create the mesh, 2 triangles and 4 vertices per rectangle in the region
if (!mRegionMesh) {
- mRegionMesh.reset(new TextureVertex[gMaxNumberOfQuads * 4]);
+ mRegionMesh.reset(new TextureVertex[kMaxNumberOfQuads * 4]);
}
return mRegionMesh.get();
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index b0eebd7..f6d3476 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -21,7 +21,28 @@
#define LOG_TAG "OpenGLRenderer"
#endif
+
+#include "AssetAtlas.h"
+#include "Dither.h"
+#include "Extensions.h"
+#include "FboCache.h"
+#include "GradientCache.h"
+#include "LayerCache.h"
+#include "PatchCache.h"
+#include "ProgramCache.h"
+#include "PathCache.h"
+#include "RenderBufferCache.h"
+#include "renderstate/PixelBufferState.h"
+#include "renderstate/TextureState.h"
+#include "ResourceCache.h"
+#include "TessellationCache.h"
+#include "TextDropShadowCache.h"
+#include "TextureCache.h"
+#include "thread/TaskProcessor.h"
+#include "thread/TaskManager.h"
+
#include <vector>
+#include <memory>
#include <GLES3/gl3.h>
@@ -33,90 +54,37 @@
#include <SkPath.h>
-#include "thread/TaskProcessor.h"
-#include "thread/TaskManager.h"
-
-#include "AssetAtlas.h"
-#include "Extensions.h"
-#include "TextureCache.h"
-#include "LayerCache.h"
-#include "RenderBufferCache.h"
-#include "GradientCache.h"
-#include "PatchCache.h"
-#include "ProgramCache.h"
-#include "PathCache.h"
-#include "TessellationCache.h"
-#include "TextDropShadowCache.h"
-#include "FboCache.h"
-#include "ResourceCache.h"
-#include "Stencil.h"
-#include "Dither.h"
-
namespace android {
namespace uirenderer {
class GammaFontRenderer;
///////////////////////////////////////////////////////////////////////////////
-// Globals
-///////////////////////////////////////////////////////////////////////////////
-
-// GL ES 2.0 defines that at least 16 texture units must be supported
-#define REQUIRED_TEXTURE_UNITS_COUNT 3
-
-// Maximum number of quads that pre-allocated meshes can draw
-static const uint32_t gMaxNumberOfQuads = 2048;
-
-// Generates simple and textured vertices
-#define FV(x, y, u, v) { x, y, u, v }
-
-// This array is never used directly but used as a memcpy source in the
-// OpenGLRenderer constructor
-static const TextureVertex gMeshVertices[] = {
- FV(0.0f, 0.0f, 0.0f, 0.0f),
- FV(1.0f, 0.0f, 1.0f, 0.0f),
- FV(0.0f, 1.0f, 0.0f, 1.0f),
- FV(1.0f, 1.0f, 1.0f, 1.0f)
-};
-static const GLsizei gMeshStride = sizeof(TextureVertex);
-static const GLsizei gVertexStride = sizeof(Vertex);
-static const GLsizei gAlphaVertexStride = sizeof(AlphaVertex);
-static const GLsizei gMeshTextureOffset = 2 * sizeof(float);
-static const GLsizei gVertexAlphaOffset = 2 * sizeof(float);
-static const GLsizei gVertexAAWidthOffset = 2 * sizeof(float);
-static const GLsizei gVertexAALengthOffset = 3 * sizeof(float);
-static const GLsizei gMeshCount = 4;
-
-// Must define as many texture units as specified by REQUIRED_TEXTURE_UNITS_COUNT
-static const GLenum gTextureUnits[] = {
- GL_TEXTURE0,
- GL_TEXTURE1,
- GL_TEXTURE2
-};
-
-///////////////////////////////////////////////////////////////////////////////
-// Debug
-///////////////////////////////////////////////////////////////////////////////
-
-struct CacheLogger {
- CacheLogger() {
- INIT_LOGD("Creating OpenGL renderer caches");
- }
-}; // struct CacheLogger
-
-///////////////////////////////////////////////////////////////////////////////
// Caches
///////////////////////////////////////////////////////////////////////////////
class RenderNode;
class RenderState;
-class ANDROID_API Caches: public Singleton<Caches> {
- Caches();
+class ANDROID_API Caches {
+public:
+ static Caches& createInstance(RenderState& renderState) {
+ LOG_ALWAYS_FATAL_IF(sInstance, "double create of Caches attempted");
+ sInstance = new Caches(renderState);
+ return *sInstance;
+ }
- friend class Singleton<Caches>;
+ static Caches& getInstance() {
+ LOG_ALWAYS_FATAL_IF(!sInstance, "instance not yet created");
+ return *sInstance;
+ }
- CacheLogger mLogger;
+ static bool hasInstance() {
+ return sInstance != 0;
+ }
+private:
+ Caches(RenderState& renderState);
+ static Caches* sInstance;
public:
enum FlushMode {
@@ -135,8 +103,6 @@
*/
bool initProperties();
- void setRenderState(RenderState* renderState) { mRenderState = renderState; }
-
/**
* Flush the cache.
*
@@ -175,116 +141,6 @@
*/
void deleteLayerDeferred(Layer* layer);
- /**
- * Binds the VBO used to render simple textured quads.
- */
- bool bindMeshBuffer();
-
- /**
- * Binds the specified VBO if needed.
- */
- bool bindMeshBuffer(const GLuint buffer);
-
- /**
- * Unbinds the VBO used to render simple textured quads.
- */
- bool unbindMeshBuffer();
-
- /**
- * Binds a global indices buffer that can draw up to
- * gMaxNumberOfQuads quads.
- */
- bool bindQuadIndicesBuffer();
- bool bindShadowIndicesBuffer();
- bool unbindIndicesBuffer();
-
- /**
- * Binds the specified buffer as the current GL unpack pixel buffer.
- */
- bool bindPixelBuffer(const GLuint buffer);
-
- /**
- * Resets the current unpack pixel buffer to 0 (default value.)
- */
- bool unbindPixelBuffer();
-
- /**
- * Binds an attrib to the specified float vertex pointer.
- * Assumes a stride of gMeshStride and a size of 2.
- */
- void bindPositionVertexPointer(bool force, const GLvoid* vertices, GLsizei stride = gMeshStride);
-
- /**
- * Binds an attrib to the specified float vertex pointer.
- * Assumes a stride of gMeshStride and a size of 2.
- */
- void bindTexCoordsVertexPointer(bool force, const GLvoid* vertices, GLsizei stride = gMeshStride);
-
- /**
- * Resets the vertex pointers.
- */
- void resetVertexPointers();
- void resetTexCoordsVertexPointer();
-
- void enableTexCoordsVertexArray();
- void disableTexCoordsVertexArray();
-
- /**
- * Activate the specified texture unit. The texture unit must
- * be specified using an integer number (0 for GL_TEXTURE0 etc.)
- */
- void activeTexture(GLuint textureUnit);
-
- /**
- * Invalidate the cached value of the active texture unit.
- */
- void resetActiveTexture();
-
- /**
- * Binds the specified texture as a GL_TEXTURE_2D texture.
- * All texture bindings must be performed with this method or
- * bindTexture(GLenum, GLuint).
- */
- void bindTexture(GLuint texture);
-
- /**
- * Binds the specified texture with the specified render target.
- * All texture bindings must be performed with this method or
- * bindTexture(GLuint).
- */
- void bindTexture(GLenum target, GLuint texture);
-
- /**
- * Deletes the specified texture and clears it from the cache
- * of bound textures.
- * All textures must be deleted using this method.
- */
- void deleteTexture(GLuint texture);
-
- /**
- * Signals that the cache of bound textures should be cleared.
- * Other users of the context may have altered which textures are bound.
- */
- void resetBoundTextures();
-
- /**
- * Clear the cache of bound textures.
- */
- void unbindTexture(GLuint texture);
-
- /**
- * Sets the scissor for the current surface.
- */
- bool setScissor(GLint x, GLint y, GLint width, GLint height);
-
- /**
- * Resets the scissor state.
- */
- void resetScissor();
-
- bool enableScissor();
- bool disableScissor();
- void setScissorEnabled(bool enabled);
void startTiling(GLuint x, GLuint y, GLuint width, GLuint height, bool discard);
void endTiling();
@@ -306,18 +162,11 @@
void registerFunctors(uint32_t functorCount);
void unregisterFunctors(uint32_t functorCount);
- bool blend;
- GLenum lastSrcMode;
- GLenum lastDstMode;
Program* currentProgram;
- bool scissorEnabled;
bool drawDeferDisabled;
bool drawReorderDisabled;
- // VBO to draw with
- GLuint meshBuffer;
-
// Misc
GLint maxTextureSize;
@@ -348,7 +197,6 @@
TaskManager tasks;
Dither dither;
- Stencil stencil;
bool gpuPixelBuffersEnabled;
@@ -371,6 +219,9 @@
int propertyAmbientShadowStrength;
int propertySpotShadowStrength;
+ PixelBufferState& pixelBufferState() { return *mPixelBufferState; }
+ TextureState& textureState() { return *mTextureState; }
+
private:
enum OverdrawColorSet {
kColorSet_Default = 0,
@@ -382,8 +233,6 @@
void initConstraints();
void initStaticProperties();
- bool bindIndicesBufferInternal(const GLuint buffer);
-
static void eventMarkNull(GLsizei length, const GLchar* marker) { }
static void startMarkNull(GLsizei length, const GLchar* marker) { }
static void endMarkNull() { }
@@ -396,32 +245,16 @@
if (label) *label = '\0';
}
- GLuint mCurrentBuffer;
- GLuint mCurrentIndicesBuffer;
- GLuint mCurrentPixelBuffer;
- const void* mCurrentPositionPointer;
- GLsizei mCurrentPositionStride;
- const void* mCurrentTexCoordsPointer;
- GLsizei mCurrentTexCoordsStride;
+ RenderState* mRenderState;
- bool mTexCoordsArrayEnabled;
-
- GLuint mTextureUnit;
-
- GLint mScissorX;
- GLint mScissorY;
- GLint mScissorWidth;
- GLint mScissorHeight;
+ PixelBufferState* mPixelBufferState = nullptr; // TODO: move to RenderState
+ TextureState* mTextureState = nullptr; // TODO: move to RenderState
Extensions& mExtensions;
// Used to render layers
std::unique_ptr<TextureVertex[]> mRegionMesh;
- // Global index buffer
- GLuint mMeshIndices;
- GLuint mShadowStripsIndices;
-
mutable Mutex mGarbageLock;
Vector<Layer*> mLayerGarbage;
@@ -430,12 +263,7 @@
uint32_t mFunctorsCount;
- // Caches texture bindings for the GL_TEXTURE_2D target
- GLuint mBoundTextures[REQUIRED_TEXTURE_UNITS_COUNT];
-
OverdrawColorSet mOverdrawDebugColorSet;
-
- RenderState* mRenderState;
}; // class Caches
}; // namespace uirenderer
diff --git a/libs/hwui/Canvas.h b/libs/hwui/Canvas.h
index f9c8620..7ad0683 100644
--- a/libs/hwui/Canvas.h
+++ b/libs/hwui/Canvas.h
@@ -42,15 +42,15 @@
*/
static Canvas* create_canvas(SkCanvas* skiaCanvas);
- // TODO: enable HWUI to either create similar canvas wrapper or subclass
- // directly from Canvas
- //static Canvas* create_canvas(uirenderer::Renderer* renderer);
-
- // TODO: this is a temporary affordance until all necessary logic can be
- // moved within this interface! Further, the return value should
- // NOT be unref'd and is valid until this canvas is destroyed or a
- // new bitmap is set.
- virtual SkCanvas* getSkCanvas() = 0;
+ /**
+ * Provides a Skia SkCanvas interface that acts as a proxy to this Canvas.
+ * It is useful for testing and clients (e.g. Picture/Movie) that expect to
+ * draw their contents into an SkCanvas.
+ *
+ * Further, the returned SkCanvas should NOT be unref'd and is valid until
+ * this canvas is destroyed or a new bitmap is set.
+ */
+ virtual SkCanvas* asSkCanvas() = 0;
virtual void setBitmap(SkBitmap* bitmap, bool copyState) = 0;
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 36c14c4..d128ffe 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -17,13 +17,6 @@
#ifndef ANDROID_HWUI_DISPLAY_OPERATION_H
#define ANDROID_HWUI_DISPLAY_OPERATION_H
-#include <SkColor.h>
-#include <SkPath.h>
-#include <SkPathOps.h>
-#include <SkXfermode.h>
-
-#include <private/hwui/DrawGlInfo.h>
-
#include "OpenGLRenderer.h"
#include "AssetAtlas.h"
#include "DeferredDisplayList.h"
@@ -31,11 +24,18 @@
#include "GammaFontRenderer.h"
#include "Patch.h"
#include "RenderNode.h"
-#include "RenderState.h"
+#include "renderstate/RenderState.h"
#include "UvMapper.h"
#include "utils/LinearAllocator.h"
#include "utils/PaintUtils.h"
+#include <SkColor.h>
+#include <SkPath.h>
+#include <SkPathOps.h>
+#include <SkXfermode.h>
+
+#include <private/hwui/DrawGlInfo.h>
+
// Use OP_LOG for logging with arglist, OP_LOGS if just printing char*
#define OP_LOGS(s) OP_LOG("%s", (s))
#define OP_LOG(s, ...) ALOGD( "%*s" s, level * 2, "", __VA_ARGS__ )
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index d98d744..23181bc 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -59,6 +59,7 @@
mPathMap.clear();
DisplayListData* data = mDisplayListData;
mDisplayListData = nullptr;
+ mSkiaCanvasProxy.reset(nullptr);
return data;
}
@@ -94,6 +95,15 @@
mDisplayListData->functors.add(functor);
}
+SkCanvas* DisplayListRenderer::asSkCanvas() {
+ LOG_ALWAYS_FATAL_IF(!mDisplayListData,
+ "attempting to get an SkCanvas when we are not recording!");
+ if (!mSkiaCanvasProxy) {
+ mSkiaCanvasProxy.reset(new SkiaCanvasProxy(this));
+ }
+ return mSkiaCanvasProxy.get();
+}
+
int DisplayListRenderer::save(SkCanvas::SaveFlags flags) {
addStateOp(new (alloc()) SaveOp((int) flags));
return mState.save((int) flags);
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index d2b3ead..bd0b3b7 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -28,6 +28,7 @@
#include "Canvas.h"
#include "CanvasState.h"
#include "DisplayList.h"
+#include "SkiaCanvasProxy.h"
#include "RenderNode.h"
#include "Renderer.h"
#include "ResourceCache.h"
@@ -136,10 +137,8 @@
// ----------------------------------------------------------------------------
// android/graphics/Canvas interface
// ----------------------------------------------------------------------------
- virtual SkCanvas* getSkCanvas() override {
- LOG_ALWAYS_FATAL("DisplayListRenderer has no SkCanvas");
- return nullptr;
- }
+ virtual SkCanvas* asSkCanvas() override;
+
virtual void setBitmap(SkBitmap* bitmap, bool copyState) override {
LOG_ALWAYS_FATAL("DisplayListRenderer is not backed by a bitmap.");
}
@@ -244,6 +243,7 @@
private:
CanvasState mState;
+ std::unique_ptr<SkiaCanvasProxy> mSkiaCanvasProxy;
enum DeferredBarrierType {
kBarrier_None,
diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp
index 12d9389..d637ec1 100644
--- a/libs/hwui/Dither.cpp
+++ b/libs/hwui/Dither.cpp
@@ -24,7 +24,10 @@
// Lifecycle
///////////////////////////////////////////////////////////////////////////////
-Dither::Dither(): mCaches(nullptr), mInitialized(false), mDitherTexture(0) {
+Dither::Dither(Caches& caches)
+ : mCaches(caches)
+ , mInitialized(false)
+ , mDitherTexture(0) {
}
void Dither::bindDitherTexture() {
@@ -32,7 +35,7 @@
bool useFloatTexture = Extensions::getInstance().hasFloatTextures();
glGenTextures(1, &mDitherTexture);
- mCaches->bindTexture(mDitherTexture);
+ mCaches.textureState().bindTexture(mDitherTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -71,13 +74,13 @@
mInitialized = true;
} else {
- mCaches->bindTexture(mDitherTexture);
+ mCaches.textureState().bindTexture(mDitherTexture);
}
}
void Dither::clear() {
if (mInitialized) {
- mCaches->deleteTexture(mDitherTexture);
+ mCaches.textureState().deleteTexture(mDitherTexture);
mInitialized = false;
}
}
@@ -87,10 +90,8 @@
///////////////////////////////////////////////////////////////////////////////
void Dither::setupProgram(Program* program, GLuint* textureUnit) {
- if (!mCaches) mCaches = &Caches::getInstance();
-
GLuint textureSlot = (*textureUnit)++;
- mCaches->activeTexture(textureSlot);
+ mCaches.textureState().activateTexture(textureSlot);
bindDitherTexture();
diff --git a/libs/hwui/Dither.h b/libs/hwui/Dither.h
index 092ebf2..38633af 100644
--- a/libs/hwui/Dither.h
+++ b/libs/hwui/Dither.h
@@ -36,7 +36,7 @@
*/
class Dither {
public:
- Dither();
+ Dither(Caches& caches);
void clear();
void setupProgram(Program* program, GLuint* textureUnit);
@@ -44,7 +44,7 @@
private:
void bindDitherTexture();
- Caches* mCaches;
+ Caches& mCaches;
bool mInitialized;
GLuint mDitherTexture;
};
diff --git a/libs/hwui/Fence.h b/libs/hwui/Fence.h
deleted file mode 100644
index fc29f7ac1..0000000
--- a/libs/hwui/Fence.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2013 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_HWUI_FENCE_H
-#define ANDROID_HWUI_FENCE_H
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-
-namespace android {
-namespace uirenderer {
-
-/**
- * Creating a Fence instance inserts a new sync fence in the OpenGL
- * commands stream. The caller can then wait for the fence to be signaled
- * by calling the wait method.
- */
-class Fence {
-public:
- enum {
- /**
- * Default timeout in nano-seconds for wait()
- */
- kDefaultTimeout = 1000000000
- };
-
- /**
- * Inserts a new sync fence in the OpenGL commands stream.
- */
- Fence() {
- mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- if (mDisplay != EGL_NO_DISPLAY) {
- mFence = eglCreateSyncKHR(mDisplay, EGL_SYNC_FENCE_KHR, nullptr);
- } else {
- mFence = EGL_NO_SYNC_KHR;
- }
- }
-
- /**
- * Destroys the fence. Any caller waiting on the fence will be
- * signaled immediately.
- */
- ~Fence() {
- if (mFence != EGL_NO_SYNC_KHR) {
- eglDestroySyncKHR(mDisplay, mFence);
- }
- }
-
- /**
- * Blocks the calling thread until this fence is signaled, or until
- * <timeout> nanoseconds have passed.
- *
- * Returns true if waiting for the fence was successful, false if
- * a timeout or an error occurred.
- */
- bool wait(EGLTimeKHR timeout = kDefaultTimeout) {
- EGLint waitStatus = eglClientWaitSyncKHR(mDisplay, mFence,
- EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, timeout);
- if (waitStatus == EGL_FALSE) {
- ALOGW("Failed to wait for the fence %#x", eglGetError());
- }
- return waitStatus == EGL_CONDITION_SATISFIED_KHR;
- }
-
-private:
- EGLDisplay mDisplay;
- EGLSyncKHR mFence;
-
-}; // class Fence
-
-/**
- * An AutoFence creates a Fence instance and waits for the fence
- * to be signaled when the AutoFence is destroyed. This is useful
- * to automatically wait for a series of OpenGL commands to be
- * executed. For example:
- *
- * void drawAndWait() {
- * glDrawElements();
- * AutoFence fence;
- * }
- */
-class AutoFence {
-public:
- AutoFence(EGLTimeKHR timeout = Fence::kDefaultTimeout): mTimeout(timeout) {
- }
-
- ~AutoFence() {
- mFence.wait(mTimeout);
- }
-
-private:
- EGLTimeKHR mTimeout;
- Fence mFence;
-
-}; // class AutoFence
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_FENCE_H
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index a0bc7b0..6dcd3e1 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -14,7 +14,17 @@
* limitations under the License.
*/
-#define LOG_TAG "OpenGLRenderer"
+#include "FontRenderer.h"
+
+#include "Caches.h"
+#include "Debug.h"
+#include "Extensions.h"
+#include "OpenGLRenderer.h"
+#include "PixelBuffer.h"
+#include "Rect.h"
+#include "renderstate/RenderState.h"
+#include "utils/Blur.h"
+#include "utils/Timing.h"
#include <SkGlyph.h>
#include <SkUtils.h>
@@ -27,17 +37,6 @@
#include <RenderScript.h>
#endif
-#include "utils/Blur.h"
-#include "utils/Timing.h"
-
-#include "Caches.h"
-#include "Debug.h"
-#include "Extensions.h"
-#include "FontRenderer.h"
-#include "OpenGLRenderer.h"
-#include "PixelBuffer.h"
-#include "Rect.h"
-
namespace android {
namespace uirenderer {
@@ -47,9 +46,7 @@
///////////////////////////////////////////////////////////////////////////////
// TextSetupFunctor
///////////////////////////////////////////////////////////////////////////////
-status_t TextSetupFunctor::operator ()(int what, void* data) {
- Data* typedData = reinterpret_cast<Data*>(data);
- GLenum glyphFormat = typedData ? typedData->glyphFormat : GL_ALPHA;
+status_t TextSetupFunctor::setup(GLenum glyphFormat) {
renderer->setupDraw();
renderer->setupDrawTextGamma(paint);
@@ -287,7 +284,7 @@
uint32_t cacheWidth = cacheTexture->getWidth();
if (!cacheTexture->getPixelBuffer()) {
- Caches::getInstance().activeTexture(0);
+ Caches::getInstance().textureState().activateTexture(0);
// Large-glyph texture memory is allocated only as needed
cacheTexture->allocateTexture();
}
@@ -397,10 +394,10 @@
CacheTexture* FontRenderer::createCacheTexture(int width, int height, GLenum format,
bool allocate) {
- CacheTexture* cacheTexture = new CacheTexture(width, height, format, gMaxNumberOfQuads);
+ CacheTexture* cacheTexture = new CacheTexture(width, height, format, kMaxNumberOfQuads);
if (allocate) {
- Caches::getInstance().activeTexture(0);
+ Caches::getInstance().textureState().activateTexture(0);
cacheTexture->allocateTexture();
cacheTexture->allocateMesh();
}
@@ -446,8 +443,8 @@
if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) {
if (cacheTexture->getTextureId() != lastTextureId) {
lastTextureId = cacheTexture->getTextureId();
- caches.activeTexture(0);
- caches.bindTexture(lastTextureId);
+ caches.textureState().activateTexture(0);
+ caches.textureState().bindTexture(lastTextureId);
}
if (cacheTexture->upload()) {
@@ -473,7 +470,7 @@
checkTextureUpdateForCache(caches, mRGBACacheTextures, resetPixelStore, lastTextureId);
// Unbind any PBO we might have used to update textures
- caches.unbindPixelBuffer();
+ caches.pixelBufferState().unbind();
// Reset to default unpack row length to avoid affecting texture
// uploads in other parts of the renderer
@@ -485,44 +482,49 @@
}
void FontRenderer::issueDrawCommand(Vector<CacheTexture*>& cacheTextures) {
- Caches& caches = Caches::getInstance();
+ if (!mFunctor) return;
+
+ Caches& caches = mFunctor->renderer->getCaches();
+ RenderState& renderState = mFunctor->renderer->renderState();
+
bool first = true;
- bool force = false;
+ bool forceRebind = false;
for (uint32_t i = 0; i < cacheTextures.size(); i++) {
CacheTexture* texture = cacheTextures[i];
if (texture->canDraw()) {
if (first) {
if (mFunctor) {
- TextSetupFunctor::Data functorData(texture->getFormat());
- (*mFunctor)(0, &functorData);
+ mFunctor->setup(texture->getFormat());
}
checkTextureUpdate();
- caches.bindQuadIndicesBuffer();
+ renderState.meshState().bindQuadIndicesBuffer();
if (!mDrawn) {
// If returns true, a VBO was bound and we must
// rebind our vertex attrib pointers even if
// they have the same values as the current pointers
- force = caches.unbindMeshBuffer();
+ forceRebind = renderState.meshState().unbindMeshBuffer();
}
- caches.activeTexture(0);
+ caches.textureState().activateTexture(0);
first = false;
}
- caches.bindTexture(texture->getTextureId());
+ caches.textureState().bindTexture(texture->getTextureId());
texture->setLinearFiltering(mLinearFiltering, false);
TextureVertex* mesh = texture->mesh();
- caches.bindPositionVertexPointer(force, &mesh[0].x);
- caches.bindTexCoordsVertexPointer(force, &mesh[0].u);
- force = false;
+ MeshState& meshState = renderState.meshState();
+ Program* program = caches.currentProgram;
+ meshState.bindPositionVertexPointer(program, forceRebind, &mesh[0].x);
+ meshState.bindTexCoordsVertexPointer(program, forceRebind, &mesh[0].u);
glDrawElements(GL_TRIANGLES, texture->meshElementCount(),
GL_UNSIGNED_SHORT, texture->indices());
texture->resetMesh();
+ forceRebind = false;
}
}
}
@@ -647,7 +649,7 @@
Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions);
// Unbind any PBO we might have used
- Caches::getInstance().unbindPixelBuffer();
+ Caches::getInstance().pixelBufferState().unbind();
blurImage(&dataBuffer, paddedWidth, paddedHeight, radius);
}
@@ -661,7 +663,7 @@
return image;
}
-void FontRenderer::initRender(const Rect* clip, Rect* bounds, Functor* functor) {
+void FontRenderer::initRender(const Rect* clip, Rect* bounds, TextSetupFunctor* functor) {
checkInit();
mDrawn = false;
@@ -689,7 +691,7 @@
bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const char *text,
uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
- const float* positions, Rect* bounds, Functor* functor, bool forceFinish) {
+ const float* positions, Rect* bounds, TextSetupFunctor* functor, bool forceFinish) {
if (!mCurrentFont) {
ALOGE("No font set");
return false;
@@ -707,7 +709,7 @@
bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text,
uint32_t startIndex, uint32_t len, int numGlyphs, const SkPath* path,
- float hOffset, float vOffset, Rect* bounds, Functor* functor) {
+ float hOffset, float vOffset, Rect* bounds, TextSetupFunctor* functor) {
if (!mCurrentFont) {
ALOGE("No font set");
return false;
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 668ee64..cb63684 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -17,7 +17,12 @@
#ifndef ANDROID_HWUI_FONT_RENDERER_H
#define ANDROID_HWUI_FONT_RENDERER_H
-#include <utils/Functor.h>
+#include "font/FontUtil.h"
+#include "font/CacheTexture.h"
+#include "font/CachedGlyphInfo.h"
+#include "font/Font.h"
+#include "utils/SortedList.h"
+
#include <utils/LruCache.h>
#include <utils/Vector.h>
#include <utils/StrongPointer.h>
@@ -26,12 +31,6 @@
#include <GLES2/gl2.h>
-#include "font/FontUtil.h"
-#include "font/CacheTexture.h"
-#include "font/CachedGlyphInfo.h"
-#include "font/Font.h"
-#include "utils/SortedList.h"
-
#ifdef ANDROID_ENABLE_RENDERSCRIPT
#include "RenderScript.h"
namespace RSC {
@@ -47,26 +46,20 @@
class OpenGLRenderer;
-///////////////////////////////////////////////////////////////////////////////
-// TextSetupFunctor
-///////////////////////////////////////////////////////////////////////////////
-class TextSetupFunctor: public Functor {
+class TextSetupFunctor {
public:
- struct Data {
- Data(GLenum glyphFormat) : glyphFormat(glyphFormat) {
- }
-
- GLenum glyphFormat;
- };
-
TextSetupFunctor(OpenGLRenderer* renderer, float x, float y, bool pureTranslate,
- int alpha, SkXfermode::Mode mode, const SkPaint* paint): Functor(),
- renderer(renderer), x(x), y(y), pureTranslate(pureTranslate),
- alpha(alpha), mode(mode), paint(paint) {
+ int alpha, SkXfermode::Mode mode, const SkPaint* paint)
+ : renderer(renderer)
+ , x(x)
+ , y(y)
+ , pureTranslate(pureTranslate)
+ , alpha(alpha)
+ , mode(mode)
+ , paint(paint) {
}
- ~TextSetupFunctor() { }
- status_t operator ()(int what, void* data) override;
+ status_t setup(GLenum glyphFormat);
OpenGLRenderer* renderer;
float x;
@@ -77,10 +70,6 @@
const SkPaint* paint;
};
-///////////////////////////////////////////////////////////////////////////////
-// FontRenderer
-///////////////////////////////////////////////////////////////////////////////
-
class FontRenderer {
public:
FontRenderer();
@@ -101,22 +90,14 @@
// bounds is an out parameter
bool renderPosText(const SkPaint* paint, const Rect* clip, const char *text,
uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, const float* positions,
- Rect* bounds, Functor* functor, bool forceFinish = true);
+ Rect* bounds, TextSetupFunctor* functor, bool forceFinish = true);
// bounds is an out parameter
bool renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text,
uint32_t startIndex, uint32_t len, int numGlyphs, const SkPath* path,
- float hOffset, float vOffset, Rect* bounds, Functor* functor);
+ float hOffset, float vOffset, Rect* bounds, TextSetupFunctor* functor);
struct DropShadow {
- DropShadow() { };
-
- DropShadow(const DropShadow& dropShadow):
- width(dropShadow.width), height(dropShadow.height),
- image(dropShadow.image), penX(dropShadow.penX),
- penY(dropShadow.penY) {
- }
-
uint32_t width;
uint32_t height;
uint8_t* image;
@@ -152,7 +133,7 @@
void flushAllAndInvalidate();
void checkInit();
- void initRender(const Rect* clip, Rect* bounds, Functor* functor);
+ void initRender(const Rect* clip, Rect* bounds, TextSetupFunctor* functor);
void finishRender();
void issueDrawCommand(Vector<CacheTexture*>& cacheTextures);
@@ -193,7 +174,7 @@
bool mUploadTexture;
- Functor* mFunctor;
+ TextSetupFunctor* mFunctor;
const Rect* mClip;
Rect* mBounds;
bool mDrawn;
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 0987d9b..416b0b3 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -285,7 +285,7 @@
memcpy(pixels + rowBytes, pixels, rowBytes);
glGenTextures(1, &texture->id);
- Caches::getInstance().bindTexture(texture->id);
+ Caches::getInstance().textureState().bindTexture(texture->id);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
if (mUseFloatTexture) {
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
index 9176c76..1714e6d 100644
--- a/libs/hwui/GradientCache.h
+++ b/libs/hwui/GradientCache.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_HWUI_GRADIENT_CACHE_H
#define ANDROID_HWUI_GRADIENT_CACHE_H
+#include <memory>
+
#include <GLES3/gl3.h>
#include <SkShader.h>
diff --git a/libs/hwui/Image.cpp b/libs/hwui/Image.cpp
index edf3930..a31c546 100644
--- a/libs/hwui/Image.cpp
+++ b/libs/hwui/Image.cpp
@@ -39,7 +39,7 @@
} else {
// Create a 2D texture to sample from the EGLImage
glGenTextures(1, &mTexture);
- Caches::getInstance().bindTexture(mTexture);
+ Caches::getInstance().textureState().bindTexture(mTexture);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, mImage);
GLenum status = GL_NO_ERROR;
@@ -54,7 +54,7 @@
eglDestroyImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), mImage);
mImage = EGL_NO_IMAGE_KHR;
- Caches::getInstance().deleteTexture(mTexture);
+ Caches::getInstance().textureState().deleteTexture(mTexture);
mTexture = 0;
}
}
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 7388e3c..7a026ef 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -16,17 +16,18 @@
#define LOG_TAG "OpenGLRenderer"
-#include <utils/Log.h>
+#include "Layer.h"
#include "Caches.h"
#include "DeferredDisplayList.h"
-#include "Layer.h"
#include "LayerRenderer.h"
#include "OpenGLRenderer.h"
#include "RenderNode.h"
-#include "RenderState.h"
+#include "renderstate/RenderState.h"
#include "utils/TraceUtils.h"
+#include <utils/Log.h>
+
#define ATRACE_LAYER_WORK(label) \
ATRACE_FORMAT("%s HW Layer DisplayList %s %ux%u", \
label, \
@@ -67,15 +68,23 @@
}
Layer::~Layer() {
- renderState.requireGLContext();
renderState.unregisterLayer(this);
SkSafeUnref(colorFilter);
- removeFbo();
- deleteTexture();
+
+ if (stencil || fbo || texture.id) {
+ renderState.requireGLContext();
+ removeFbo();
+ deleteTexture();
+ }
delete[] mesh;
}
+void Layer::onGlContextLost() {
+ removeFbo();
+ deleteTexture();
+}
+
uint32_t Layer::computeIdealWidth(uint32_t layerWidth) {
return uint32_t(ceilf(layerWidth / float(LAYER_SIZE)) * LAYER_SIZE);
}
@@ -125,7 +134,7 @@
setSize(desiredWidth, desiredHeight);
if (fbo) {
- caches.activeTexture(0);
+ caches.textureState().activateTexture(0);
bindTexture();
allocateTexture();
@@ -186,7 +195,7 @@
void Layer::bindTexture() const {
if (texture.id) {
- caches.bindTexture(renderTarget, texture.id);
+ caches.textureState().bindTexture(renderTarget, texture.id);
}
}
@@ -210,7 +219,7 @@
}
void Layer::clearTexture() {
- caches.unbindTexture(texture.id);
+ caches.textureState().unbindTexture(texture.id);
texture.id = 0;
}
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 3b4f293..84ff021 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -288,6 +288,12 @@
void postDecStrong();
/**
+ * Lost the GL context but the layer is still around, mark it invalid internally
+ * so the dtor knows not to do any GL work
+ */
+ void onGlContextLost();
+
+ /**
* Bounds of the layer.
*/
Rect layer;
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 6ad1b197..d2f9a94 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -17,18 +17,19 @@
#define LOG_TAG "OpenGLRenderer"
#define ATRACE_TAG ATRACE_TAG_VIEW
-#include <ui/Rect.h>
-
-#include <private/hwui/DrawGlInfo.h>
-
-#include "RenderState.h"
#include "LayerCache.h"
#include "LayerRenderer.h"
#include "Matrix.h"
#include "Properties.h"
#include "Rect.h"
+#include "renderstate/RenderState.h"
#include "utils/TraceUtils.h"
+#include <ui/Rect.h>
+
+#include <private/hwui/DrawGlInfo.h>
+
+
namespace android {
namespace uirenderer {
@@ -48,7 +49,7 @@
bool opaque) {
LAYER_RENDERER_LOGD("Rendering into layer, fbo = %d", mLayer->getFbo());
- renderState().bindFramebuffer(mLayer->getFbo());
+ mRenderState.bindFramebuffer(mLayer->getFbo());
const float width = mLayer->layer.getWidth();
const float height = mLayer->layer.getHeight();
@@ -70,10 +71,10 @@
void LayerRenderer::clear(float left, float top, float right, float bottom, bool opaque) {
if (mLayer->isDirty()) {
- getCaches().disableScissor();
+ mRenderState.scissor().setEnabled(false);
glClear(GL_COLOR_BUFFER_BIT);
- getCaches().resetScissor();
+ mRenderState.scissor().reset();
mLayer->setDirty(false);
} else {
OpenGLRenderer::clear(left, top, right, bottom, opaque);
@@ -195,7 +196,7 @@
return nullptr;
}
- caches.activeTexture(0);
+ caches.textureState().activateTexture(0);
Layer* layer = caches.layerCache.get(renderState, width, height);
if (!layer) {
ALOGW("Could not obtain a layer");
@@ -282,7 +283,7 @@
layer->region.clear();
layer->setRenderTarget(GL_NONE); // see ::updateTextureLayer()
- Caches::getInstance().activeTexture(0);
+ Caches::getInstance().textureState().activateTexture(0);
layer->generateTexture();
return layer;
@@ -411,8 +412,8 @@
glGenTextures(1, &texture);
if ((error = glGetError()) != GL_NO_ERROR) goto error;
- caches.activeTexture(0);
- caches.bindTexture(texture);
+ caches.textureState().activateTexture(0);
+ caches.textureState().bindTexture(texture);
glPixelStorei(GL_PACK_ALIGNMENT, bitmap->bytesPerPixel());
@@ -436,7 +437,7 @@
renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f,
bitmap->width(), bitmap->height(), !layer->isBlend());
- caches.disableScissor();
+ renderState.scissor().setEnabled(false);
renderer.translate(0.0f, bitmap->height());
renderer.scale(1.0f, -1.0f);
@@ -474,7 +475,7 @@
renderState.bindFramebuffer(previousFbo);
layer->setAlpha(alpha, mode);
layer->setFbo(previousLayerFbo);
- caches.deleteTexture(texture);
+ caches.textureState().deleteTexture(texture);
caches.fboCache.put(fbo);
renderState.setViewport(previousViewportWidth, previousViewportHeight);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 82f6ddd..3ee9808 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -16,6 +16,24 @@
#define LOG_TAG "OpenGLRenderer"
+#include "OpenGLRenderer.h"
+
+#include "DeferredDisplayList.h"
+#include "DisplayListRenderer.h"
+#include "GammaFontRenderer.h"
+#include "Patch.h"
+#include "PathTessellator.h"
+#include "Properties.h"
+#include "RenderNode.h"
+#include "renderstate/RenderState.h"
+#include "ShadowTessellator.h"
+#include "SkiaShader.h"
+#include "Vector.h"
+#include "VertexBuffer.h"
+#include "utils/GLUtils.h"
+#include "utils/PaintUtils.h"
+#include "utils/TraceUtils.h"
+
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
@@ -32,24 +50,6 @@
#include <ui/Rect.h>
-#include "OpenGLRenderer.h"
-#include "DeferredDisplayList.h"
-#include "DisplayListRenderer.h"
-#include "Fence.h"
-#include "GammaFontRenderer.h"
-#include "Patch.h"
-#include "PathTessellator.h"
-#include "Properties.h"
-#include "RenderNode.h"
-#include "RenderState.h"
-#include "ShadowTessellator.h"
-#include "SkiaShader.h"
-#include "Vector.h"
-#include "VertexBuffer.h"
-#include "utils/GLUtils.h"
-#include "utils/PaintUtils.h"
-#include "utils/TraceUtils.h"
-
#if DEBUG_DETAILED_EVENTS
#define EVENT_LOGD(...) eventMarkDEBUG(__VA_ARGS__)
#else
@@ -70,55 +70,6 @@
// Globals
///////////////////////////////////////////////////////////////////////////////
-/**
- * Structure mapping Skia xfermodes to OpenGL blending factors.
- */
-struct Blender {
- SkXfermode::Mode mode;
- GLenum src;
- GLenum dst;
-}; // struct Blender
-
-// In this array, the index of each Blender equals the value of the first
-// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode]
-static const Blender gBlends[] = {
- { SkXfermode::kClear_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kSrc_Mode, GL_ONE, GL_ZERO },
- { SkXfermode::kDst_Mode, GL_ZERO, GL_ONE },
- { SkXfermode::kSrcOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kDstOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
- { SkXfermode::kSrcIn_Mode, GL_DST_ALPHA, GL_ZERO },
- { SkXfermode::kDstIn_Mode, GL_ZERO, GL_SRC_ALPHA },
- { SkXfermode::kSrcOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
- { SkXfermode::kDstOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kSrcATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kDstATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
- { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kPlus_Mode, GL_ONE, GL_ONE },
- { SkXfermode::kModulate_Mode, GL_ZERO, GL_SRC_COLOR },
- { SkXfermode::kScreen_Mode, GL_ONE, GL_ONE_MINUS_SRC_COLOR }
-};
-
-// This array contains the swapped version of each SkXfermode. For instance
-// this array's SrcOver blending mode is actually DstOver. You can refer to
-// createLayer() for more information on the purpose of this array.
-static const Blender gBlendsSwap[] = {
- { SkXfermode::kClear_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
- { SkXfermode::kSrc_Mode, GL_ZERO, GL_ONE },
- { SkXfermode::kDst_Mode, GL_ONE, GL_ZERO },
- { SkXfermode::kSrcOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
- { SkXfermode::kDstOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kSrcIn_Mode, GL_ZERO, GL_SRC_ALPHA },
- { SkXfermode::kDstIn_Mode, GL_DST_ALPHA, GL_ZERO },
- { SkXfermode::kSrcOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kDstOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
- { SkXfermode::kSrcATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
- { SkXfermode::kDstATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
- { SkXfermode::kPlus_Mode, GL_ONE, GL_ONE },
- { SkXfermode::kModulate_Mode, GL_DST_COLOR, GL_ZERO },
- { SkXfermode::kScreen_Mode, GL_ONE_MINUS_DST_COLOR, GL_ONE }
-};
///////////////////////////////////////////////////////////////////////////////
// Functions
@@ -135,10 +86,10 @@
OpenGLRenderer::OpenGLRenderer(RenderState& renderState)
: mState(*this)
- , mFrameStarted(false)
, mCaches(Caches::getInstance())
, mExtensions(Extensions::getInstance())
, mRenderState(renderState)
+ , mFrameStarted(false)
, mScissorOptimizationDisabled(false)
, mSuppressTiling(false)
, mFirstFrameAfterResize(true)
@@ -151,7 +102,7 @@
memset(&mDrawModifiers, 0, sizeof(mDrawModifiers));
mDrawModifiers.mOverrideLayerAlpha = 1.0f;
- memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
+ memcpy(mMeshVertices, kMeshVertices, sizeof(kMeshVertices));
}
OpenGLRenderer::~OpenGLRenderer() {
@@ -233,7 +184,7 @@
// for each layer and wait until the first drawing command
// to start the frame
if (currentSnapshot()->fbo == 0) {
- syncState();
+ mRenderState.blend().syncEnabled();
updateLayers();
} else {
startFrame();
@@ -256,22 +207,14 @@
void OpenGLRenderer::clear(float left, float top, float right, float bottom, bool opaque) {
if (!opaque) {
- mCaches.enableScissor();
- mCaches.setScissor(left, getViewportHeight() - bottom, right - left, bottom - top);
+ mRenderState.scissor().setEnabled(true);
+ mRenderState.scissor().set(left, getViewportHeight() - bottom, right - left, bottom - top);
glClear(GL_COLOR_BUFFER_BIT);
mDirty = true;
return;
}
- mCaches.resetScissor();
-}
-
-void OpenGLRenderer::syncState() {
- if (mCaches.blend) {
- glEnable(GL_BLEND);
- } else {
- glDisable(GL_BLEND);
- }
+ mRenderState.scissor().reset();
}
void OpenGLRenderer::startTilingCurrentClip(bool opaque, bool expand) {
@@ -347,7 +290,7 @@
mRenderState.bindFramebuffer(currentSnapshot()->fbo);
debugOverdraw(true, false);
- mCaches.resetScissor();
+ mRenderState.scissor().reset();
dirtyClip();
}
@@ -378,7 +321,7 @@
if (mState.getDirtyClip()) {
setStencilFromClip(); // can issue draws, so must precede enableScissor()/interrupt()
}
- if (mCaches.enableScissor() || prevDirtyClip) {
+ if (mRenderState.scissor().setEnabled(true) || prevDirtyClip) {
setScissorFromClip();
}
@@ -428,27 +371,29 @@
if (mCaches.debugOverdraw && onGetTargetFbo() == 0) {
const Rect* clip = &mTilingClip;
- mCaches.enableScissor();
- mCaches.setScissor(clip->left, mState.firstSnapshot()->getViewportHeight() - clip->bottom,
- clip->right - clip->left, clip->bottom - clip->top);
+ mRenderState.scissor().setEnabled(true);
+ mRenderState.scissor().set(clip->left,
+ mState.firstSnapshot()->getViewportHeight() - clip->bottom,
+ clip->right - clip->left,
+ clip->bottom - clip->top);
// 1x overdraw
- mCaches.stencil.enableDebugTest(2);
+ mRenderState.stencil().enableDebugTest(2);
drawColor(mCaches.getOverdrawColor(1), SkXfermode::kSrcOver_Mode);
// 2x overdraw
- mCaches.stencil.enableDebugTest(3);
+ mRenderState.stencil().enableDebugTest(3);
drawColor(mCaches.getOverdrawColor(2), SkXfermode::kSrcOver_Mode);
// 3x overdraw
- mCaches.stencil.enableDebugTest(4);
+ mRenderState.stencil().enableDebugTest(4);
drawColor(mCaches.getOverdrawColor(3), SkXfermode::kSrcOver_Mode);
// 4x overdraw and higher
- mCaches.stencil.enableDebugTest(4, true);
+ mRenderState.stencil().enableDebugTest(4, true);
drawColor(mCaches.getOverdrawColor(4), SkXfermode::kSrcOver_Mode);
- mCaches.stencil.disable();
+ mRenderState.stencil().disable();
}
}
@@ -556,11 +501,11 @@
void OpenGLRenderer::flushLayerUpdates() {
ATRACE_NAME("Update HW Layers");
- syncState();
+ mRenderState.blend().syncEnabled();
updateLayers();
flushLayers();
// Wait for all the layer updates to be executed
- AutoFence fence;
+ glFinish();
}
void OpenGLRenderer::markLayersAsBuildLayers() {
@@ -753,7 +698,7 @@
return false;
}
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
Layer* layer = mCaches.layerCache.get(mRenderState, bounds.getWidth(), bounds.getHeight());
if (!layer) {
return false;
@@ -835,8 +780,8 @@
startTilingCurrentClip(true, true);
// Clear the FBO, expand the clear region by 1 to get nice bilinear filtering
- mCaches.enableScissor();
- mCaches.setScissor(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f,
+ mRenderState.scissor().setEnabled(true);
+ mRenderState.scissor().set(clip.left - 1.0f, bounds.getHeight() - clip.bottom - 1.0f,
clip.getWidth() + 2.0f, clip.getHeight() + 2.0f);
glClear(GL_COLOR_BUFFER_BIT);
@@ -863,7 +808,7 @@
bool clipRequired = false;
mState.calculateQuickRejectForScissor(rect.left, rect.top, rect.right, rect.bottom,
&clipRequired, nullptr, false); // safely ignore return, should never be rejected
- mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
+ mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired);
if (fboLayer) {
endTiling();
@@ -891,9 +836,9 @@
layer->setAlpha(255);
}
- mCaches.unbindMeshBuffer();
+ mRenderState.meshState().unbindMeshBuffer();
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
// When the layer is stored in an FBO, we can save a bit of fillrate by
// drawing only the dirty region
@@ -960,7 +905,7 @@
setupDrawTextureTransformUniforms(layer->getTexTransform());
setupDrawMesh(&mMeshVertices[0].x, &mMeshVertices[0].u);
- glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, kMeshCount);
}
void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) {
@@ -1002,7 +947,7 @@
drawTextureMesh(x, y, x + rect.getWidth(), y + rect.getHeight(),
layer->getTexture(), &layerPaint, blend,
&mMeshVertices[0].x, &mMeshVertices[0].u,
- GL_TRIANGLE_STRIP, gMeshCount, swap, swap || simpleTransform);
+ GL_TRIANGLE_STRIP, kMeshCount, swap, swap || simpleTransform);
resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
}
@@ -1168,7 +1113,7 @@
numQuads++;
- if (numQuads >= gMaxNumberOfQuads) {
+ if (numQuads >= kMaxNumberOfQuads) {
DRAW_DOUBLE_STENCIL(glDrawElements(GL_TRIANGLES, numQuads * 6,
GL_UNSIGNED_SHORT, nullptr));
numQuads = 0;
@@ -1261,7 +1206,7 @@
void OpenGLRenderer::issueIndexedQuadDraw(Vertex* mesh, GLsizei quadsCount) {
GLsizei elementsCount = quadsCount * 6;
while (elementsCount > 0) {
- GLsizei drawCount = min(elementsCount, (GLsizei) gMaxNumberOfQuads * 6);
+ GLsizei drawCount = min(elementsCount, (GLsizei) kMaxNumberOfQuads * 6);
setupDrawIndexedVertices(&mesh[0].x);
glDrawElements(GL_TRIANGLES, drawCount, GL_UNSIGNED_SHORT, nullptr);
@@ -1286,7 +1231,7 @@
// The list contains bounds that have already been clipped
// against their initial clip rect, and the current clip
// is likely different so we need to disable clipping here
- bool scissorChanged = mCaches.disableScissor();
+ bool scissorChanged = mRenderState.scissor().setEnabled(false);
Vertex mesh[count * 4];
Vertex* vertex = mesh;
@@ -1317,7 +1262,7 @@
issueIndexedQuadDraw(&mesh[0], count);
- if (scissorChanged) mCaches.enableScissor();
+ if (scissorChanged) mRenderState.scissor().setEnabled(true);
} else {
mLayers.clear();
}
@@ -1406,7 +1351,8 @@
writableSnapshot()->setClip(0, 0, mState.getWidth(), mState.getHeight());
}
dirtyClip();
- mCaches.setScissorEnabled(clipRect != nullptr || mScissorOptimizationDisabled);
+ bool enableScissor = (clipRect != nullptr) || mScissorOptimizationDisabled;
+ mRenderState.scissor().setEnabled(enableScissor);
}
///////////////////////////////////////////////////////////////////////////////
@@ -1417,7 +1363,7 @@
Rect clip(mState.currentClipRect());
clip.snapToPixelBoundaries();
- if (mCaches.setScissor(clip.left, getViewportHeight() - clip.bottom,
+ if (mRenderState.scissor().set(clip.left, getViewportHeight() - clip.bottom,
clip.getWidth(), clip.getHeight())) {
mState.setDirtyClip(false);
}
@@ -1491,7 +1437,7 @@
}
}
- mCaches.setScissor(scissorBox.left, getViewportHeight() - scissorBox.bottom,
+ mRenderState.scissor().set(scissorBox.left, getViewportHeight() - scissorBox.bottom,
scissorBox.getWidth(), scissorBox.getHeight());
const SkPaint* paint = nullptr;
@@ -1533,17 +1479,17 @@
incrementThreshold = 0;
}
- mCaches.stencil.enableWrite(incrementThreshold);
+ mRenderState.stencil().enableWrite(incrementThreshold);
// Clean and update the stencil, but first make sure we restrict drawing
// to the region's bounds
- bool resetScissor = mCaches.enableScissor();
+ bool resetScissor = mRenderState.scissor().setEnabled(true);
if (resetScissor) {
// The scissor was not set so we now need to update it
setScissorFromClip();
}
- mCaches.stencil.clear();
+ mRenderState.stencil().clear();
// stash and disable the outline clip state, since stencil doesn't account for outline
bool storedSkipOutlineClip = mSkipOutlineClip;
@@ -1564,10 +1510,10 @@
// so we don't want to dirty the current layer, if any
drawRegionRects(clipArea.getClipRegion(), paint, false);
}
- if (resetScissor) mCaches.disableScissor();
+ if (resetScissor) mRenderState.scissor().setEnabled(false);
mSkipOutlineClip = storedSkipOutlineClip;
- mCaches.stencil.enableTest(incrementThreshold);
+ mRenderState.stencil().enableTest(incrementThreshold);
// Draw the region used to generate the stencil if the appropriate debug
// mode is enabled
@@ -1580,7 +1526,7 @@
}
} else {
EVENT_LOGD("setStencilFromClip - disabling");
- mCaches.stencil.disable();
+ mRenderState.stencil().disable();
}
}
}
@@ -1611,7 +1557,7 @@
}
// not quick rejected, so enable the scissor if clipRequired
- mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
+ mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired);
mSkipOutlineClip = !roundRectClipRequired;
return false;
}
@@ -1638,7 +1584,7 @@
// Make sure setScissor & setStencil happen at the beginning of
// this method
if (mState.getDirtyClip()) {
- if (mCaches.scissorEnabled) {
+ if (mRenderState.scissor().isEnabled()) {
setScissorFromClip();
}
@@ -1657,7 +1603,7 @@
// the stencil buffer and if stencil highlight debugging is on
mDescription.hasDebugHighlight = !mCaches.debugOverdraw &&
mCaches.debugStencilClip == Caches::kStencilShowHighlight &&
- mCaches.stencil.isTestEnabled();
+ mRenderState.stencil().isTestEnabled();
}
void OpenGLRenderer::setupDrawWithTexture(bool isAlpha8) {
@@ -1676,7 +1622,7 @@
}
void OpenGLRenderer::setupDrawNoTexture() {
- mCaches.disableTexCoordsVertexArray();
+ mRenderState.meshState().disableTexCoordsVertexArray();
}
void OpenGLRenderer::setupDrawVertexAlpha(bool useShadowAlphaInterp) {
@@ -1888,21 +1834,21 @@
}
void OpenGLRenderer::setupDrawSimpleMesh() {
- bool force = mCaches.bindMeshBuffer();
- mCaches.bindPositionVertexPointer(force, nullptr);
- mCaches.unbindIndicesBuffer();
+ bool force = mRenderState.meshState().bindMeshBuffer();
+ mRenderState.meshState().bindPositionVertexPointer(mCaches.currentProgram, force, nullptr);
+ mRenderState.meshState().unbindIndicesBuffer();
}
void OpenGLRenderer::setupDrawTexture(GLuint texture) {
- if (texture) bindTexture(texture);
+ if (texture) mCaches.textureState().bindTexture(texture);
mTextureUnit++;
- mCaches.enableTexCoordsVertexArray();
+ mRenderState.meshState().enableTexCoordsVertexArray();
}
void OpenGLRenderer::setupDrawExternalTexture(GLuint texture) {
- bindExternalTexture(texture);
+ mCaches.textureState().bindTexture(GL_TEXTURE_EXTERNAL_OES, texture);
mTextureUnit++;
- mCaches.enableTexCoordsVertexArray();
+ mRenderState.meshState().enableTexCoordsVertexArray();
}
void OpenGLRenderer::setupDrawTextureTransform() {
@@ -1918,27 +1864,29 @@
const GLvoid* texCoords, GLuint vbo) {
bool force = false;
if (!vertices || vbo) {
- force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo);
+ force = mRenderState.meshState().bindMeshBuffer(vbo);
} else {
- force = mCaches.unbindMeshBuffer();
+ force = mRenderState.meshState().unbindMeshBuffer();
}
- mCaches.bindPositionVertexPointer(force, vertices);
+ mRenderState.meshState().bindPositionVertexPointer(mCaches.currentProgram, force, vertices);
if (mCaches.currentProgram->texCoords >= 0) {
- mCaches.bindTexCoordsVertexPointer(force, texCoords);
+ mRenderState.meshState().bindTexCoordsVertexPointer(mCaches.currentProgram, force, texCoords);
}
- mCaches.unbindIndicesBuffer();
+ mRenderState.meshState().unbindIndicesBuffer();
}
void OpenGLRenderer::setupDrawMesh(const GLvoid* vertices,
const GLvoid* texCoords, const GLvoid* colors) {
- bool force = mCaches.unbindMeshBuffer();
+ bool force = mRenderState.meshState().unbindMeshBuffer();
GLsizei stride = sizeof(ColorTextureVertex);
- mCaches.bindPositionVertexPointer(force, vertices, stride);
+ mRenderState.meshState().bindPositionVertexPointer(mCaches.currentProgram, force,
+ vertices, stride);
if (mCaches.currentProgram->texCoords >= 0) {
- mCaches.bindTexCoordsVertexPointer(force, texCoords, stride);
+ mRenderState.meshState().bindTexCoordsVertexPointer(mCaches.currentProgram, force,
+ texCoords, stride);
}
int slot = mCaches.currentProgram->getAttrib("colors");
if (slot >= 0) {
@@ -1946,7 +1894,7 @@
glVertexAttribPointer(slot, 4, GL_FLOAT, GL_FALSE, stride, colors);
}
- mCaches.unbindIndicesBuffer();
+ mRenderState.meshState().unbindIndicesBuffer();
}
void OpenGLRenderer::setupDrawMeshIndices(const GLvoid* vertices,
@@ -1954,24 +1902,26 @@
bool force = false;
// If vbo is != 0 we want to treat the vertices parameter as an offset inside
// a VBO. However, if vertices is set to NULL and vbo == 0 then we want to
- // use the default VBO found in Caches
+ // use the default VBO found in RenderState
if (!vertices || vbo) {
- force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo);
+ force = mRenderState.meshState().bindMeshBuffer(vbo);
} else {
- force = mCaches.unbindMeshBuffer();
+ force = mRenderState.meshState().unbindMeshBuffer();
}
- mCaches.bindQuadIndicesBuffer();
+ mRenderState.meshState().bindQuadIndicesBuffer();
- mCaches.bindPositionVertexPointer(force, vertices);
+ mRenderState.meshState().bindPositionVertexPointer(mCaches.currentProgram, force, vertices);
if (mCaches.currentProgram->texCoords >= 0) {
- mCaches.bindTexCoordsVertexPointer(force, texCoords);
+ mRenderState.meshState().bindTexCoordsVertexPointer(mCaches.currentProgram,
+ force, texCoords);
}
}
void OpenGLRenderer::setupDrawIndexedVertices(GLvoid* vertices) {
- bool force = mCaches.unbindMeshBuffer();
- mCaches.bindQuadIndicesBuffer();
- mCaches.bindPositionVertexPointer(force, vertices, gVertexStride);
+ bool force = mRenderState.meshState().unbindMeshBuffer();
+ mRenderState.meshState().bindQuadIndicesBuffer();
+ mRenderState.meshState().bindPositionVertexPointer(mCaches.currentProgram, force,
+ vertices, kVertexStride);
}
///////////////////////////////////////////////////////////////////////////////
@@ -2030,8 +1980,8 @@
// No need to check for a UV mapper on the texture object, only ARGB_8888
// bitmaps get packed in the atlas
drawAlpha8TextureMesh(x, y, x + texture->width, y + texture->height, texture->id,
- paint, (GLvoid*) nullptr, (GLvoid*) gMeshTextureOffset,
- GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform);
+ paint, (GLvoid*) nullptr, (GLvoid*) kMeshTextureOffset,
+ GL_TRIANGLE_STRIP, kMeshCount, ignoreTransform);
}
/**
@@ -2042,7 +1992,7 @@
void OpenGLRenderer::drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry,
int bitmapCount, TextureVertex* vertices, bool pureTranslate,
const Rect& bounds, const SkPaint* paint) {
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
if (!texture) return;
@@ -2073,7 +2023,7 @@
return;
}
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
Texture* texture = getTexture(bitmap);
if (!texture) return;
const AutoTexture autoCleanup(texture);
@@ -2094,7 +2044,7 @@
}
// TODO: use quickReject on bounds from vertices
- mCaches.enableScissor();
+ mRenderState.scissor().setEnabled(true);
float left = FLT_MAX;
float top = FLT_MAX;
@@ -2114,7 +2064,7 @@
colors = tempColors.get();
}
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap);
const UvMapper& mapper(getMapper(texture));
@@ -2209,7 +2159,7 @@
return;
}
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
Texture* texture = getTexture(bitmap);
if (!texture) return;
const AutoTexture autoCleanup(texture);
@@ -2224,7 +2174,7 @@
getMapper(texture).map(u1, v1, u2, v2);
- mCaches.unbindMeshBuffer();
+ mRenderState.meshState().unbindMeshBuffer();
resetDrawTextureTexCoords(u1, v1, u2, v2);
texture->setWrap(GL_CLAMP_TO_EDGE, true);
@@ -2271,12 +2221,12 @@
drawAlpha8TextureMesh(dstLeft, dstTop, dstRight, dstBottom,
texture->id, paint,
&mMeshVertices[0].x, &mMeshVertices[0].u,
- GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform);
+ GL_TRIANGLE_STRIP, kMeshCount, ignoreTransform);
} else {
drawTextureMesh(dstLeft, dstTop, dstRight, dstBottom,
texture->id, paint, texture->blend,
&mMeshVertices[0].x, &mMeshVertices[0].u,
- GL_TRIANGLE_STRIP, gMeshCount, false, ignoreTransform);
+ GL_TRIANGLE_STRIP, kMeshCount, false, ignoreTransform);
}
if (CC_UNLIKELY(useScaleTransform)) {
@@ -2309,7 +2259,7 @@
}
if (CC_LIKELY(mesh && mesh->verticesCount > 0)) {
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
if (!texture) return;
const AutoTexture autoCleanup(texture);
@@ -2363,7 +2313,7 @@
*/
void OpenGLRenderer::drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* entry,
TextureVertex* vertices, uint32_t indexCount, const SkPaint* paint) {
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
if (!texture) return;
const AutoTexture autoCleanup(texture);
@@ -2408,33 +2358,34 @@
setupDrawShaderUniforms(getShader(paint));
const void* vertices = vertexBuffer.getBuffer();
- mCaches.unbindMeshBuffer();
- mCaches.bindPositionVertexPointer(true, vertices, isAA ? gAlphaVertexStride : gVertexStride);
- mCaches.resetTexCoordsVertexPointer();
+ mRenderState.meshState().unbindMeshBuffer();
+ mRenderState.meshState().bindPositionVertexPointer(mCaches.currentProgram,
+ true, vertices, isAA ? kAlphaVertexStride : kVertexStride);
+ mRenderState.meshState().resetTexCoordsVertexPointer();
int alphaSlot = -1;
if (isAA) {
- void* alphaCoords = ((GLbyte*) vertices) + gVertexAlphaOffset;
+ void* alphaCoords = ((GLbyte*) vertices) + kVertexAlphaOffset;
alphaSlot = mCaches.currentProgram->getAttrib("vtxAlpha");
// TODO: avoid enable/disable in back to back uses of the alpha attribute
glEnableVertexAttribArray(alphaSlot);
- glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, alphaCoords);
+ glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, kAlphaVertexStride, alphaCoords);
}
const VertexBuffer::Mode mode = vertexBuffer.getMode();
if (mode == VertexBuffer::kStandard) {
- mCaches.unbindIndicesBuffer();
+ mRenderState.meshState().unbindIndicesBuffer();
glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getVertexCount());
} else if (mode == VertexBuffer::kOnePolyRingShadow) {
- mCaches.bindShadowIndicesBuffer();
+ mRenderState.meshState().bindShadowIndicesBuffer();
glDrawElements(GL_TRIANGLE_STRIP, ONE_POLY_RING_SHADOW_INDEX_COUNT,
GL_UNSIGNED_SHORT, nullptr);
} else if (mode == VertexBuffer::kTwoPolyRingShadow) {
- mCaches.bindShadowIndicesBuffer();
+ mRenderState.meshState().bindShadowIndicesBuffer();
glDrawElements(GL_TRIANGLE_STRIP, TWO_POLY_RING_SHADOW_INDEX_COUNT,
GL_UNSIGNED_SHORT, nullptr);
} else if (mode == VertexBuffer::kIndices) {
- mCaches.unbindIndicesBuffer();
+ mRenderState.meshState().unbindIndicesBuffer();
glDrawElements(GL_TRIANGLE_STRIP, vertexBuffer.getIndexCount(),
GL_UNSIGNED_SHORT, vertexBuffer.getIndices());
}
@@ -2547,7 +2498,7 @@
}
if (p->getPathEffect() != nullptr) {
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
const PathTexture* texture = mCaches.pathCache.getRoundRect(
right - left, bottom - top, rx, ry, p);
drawShape(left, top, texture, p);
@@ -2565,7 +2516,7 @@
return;
}
if (p->getPathEffect() != nullptr) {
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
const PathTexture* texture = mCaches.pathCache.getCircle(radius, p);
drawShape(x - radius, y - radius, texture, p);
} else {
@@ -2588,7 +2539,7 @@
}
if (p->getPathEffect() != nullptr) {
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
const PathTexture* texture = mCaches.pathCache.getOval(right - left, bottom - top, p);
drawShape(left, top, texture, p);
} else {
@@ -2612,7 +2563,7 @@
// TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != nullptr || useCenter) {
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
const PathTexture* texture = mCaches.pathCache.getArc(right - left, bottom - top,
startAngle, sweepAngle, useCenter, p);
drawShape(left, top, texture, p);
@@ -2649,7 +2600,7 @@
// only fill style is supported by drawConvexPath, since others have to handle joins
if (p->getPathEffect() != nullptr || p->getStrokeJoin() != SkPaint::kMiter_Join ||
p->getStrokeMiter() != SkPaintDefaults_MiterLimit) {
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
const PathTexture* texture =
mCaches.pathCache.getRect(right - left, bottom - top, p);
drawShape(left, top, texture, p);
@@ -2678,7 +2629,7 @@
void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text,
int bytesCount, int count, const float* positions,
FontRenderer& fontRenderer, int alpha, float x, float y) {
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
TextShadow textShadow;
if (!getTextShadow(paint, &textShadow)) {
@@ -2716,9 +2667,9 @@
setupDrawPureColorUniforms();
setupDrawColorFilterUniforms(getColorFilter(paint));
setupDrawShaderUniforms(getShader(paint));
- setupDrawMesh(nullptr, (GLvoid*) gMeshTextureOffset);
+ setupDrawMesh(nullptr, (GLvoid*) kMeshTextureOffset);
- glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, kMeshCount);
}
bool OpenGLRenderer::canSkipText(const SkPaint* paint) const {
@@ -2738,7 +2689,7 @@
return;
}
- mCaches.enableScissor();
+ mRenderState.scissor().setEnabled(true);
float x = 0.0f;
float y = 0.0f;
@@ -2868,8 +2819,6 @@
mState.setClippingRoundRect(allocator, rect, radius, highPriority);
}
-
-
void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y,
const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
DrawOpMode drawOpMode) {
@@ -2964,7 +2913,7 @@
}
// TODO: avoid scissor by calculating maximum bounds using path bounds + font metrics
- mCaches.enableScissor();
+ mRenderState.scissor().setEnabled(true);
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
fontRenderer.setFont(paint, SkMatrix::I());
@@ -2994,7 +2943,7 @@
void OpenGLRenderer::drawPath(const SkPath* path, const SkPaint* paint) {
if (mState.currentlyIgnored()) return;
- mCaches.activeTexture(0);
+ mCaches.textureState().activateTexture(0);
const PathTexture* texture = mCaches.pathCache.get(path, paint);
if (!texture) return;
@@ -3038,8 +2987,8 @@
updateLayer(layer, true);
- mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
- mCaches.activeTexture(0);
+ mRenderState.scissor().setEnabled(mScissorOptimizationDisabled || clipRequired);
+ mCaches.textureState().activateTexture(0);
if (CC_LIKELY(!layer->region.isEmpty())) {
if (layer->region.isRect()) {
@@ -3074,7 +3023,7 @@
GLsizei elementsCount = layer->meshElementCount;
while (elementsCount > 0) {
- GLsizei drawCount = min(elementsCount, (GLsizei) gMaxNumberOfQuads * 6);
+ GLsizei drawCount = min(elementsCount, (GLsizei) kMaxNumberOfQuads * 6);
setupDrawMeshIndices(&mesh[0].x, &mesh[0].u);
DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate,
@@ -3152,9 +3101,9 @@
setupDrawPureColorUniforms();
setupDrawColorFilterUniforms(getColorFilter(paint));
setupDrawShaderUniforms(getShader(paint));
- setupDrawMesh(nullptr, (GLvoid*) gMeshTextureOffset);
+ setupDrawMesh(nullptr, (GLvoid*) kMeshTextureOffset);
- glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, kMeshCount);
}
// Same values used by Skia
@@ -3220,7 +3169,7 @@
if (mState.currentlyIgnored()) return;
// TODO: use quickRejectWithScissor. For now, always force enable scissor.
- mCaches.enableScissor();
+ mRenderState.scissor().setEnabled(true);
SkPaint paint;
paint.setAntiAlias(true); // want to use AlphaVertex
@@ -3333,7 +3282,7 @@
setupDrawColorFilterUniforms(getColorFilter(paint));
setupDrawSimpleMesh();
- glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, kMeshCount);
}
void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
@@ -3341,7 +3290,7 @@
texture->setWrap(GL_CLAMP_TO_EDGE, true);
GLvoid* vertices = (GLvoid*) nullptr;
- GLvoid* texCoords = (GLvoid*) gMeshTextureOffset;
+ GLvoid* texCoords = (GLvoid*) kMeshTextureOffset;
if (texture->uvMapper) {
vertices = &mMeshVertices[0].x;
@@ -3360,11 +3309,11 @@
texture->setFilter(GL_NEAREST, true);
drawTextureMesh(x, y, x + texture->width, y + texture->height, texture->id,
paint, texture->blend, vertices, texCoords,
- GL_TRIANGLE_STRIP, gMeshCount, false, true);
+ GL_TRIANGLE_STRIP, kMeshCount, false, true);
} else {
texture->setFilter(getFilter(paint), true);
drawTextureMesh(left, top, right, bottom, texture->id, paint,
- texture->blend, vertices, texCoords, GL_TRIANGLE_STRIP, gMeshCount);
+ texture->blend, vertices, texCoords, GL_TRIANGLE_STRIP, kMeshCount);
}
if (texture->uvMapper) {
@@ -3478,33 +3427,16 @@
description.framebufferMode = mode;
description.swapSrcDst = swapSrcDst;
- if (mCaches.blend) {
- glDisable(GL_BLEND);
- mCaches.blend = false;
- }
-
+ mRenderState.blend().disable();
return;
} else {
mode = SkXfermode::kSrcOver_Mode;
}
}
-
- if (!mCaches.blend) {
- glEnable(GL_BLEND);
- }
-
- GLenum sourceMode = swapSrcDst ? gBlendsSwap[mode].src : gBlends[mode].src;
- GLenum destMode = swapSrcDst ? gBlendsSwap[mode].dst : gBlends[mode].dst;
-
- if (sourceMode != mCaches.lastSrcMode || destMode != mCaches.lastDstMode) {
- glBlendFunc(sourceMode, destMode);
- mCaches.lastSrcMode = sourceMode;
- mCaches.lastDstMode = destMode;
- }
- } else if (mCaches.blend) {
- glDisable(GL_BLEND);
+ mRenderState.blend().enable(mode, swapSrcDst);
+ } else {
+ mRenderState.blend().disable();
}
- mCaches.blend = blend;
}
bool OpenGLRenderer::useProgram(Program* program) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 76ad6ce..cf6f0c8 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -222,6 +222,10 @@
return mCaches;
}
+ RenderState& renderState() {
+ return mRenderState;
+ }
+
int getViewportWidth() { return mState.getViewportWidth(); }
int getViewportHeight() { return mState.getViewportHeight(); }
@@ -523,9 +527,10 @@
return false;
}
- inline RenderState& renderState() { return mRenderState; }
-
CanvasState mState;
+ Caches& mCaches;
+ Extensions& mExtensions; // TODO: move to RenderState
+ RenderState& mRenderState;
private:
/**
@@ -536,12 +541,6 @@
void discardFramebuffer(float left, float top, float right, float bottom);
/**
- * Ensures the state of the renderer is the same as the state of
- * the GL context.
- */
- void syncState();
-
- /**
* Tells the GPU what part of the screen is about to be redrawn.
* This method will use the current layer space clip rect.
* This method needs to be invoked every time getTargetFbo() is
@@ -847,22 +846,6 @@
bool canSkipText(const SkPaint* paint) const;
/**
- * Binds the specified texture. The texture unit must have been selected
- * prior to calling this method.
- */
- inline void bindTexture(GLuint texture) {
- mCaches.bindTexture(texture);
- }
-
- /**
- * Binds the specified EGLImage texture. The texture unit must have been selected
- * prior to calling this method.
- */
- inline void bindExternalTexture(GLuint texture) {
- mCaches.bindTexture(GL_TEXTURE_EXTERNAL_OES, texture);
- }
-
- /**
* Enable or disable blending as necessary. This function sets the appropriate
* blend function based on the specified xfermode.
*/
@@ -1027,11 +1010,6 @@
DrawModifiers mDrawModifiers;
SkPaint mFilteredPaint;
- // Various caches
- Caches& mCaches;
- Extensions& mExtensions;
- RenderState& mRenderState;
-
// List of rectangles to clear after saveLayer() is invoked
std::vector<Rect> mLayers;
// List of layers to update at the beginning of a frame
diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp
index eb88bc0..af403b4 100644
--- a/libs/hwui/PatchCache.cpp
+++ b/libs/hwui/PatchCache.cpp
@@ -23,6 +23,7 @@
#include "Patch.h"
#include "PatchCache.h"
#include "Properties.h"
+#include "renderstate/RenderState.h"
namespace android {
namespace uirenderer {
@@ -31,9 +32,13 @@
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
-PatchCache::PatchCache():
- mSize(0), mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity),
- mMeshBuffer(0), mFreeBlocks(nullptr), mGenerationId(0) {
+PatchCache::PatchCache(RenderState& renderState)
+ : mRenderState(renderState)
+ , mSize(0)
+ , mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity)
+ , mMeshBuffer(0)
+ , mFreeBlocks(nullptr)
+ , mGenerationId(0) {
char property[PROPERTY_VALUE_MAX];
if (property_get(PROPERTY_PATCH_CACHE_SIZE, property, nullptr) > 0) {
INIT_LOGD(" Setting patch cache size to %skB", property);
@@ -48,15 +53,15 @@
clear();
}
-void PatchCache::init(Caches& caches) {
+void PatchCache::init() {
bool created = false;
if (!mMeshBuffer) {
glGenBuffers(1, &mMeshBuffer);
created = true;
}
- caches.bindMeshBuffer(mMeshBuffer);
- caches.resetVertexPointers();
+ mRenderState.meshState().bindMeshBuffer(mMeshBuffer);
+ mRenderState.meshState().resetVertexPointers();
if (created) {
createVertexBuffer();
@@ -85,7 +90,7 @@
clearCache();
if (mMeshBuffer) {
- Caches::getInstance().unbindMeshBuffer();
+ mRenderState.meshState().unbindMeshBuffer();
glDeleteBuffers(1, &mMeshBuffer);
mMeshBuffer = 0;
mSize = 0;
@@ -187,7 +192,7 @@
*/
void PatchCache::setupMesh(Patch* newMesh, TextureVertex* vertices) {
// This call ensures the VBO exists and that it is bound
- init(Caches::getInstance());
+ init();
// If we're running out of space, let's clear the entire cache
uint32_t size = newMesh->getSize();
@@ -219,7 +224,7 @@
// Copy the 9patch mesh in the VBO
newMesh->offset = (GLintptr) (block->offset);
- newMesh->textureOffset = newMesh->offset + gMeshTextureOffset;
+ newMesh->textureOffset = newMesh->offset + kMeshTextureOffset;
glBufferSubData(GL_ARRAY_BUFFER, newMesh->offset, size, vertices);
// Remove the block since we've used it entirely
diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h
index 4cb5338..e038720 100644
--- a/libs/hwui/PatchCache.h
+++ b/libs/hwui/PatchCache.h
@@ -51,9 +51,9 @@
class PatchCache {
public:
- PatchCache();
+ PatchCache(RenderState& renderState);
~PatchCache();
- void init(Caches& caches);
+ void init();
const Patch* get(const AssetAtlas::Entry* entry,
const uint32_t bitmapWidth, const uint32_t bitmapHeight,
@@ -168,6 +168,7 @@
void dumpFreeBlocks(const char* prefix);
#endif
+ RenderState& mRenderState;
uint32_t mMaxSize;
uint32_t mSize;
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index c564b87..d6eff85 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -31,7 +31,6 @@
#include "PathCache.h"
#include "thread/Signal.h"
-#include "thread/Task.h"
#include "thread/TaskProcessor.h"
namespace android {
@@ -231,7 +230,7 @@
}
if (texture->id) {
- Caches::getInstance().deleteTexture(texture->id);
+ Caches::getInstance().textureState().deleteTexture(texture->id);
}
delete texture;
}
@@ -313,7 +312,7 @@
glGenTextures(1, &texture->id);
- Caches::getInstance().bindTexture(texture->id);
+ Caches::getInstance().textureState().bindTexture(texture->id);
// Textures are Alpha8
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index ecd3712..7378018 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -17,21 +17,22 @@
#ifndef ANDROID_HWUI_PATH_CACHE_H
#define ANDROID_HWUI_PATH_CACHE_H
-#include <GLES2/gl2.h>
+#include "Debug.h"
+#include "Texture.h"
+#include "thread/Task.h"
+#include "thread/TaskProcessor.h"
+#include "utils/Macros.h"
+#include "utils/Pair.h"
+#include <GLES2/gl2.h>
+#include <SkPath.h>
#include <utils/LruCache.h>
#include <utils/Mutex.h>
#include <utils/Vector.h>
-#include "Debug.h"
-#include "Texture.h"
-#include "utils/Macros.h"
-#include "utils/Pair.h"
-
class SkBitmap;
class SkCanvas;
class SkPaint;
-class SkPath;
struct SkRect;
namespace android {
diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp
index efa271e..9665a68 100644
--- a/libs/hwui/PixelBuffer.cpp
+++ b/libs/hwui/PixelBuffer.cpp
@@ -16,13 +16,14 @@
#define LOG_TAG "OpenGLRenderer"
-#include <utils/Log.h>
+#include "PixelBuffer.h"
-#include "Caches.h"
#include "Debug.h"
#include "Extensions.h"
-#include "PixelBuffer.h"
#include "Properties.h"
+#include "renderstate/RenderState.h"
+
+#include <utils/Log.h>
namespace android {
namespace uirenderer {
@@ -93,14 +94,16 @@
Caches& mCaches;
};
-GpuPixelBuffer::GpuPixelBuffer(GLenum format, uint32_t width, uint32_t height)
+GpuPixelBuffer::GpuPixelBuffer(GLenum format,
+ uint32_t width, uint32_t height)
: PixelBuffer(format, width, height)
, mMappedPointer(nullptr)
- , mCaches(Caches::getInstance()) {
+ , mCaches(Caches::getInstance()){
glGenBuffers(1, &mBuffer);
- mCaches.bindPixelBuffer(mBuffer);
+
+ mCaches.pixelBufferState().bind(mBuffer);
glBufferData(GL_PIXEL_UNPACK_BUFFER, getSize(), nullptr, GL_DYNAMIC_DRAW);
- mCaches.unbindPixelBuffer();
+ mCaches.pixelBufferState().unbind();
}
GpuPixelBuffer::~GpuPixelBuffer() {
@@ -109,7 +112,7 @@
uint8_t* GpuPixelBuffer::map(AccessMode mode) {
if (mAccessMode == kAccessMode_None) {
- mCaches.bindPixelBuffer(mBuffer);
+ mCaches.pixelBufferState().bind(mBuffer);
mMappedPointer = (uint8_t*) glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, getSize(), mode);
#if DEBUG_OPENGL
if (!mMappedPointer) {
@@ -128,7 +131,7 @@
void GpuPixelBuffer::unmap() {
if (mAccessMode != kAccessMode_None) {
if (mMappedPointer) {
- mCaches.bindPixelBuffer(mBuffer);
+ mCaches.pixelBufferState().bind(mBuffer);
GLboolean status = glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
if (status == GL_FALSE) {
ALOGE("Corrupted GPU pixel buffer");
@@ -145,7 +148,7 @@
void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) {
// If the buffer is not mapped, unmap() will not bind it
- mCaches.bindPixelBuffer(mBuffer);
+ mCaches.pixelBufferState().bind(mBuffer);
unmap();
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, mFormat,
GL_UNSIGNED_BYTE, reinterpret_cast<void*>(offset));
@@ -155,7 +158,8 @@
// Factory
///////////////////////////////////////////////////////////////////////////////
-PixelBuffer* PixelBuffer::create(GLenum format, uint32_t width, uint32_t height, BufferType type) {
+PixelBuffer* PixelBuffer::create(GLenum format,
+ uint32_t width, uint32_t height, BufferType type) {
if (type == kBufferType_Auto && Caches::getInstance().gpuPixelBuffersEnabled) {
return new GpuPixelBuffer(format, width, height);
}
diff --git a/libs/hwui/PixelBuffer.h b/libs/hwui/PixelBuffer.h
index 04225a2..aac5ec4 100644
--- a/libs/hwui/PixelBuffer.h
+++ b/libs/hwui/PixelBuffer.h
@@ -18,6 +18,7 @@
#define ANDROID_HWUI_PIXEL_BUFFER_H
#include <GLES3/gl3.h>
+#include <cutils/log.h>
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 7fdbf9a..efbb709 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -46,82 +46,87 @@
canvas->ref();
}
- virtual SkCanvas* getSkCanvas() {
+ virtual SkCanvas* asSkCanvas() override {
return mCanvas.get();
}
- virtual void setBitmap(SkBitmap* bitmap, bool copyState);
+ virtual void setBitmap(SkBitmap* bitmap, bool copyState) override;
- virtual bool isOpaque();
- virtual int width();
- virtual int height();
+ virtual bool isOpaque() override;
+ virtual int width() override;
+ virtual int height() override;
- virtual int getSaveCount() const;
- virtual int save(SkCanvas::SaveFlags flags);
- virtual void restore();
- virtual void restoreToCount(int saveCount);
+ virtual int getSaveCount() const override;
+ virtual int save(SkCanvas::SaveFlags flags) override;
+ virtual void restore() override;
+ virtual void restoreToCount(int saveCount) override;
virtual int saveLayer(float left, float top, float right, float bottom,
- const SkPaint* paint, SkCanvas::SaveFlags flags);
+ const SkPaint* paint, SkCanvas::SaveFlags flags) override;
virtual int saveLayerAlpha(float left, float top, float right, float bottom,
- int alpha, SkCanvas::SaveFlags flags);
+ int alpha, SkCanvas::SaveFlags flags) override;
- virtual void getMatrix(SkMatrix* outMatrix) const;
- virtual void setMatrix(const SkMatrix& matrix);
- virtual void concat(const SkMatrix& matrix);
- virtual void rotate(float degrees);
- virtual void scale(float sx, float sy);
- virtual void skew(float sx, float sy);
- virtual void translate(float dx, float dy);
+ virtual void getMatrix(SkMatrix* outMatrix) const override;
+ virtual void setMatrix(const SkMatrix& matrix) override;
+ virtual void concat(const SkMatrix& matrix) override;
+ virtual void rotate(float degrees) override;
+ virtual void scale(float sx, float sy) override;
+ virtual void skew(float sx, float sy) override;
+ virtual void translate(float dx, float dy) override;
- virtual bool getClipBounds(SkRect* outRect) const;
- virtual bool quickRejectRect(float left, float top, float right, float bottom) const;
- virtual bool quickRejectPath(const SkPath& path) const;
- virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
- virtual bool clipPath(const SkPath* path, SkRegion::Op op);
- virtual bool clipRegion(const SkRegion* region, SkRegion::Op op);
+ virtual bool getClipBounds(SkRect* outRect) const override;
+ virtual bool quickRejectRect(float left, float top, float right, float bottom) const override;
+ virtual bool quickRejectPath(const SkPath& path) const override;
+ virtual bool clipRect(float left, float top, float right, float bottom,
+ SkRegion::Op op) override;
+ virtual bool clipPath(const SkPath* path, SkRegion::Op op) override;
+ virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) override;
- virtual SkDrawFilter* getDrawFilter();
- virtual void setDrawFilter(SkDrawFilter* drawFilter);
+ virtual SkDrawFilter* getDrawFilter() override;
+ virtual void setDrawFilter(SkDrawFilter* drawFilter) override;
- virtual void drawColor(int color, SkXfermode::Mode mode);
- virtual void drawPaint(const SkPaint& paint);
+ virtual void drawColor(int color, SkXfermode::Mode mode) override;
+ virtual void drawPaint(const SkPaint& paint) override;
- virtual void drawPoint(float x, float y, const SkPaint& paint);
- virtual void drawPoints(const float* points, int count, const SkPaint& paint);
+ virtual void drawPoint(float x, float y, const SkPaint& paint) override;
+ virtual void drawPoints(const float* points, int count, const SkPaint& paint) override;
virtual void drawLine(float startX, float startY, float stopX, float stopY,
- const SkPaint& paint);
- virtual void drawLines(const float* points, int count, const SkPaint& paint);
- virtual void drawRect(float left, float top, float right, float bottom, const SkPaint& paint);
+ const SkPaint& paint) override;
+ virtual void drawLines(const float* points, int count, const SkPaint& paint) override;
+ virtual void drawRect(float left, float top, float right, float bottom,
+ const SkPaint& paint) override;
virtual void drawRoundRect(float left, float top, float right, float bottom,
- float rx, float ry, const SkPaint& paint);
- virtual void drawCircle(float x, float y, float radius, const SkPaint& paint);
- virtual void drawOval(float left, float top, float right, float bottom, const SkPaint& paint);
+ float rx, float ry, const SkPaint& paint) override;
+ virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) override;
+ virtual void drawOval(float left, float top, float right, float bottom,
+ const SkPaint& paint) override;
virtual void drawArc(float left, float top, float right, float bottom,
- float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint);
- virtual void drawPath(const SkPath& path, const SkPaint& paint);
+ float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) override;
+ virtual void drawPath(const SkPath& path, const SkPaint& paint) override;
virtual void drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount,
const float* verts, const float* tex, const int* colors,
- const uint16_t* indices, int indexCount, const SkPaint& paint);
+ const uint16_t* indices, int indexCount, const SkPaint& paint) override;
- virtual void drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint);
- virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint);
+ virtual void drawBitmap(const SkBitmap& bitmap, float left, float top,
+ const SkPaint* paint) override;
+ virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+ const SkPaint* paint) override;
virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
- float dstRight, float dstBottom, const SkPaint* paint);
+ float dstRight, float dstBottom, const SkPaint* paint) override;
virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
- const float* vertices, const int* colors, const SkPaint* paint);
+ const float* vertices, const int* colors, const SkPaint* paint) override;
virtual void drawText(const uint16_t* text, const float* positions, int count,
const SkPaint& paint, float x, float y,
float boundsLeft, float boundsTop, float boundsRight, float boundsBottom,
- float totalAdvance);
+ float totalAdvance) override;
virtual void drawPosText(const uint16_t* text, const float* positions, int count,
- int posCount, const SkPaint& paint);
+ int posCount, const SkPaint& paint) override;
virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path,
- float hOffset, float vOffset, const SkPaint& paint);
+ float hOffset, float vOffset, const SkPaint& paint) override;
- virtual bool drawTextAbsolutePos() const { return true; }
+ virtual bool drawTextAbsolutePos() const override { return true; }
private:
struct SaveRec {
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
new file mode 100644
index 0000000..de5f91c
--- /dev/null
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkiaCanvasProxy.h"
+
+#include <cutils/log.h>
+#include <SkPatchUtils.h>
+
+namespace android {
+namespace uirenderer {
+
+SkiaCanvasProxy::SkiaCanvasProxy(Canvas* canvas)
+ : INHERITED(canvas->width(), canvas->height())
+ , mCanvas(canvas) {}
+
+void SkiaCanvasProxy::onDrawPaint(const SkPaint& paint) {
+ mCanvas->drawPaint(paint);
+}
+
+void SkiaCanvasProxy::onDrawPoints(PointMode pointMode, size_t count, const SkPoint pts[],
+ const SkPaint& paint) {
+ // convert the SkPoints into floats
+ SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+ const size_t floatCount = count << 1;
+ const float* floatArray = &pts[0].fX;
+
+ switch (pointMode) {
+ case kPoints_PointMode: {
+ mCanvas->drawPoints(floatArray, floatCount, paint);
+ break;
+ }
+ case kLines_PointMode: {
+ mCanvas->drawLines(floatArray, floatCount, paint);
+ break;
+ }
+ case kPolygon_PointMode: {
+ SkPaint strokedPaint(paint);
+ strokedPaint.setStyle(SkPaint::kStroke_Style);
+
+ SkPath path;
+ for (size_t i = 0; i < count - 1; i++) {
+ path.moveTo(pts[i]);
+ path.lineTo(pts[i+1]);
+ this->drawPath(path, strokedPaint);
+ path.rewind();
+ }
+ break;
+ }
+ default:
+ LOG_ALWAYS_FATAL("Unknown point type");
+ }
+}
+
+void SkiaCanvasProxy::onDrawOval(const SkRect& rect, const SkPaint& paint) {
+ mCanvas->drawOval(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint);
+}
+
+void SkiaCanvasProxy::onDrawRect(const SkRect& rect, const SkPaint& paint) {
+ mCanvas->drawRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint);
+}
+
+void SkiaCanvasProxy::onDrawRRect(const SkRRect& roundRect, const SkPaint& paint) {
+ if (!roundRect.isComplex()) {
+ const SkRect& rect = roundRect.rect();
+ SkVector radii = roundRect.getSimpleRadii();
+ mCanvas->drawRoundRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom,
+ radii.fX, radii.fY, paint);
+ } else {
+ SkPath path;
+ path.addRRect(roundRect);
+ mCanvas->drawPath(path, paint);
+ }
+}
+
+void SkiaCanvasProxy::onDrawPath(const SkPath& path, const SkPaint& paint) {
+ mCanvas->drawPath(path, paint);
+}
+
+void SkiaCanvasProxy::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
+ const SkPaint* paint) {
+ mCanvas->drawBitmap(bitmap, left, top, paint);
+}
+
+void SkiaCanvasProxy::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* srcPtr,
+ const SkRect& dst, const SkPaint* paint, DrawBitmapRectFlags) {
+ SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(bitmap.width(), bitmap.height());
+ mCanvas->drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
+ dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
+}
+
+void SkiaCanvasProxy::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
+ const SkRect& dst, const SkPaint*) {
+ //TODO make nine-patch drawing a method on Canvas.h
+ SkDEBUGFAIL("SkiaCanvasProxy::onDrawBitmapNine is not yet supported");
+}
+
+void SkiaCanvasProxy::onDrawSprite(const SkBitmap& bitmap, int left, int top,
+ const SkPaint* paint) {
+ mCanvas->save(SkCanvas::kMatrixClip_SaveFlag);
+ mCanvas->setMatrix(SkMatrix::I());
+ mCanvas->drawBitmap(bitmap, left, top, paint);
+ mCanvas->restore();
+}
+
+void SkiaCanvasProxy::onDrawVertices(VertexMode mode, int vertexCount, const SkPoint vertices[],
+ const SkPoint texs[], const SkColor colors[], SkXfermode*, const uint16_t indices[],
+ int indexCount, const SkPaint& paint) {
+ // convert the SkPoints into floats
+ SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+ const int floatCount = vertexCount << 1;
+ const float* vArray = &vertices[0].fX;
+ const float* tArray = (texs) ? &texs[0].fX : NULL;
+ const int* cArray = (colors) ? (int*)colors : NULL;
+ mCanvas->drawVertices(mode, floatCount, vArray, tArray, cArray, indices, indexCount, paint);
+}
+
+SkSurface* SkiaCanvasProxy::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) {
+ SkDEBUGFAIL("SkiaCanvasProxy::onNewSurface is not supported");
+ return NULL;
+}
+
+void SkiaCanvasProxy::willSave() {
+ mCanvas->save(SkCanvas::kMatrixClip_SaveFlag);
+}
+
+SkCanvas::SaveLayerStrategy SkiaCanvasProxy::willSaveLayer(const SkRect* rectPtr,
+ const SkPaint* paint, SaveFlags flags) {
+ SkRect rect;
+ if (rectPtr) {
+ rect = *rectPtr;
+ } else if(!mCanvas->getClipBounds(&rect)) {
+ rect = SkRect::MakeEmpty();
+ }
+ mCanvas->saveLayer(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint, flags);
+ return SkCanvas::kNoLayer_SaveLayerStrategy;
+}
+
+void SkiaCanvasProxy::willRestore() {
+ mCanvas->restore();
+}
+
+void SkiaCanvasProxy::didConcat(const SkMatrix& matrix) {
+ mCanvas->concat(matrix);
+}
+
+void SkiaCanvasProxy::didSetMatrix(const SkMatrix& matrix) {
+ mCanvas->setMatrix(matrix);
+}
+
+void SkiaCanvasProxy::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
+ const SkPaint& paint) {
+ SkPath path;
+ path.addRRect(outer);
+ path.addRRect(inner);
+ path.setFillType(SkPath::kEvenOdd_FillType);
+ this->drawPath(path, paint);
+}
+
+/**
+ * Utility class that converts the incoming text & paint from the given encoding
+ * into glyphIDs.
+ */
+class GlyphIDConverter {
+public:
+ GlyphIDConverter(const void* text, size_t byteLength, const SkPaint& origPaint) {
+ paint = origPaint;
+ if (paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding) {
+ glyphIDs = (uint16_t*)text;
+ count = byteLength >> 1;
+ } else {
+ storage.reset(byteLength); // ensures space for one glyph per ID given UTF8 encoding.
+ glyphIDs = storage.get();
+ count = paint.textToGlyphs(text, byteLength, storage.get());
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ }
+ }
+
+ SkPaint paint;
+ uint16_t* glyphIDs;
+ int count;
+private:
+ SkAutoSTMalloc<32, uint16_t> storage;
+};
+
+void SkiaCanvasProxy::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
+ const SkPaint& origPaint) {
+ // convert to glyphIDs if necessary
+ GlyphIDConverter glyphs(text, byteLength, origPaint);
+
+ // compute the glyph positions
+ SkAutoSTMalloc<32, SkPoint> pointStorage(glyphs.count);
+ SkAutoSTMalloc<32, SkScalar> glyphWidths(glyphs.count);
+ glyphs.paint.getTextWidths(glyphs.glyphIDs, glyphs.count << 1, glyphWidths.get());
+
+ // compute conservative bounds
+ // NOTE: We could call the faster paint.getFontBounds for a less accurate,
+ // but even more conservative bounds if this is too slow.
+ SkRect bounds;
+ glyphs.paint.measureText(glyphs.glyphIDs, glyphs.count << 1, &bounds);
+
+ // adjust for non-left alignment
+ if (glyphs.paint.getTextAlign() != SkPaint::kLeft_Align) {
+ SkScalar stop = 0;
+ for (int i = 0; i < glyphs.count; i++) {
+ stop += glyphWidths[i];
+ }
+ if (glyphs.paint.getTextAlign() == SkPaint::kCenter_Align) {
+ stop = SkScalarHalf(stop);
+ }
+ if (glyphs.paint.isVerticalText()) {
+ y -= stop;
+ } else {
+ x -= stop;
+ }
+ }
+
+ // setup the first glyph position and adjust bounds if needed
+ if (mCanvas->drawTextAbsolutePos()) {
+ bounds.offset(x,y);
+ pointStorage[0].set(x, y);
+ } else {
+ pointStorage[0].set(0, 0);
+ }
+
+ // setup the remaining glyph positions
+ if (glyphs.paint.isVerticalText()) {
+ for (int i = 1; i < glyphs.count; i++) {
+ pointStorage[i].set(x, glyphWidths[i-1] + pointStorage[i-1].fY);
+ }
+ } else {
+ for (int i = 1; i < glyphs.count; i++) {
+ pointStorage[i].set(glyphWidths[i-1] + pointStorage[i-1].fX, y);
+ }
+ }
+
+ SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+ mCanvas->drawText(glyphs.glyphIDs, &pointStorage[0].fX, glyphs.count, glyphs.paint,
+ x, y, bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, 0);
+}
+
+void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
+ const SkPaint& origPaint) {
+ // convert to glyphIDs if necessary
+ GlyphIDConverter glyphs(text, byteLength, origPaint);
+
+ // convert to relative positions if necessary
+ int x, y;
+ const SkPoint* posArray;
+ SkAutoSTMalloc<32, SkPoint> pointStorage;
+ if (mCanvas->drawTextAbsolutePos()) {
+ x = 0;
+ y = 0;
+ posArray = pos;
+ } else {
+ x = pos[0].fX;
+ y = pos[0].fY;
+ posArray = pointStorage.reset(glyphs.count);
+ for (int i = 0; i < glyphs.count; i++) {
+ pointStorage[i].fX = pos[i].fX- x;
+ pointStorage[i].fY = pos[i].fY- y;
+ }
+ }
+
+ // compute conservative bounds
+ // NOTE: We could call the faster paint.getFontBounds for a less accurate,
+ // but even more conservative bounds if this is too slow.
+ SkRect bounds;
+ glyphs.paint.measureText(glyphs.glyphIDs, glyphs.count << 1, &bounds);
+
+ SK_COMPILE_ASSERT(sizeof(SkPoint) == sizeof(float)*2, SkPoint_is_no_longer_2_floats);
+ mCanvas->drawText(glyphs.glyphIDs, &posArray[0].fX, glyphs.count, glyphs.paint, x, y,
+ bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, 0);
+}
+
+void SkiaCanvasProxy::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
+ SkScalar constY, const SkPaint& paint) {
+ const size_t pointCount = byteLength >> 1;
+ SkAutoSTMalloc<32, SkPoint> storage(pointCount);
+ SkPoint* pts = storage.get();
+ for (size_t i = 0; i < pointCount; i++) {
+ pts[i].set(xpos[i], constY);
+ }
+ this->onDrawPosText(text, byteLength, pts, paint);
+}
+
+void SkiaCanvasProxy::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
+ const SkMatrix* matrix, const SkPaint& origPaint) {
+ // convert to glyphIDs if necessary
+ GlyphIDConverter glyphs(text, byteLength, origPaint);
+ mCanvas->drawTextOnPath(glyphs.glyphIDs, glyphs.count, path, 0, 0, glyphs.paint);
+}
+
+void SkiaCanvasProxy::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
+ const SkPaint& paint) {
+ SkDEBUGFAIL("SkiaCanvasProxy::onDrawTextBlob is not supported");
+}
+
+void SkiaCanvasProxy::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
+ const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
+ SkPatchUtils::VertexData data;
+
+ SkMatrix matrix;
+ mCanvas->getMatrix(&matrix);
+ SkISize lod = SkPatchUtils::GetLevelOfDetail(cubics, &matrix);
+
+ // It automatically adjusts lodX and lodY in case it exceeds the number of indices.
+ // If it fails to generate the vertices, then we do not draw.
+ if (SkPatchUtils::getVertexData(&data, cubics, colors, texCoords, lod.width(), lod.height())) {
+ this->drawVertices(SkCanvas::kTriangles_VertexMode, data.fVertexCount, data.fPoints,
+ data.fTexCoords, data.fColors, xmode, data.fIndices, data.fIndexCount,
+ paint);
+ }
+}
+
+void SkiaCanvasProxy::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle) {
+ mCanvas->clipRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, op);
+}
+
+void SkiaCanvasProxy::onClipRRect(const SkRRect& roundRect, SkRegion::Op op, ClipEdgeStyle) {
+ SkPath path;
+ path.addRRect(roundRect);
+ mCanvas->clipPath(&path, op);
+}
+
+void SkiaCanvasProxy::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle) {
+ mCanvas->clipPath(&path, op);
+}
+
+void SkiaCanvasProxy::onClipRegion(const SkRegion& region, SkRegion::Op op) {
+ mCanvas->clipRegion(®ion, op);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h
new file mode 100644
index 0000000..4322fcf
--- /dev/null
+++ b/libs/hwui/SkiaCanvasProxy.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkiaCanvasProxy_DEFINED
+#define SkiaCanvasProxy_DEFINED
+
+#include <cutils/compiler.h>
+#include <SkCanvas.h>
+
+#include "Canvas.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * This class serves as a proxy between Skia's SkCanvas and Android Framework's
+ * Canvas. The class does not maintain any state and will pass through any request
+ * directly to the Canvas provided in the constructor.
+ *
+ * Upon construction it is expected that the provided Canvas has already been
+ * prepared for recording and will continue to be in the recording state while
+ * this proxy class is being used.
+ */
+class ANDROID_API SkiaCanvasProxy : public SkCanvas {
+public:
+ SkiaCanvasProxy(Canvas* canvas);
+ virtual ~SkiaCanvasProxy() {}
+
+protected:
+
+ virtual SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override;
+
+ virtual void willSave() override;
+ virtual SaveLayerStrategy willSaveLayer(const SkRect*, const SkPaint*, SaveFlags) override;
+ virtual void willRestore() override;
+
+ virtual void didConcat(const SkMatrix&) override;
+ virtual void didSetMatrix(const SkMatrix&) override;
+
+ virtual void onDrawPaint(const SkPaint& paint) override;
+ virtual void onDrawPoints(PointMode, size_t count, const SkPoint pts[],
+ const SkPaint&) override;
+ virtual void onDrawOval(const SkRect&, const SkPaint&) override;
+ virtual void onDrawRect(const SkRect&, const SkPaint&) override;
+ virtual void onDrawRRect(const SkRRect&, const SkPaint&) override;
+ virtual void onDrawPath(const SkPath& path, const SkPaint&) override;
+ virtual void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top,
+ const SkPaint*) override;
+ virtual void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst,
+ const SkPaint* paint, DrawBitmapRectFlags flags) override;
+ virtual void onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
+ const SkRect& dst, const SkPaint*) override;
+ virtual void onDrawSprite(const SkBitmap&, int left, int top,
+ const SkPaint*) override;
+ virtual void onDrawVertices(VertexMode, int vertexCount, const SkPoint vertices[],
+ const SkPoint texs[], const SkColor colors[], SkXfermode*,
+ const uint16_t indices[], int indexCount,
+ const SkPaint&) override;
+
+ virtual void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override;
+
+ virtual void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
+ const SkPaint&) override;
+ virtual void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
+ const SkPaint&) override;
+ virtual void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
+ SkScalar constY, const SkPaint&) override;
+ virtual void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
+ const SkMatrix* matrix, const SkPaint&) override;
+ virtual void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
+ const SkPaint& paint) override;
+
+ virtual void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
+ const SkPoint texCoords[4], SkXfermode* xmode,
+ const SkPaint& paint) override;
+
+ virtual void onClipRect(const SkRect&, SkRegion::Op, ClipEdgeStyle) override;
+ virtual void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override;
+ virtual void onClipPath(const SkPath&, SkRegion::Op, ClipEdgeStyle) override;
+ virtual void onClipRegion(const SkRegion&, SkRegion::Op) override;
+
+private:
+ Canvas* mCanvas;
+
+ typedef SkCanvas INHERITED;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // SkiaCanvasProxy_DEFINED
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 2c09344..e13c861 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -57,7 +57,7 @@
}
static inline void bindTexture(Caches* caches, Texture* texture, GLenum wrapS, GLenum wrapT) {
- caches->bindTexture(texture->id);
+ caches->textureState().bindTexture(texture->id);
texture->setWrapST(wrapS, wrapT);
}
@@ -176,7 +176,7 @@
}
GLuint textureSlot = (*textureUnit)++;
- caches->activeTexture(textureSlot);
+ caches->textureState().activateTexture(textureSlot);
const float width = layer->getWidth();
const float height = layer->getHeight();
@@ -270,7 +270,7 @@
}
GLuint textureSlot = (*textureUnit)++;
- Caches::getInstance().activeTexture(textureSlot);
+ Caches::getInstance().textureState().activateTexture(textureSlot);
BitmapShaderInfo shaderInfo;
if (!bitmapShaderHelper(caches, nullptr, &shaderInfo, extensions, bitmap, xy)) {
@@ -392,7 +392,7 @@
shader.asAGradient(&gradInfo);
}
GLuint textureSlot = (*textureUnit)++;
- caches->activeTexture(textureSlot);
+ caches->textureState().activateTexture(textureSlot);
#ifndef SK_SCALAR_IS_FLOAT
#error Need to convert gradInfo.fColorOffsets to float!
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index 4ec298d..c2e88f3 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -207,7 +207,7 @@
glGenTextures(1, &texture->id);
- caches.bindTexture(texture->id);
+ caches.textureState().bindTexture(texture->id);
// Textures are Alpha8
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
index 58fd972..512f5cf 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -24,18 +24,44 @@
namespace android {
namespace uirenderer {
-Texture::Texture(): id(0), generation(0), blend(false), width(0), height(0),
- cleanup(false), bitmapSize(0), mipMap(false), uvMapper(nullptr), isInUse(false),
- mWrapS(GL_CLAMP_TO_EDGE), mWrapT(GL_CLAMP_TO_EDGE),
- mMinFilter(GL_NEAREST), mMagFilter(GL_NEAREST),
- mFirstFilter(true), mFirstWrap(true), mCaches(Caches::getInstance()) {
+Texture::Texture()
+ : id(0)
+ , generation(0)
+ , blend(false)
+ , width(0)
+ , height(0)
+ , cleanup(false)
+ , bitmapSize(0)
+ , mipMap(false)
+ , uvMapper(nullptr)
+ , isInUse(false)
+ , mWrapS(GL_CLAMP_TO_EDGE)
+ , mWrapT(GL_CLAMP_TO_EDGE)
+ , mMinFilter(GL_NEAREST)
+ , mMagFilter(GL_NEAREST)
+ , mFirstFilter(true)
+ , mFirstWrap(true)
+ , mCaches(Caches::getInstance()) {
}
-Texture::Texture(Caches& caches): id(0), generation(0), blend(false), width(0), height(0),
- cleanup(false), bitmapSize(0), mipMap(false), uvMapper(nullptr), isInUse(false),
- mWrapS(GL_CLAMP_TO_EDGE), mWrapT(GL_CLAMP_TO_EDGE),
- mMinFilter(GL_NEAREST), mMagFilter(GL_NEAREST),
- mFirstFilter(true), mFirstWrap(true), mCaches(caches) {
+Texture::Texture(Caches& caches)
+ : id(0)
+ , generation(0)
+ , blend(false)
+ , width(0)
+ , height(0)
+ , cleanup(false)
+ , bitmapSize(0)
+ , mipMap(false)
+ , uvMapper(nullptr)
+ , isInUse(false)
+ , mWrapS(GL_CLAMP_TO_EDGE)
+ , mWrapT(GL_CLAMP_TO_EDGE)
+ , mMinFilter(GL_NEAREST)
+ , mMagFilter(GL_NEAREST)
+ , mFirstFilter(true)
+ , mFirstWrap(true)
+ , mCaches(caches) {
}
void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force,
@@ -48,7 +74,7 @@
mWrapT = wrapT;
if (bindTexture) {
- mCaches.bindTexture(renderTarget, id);
+ mCaches.textureState().bindTexture(renderTarget, id);
}
glTexParameteri(renderTarget, GL_TEXTURE_WRAP_S, wrapS);
@@ -66,7 +92,7 @@
mMagFilter = mag;
if (bindTexture) {
- mCaches.bindTexture(renderTarget, id);
+ mCaches.textureState().bindTexture(renderTarget, id);
}
if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR;
@@ -77,7 +103,7 @@
}
void Texture::deleteTexture() const {
- mCaches.deleteTexture(id);
+ mCaches.textureState().deleteTexture(id);
}
}; // namespace uirenderer
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 524f206..fe8fb5b 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -296,7 +296,7 @@
texture->width = bitmap->width();
texture->height = bitmap->height();
- Caches::getInstance().bindTexture(texture->id);
+ Caches::getInstance().textureState().bindTexture(texture->id);
switch (bitmap->colorType()) {
case kAlpha_8_SkColorType:
diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h
index aa6acc9..7c3f2fd 100644
--- a/libs/hwui/Vector.h
+++ b/libs/hwui/Vector.h
@@ -17,6 +17,9 @@
#ifndef ANDROID_HWUI_VECTOR_H
#define ANDROID_HWUI_VECTOR_H
+#include <math.h>
+#include <utils/Log.h>
+
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
index 128e392..53fa0dc 100644
--- a/libs/hwui/font/CacheTexture.cpp
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -157,7 +157,7 @@
mTexture = nullptr;
}
if (mTextureId) {
- mCaches.deleteTexture(mTextureId);
+ mCaches.textureState().deleteTexture(mTextureId);
mTextureId = 0;
}
mDirty = false;
@@ -169,7 +169,7 @@
mLinearFiltering = linearFiltering;
const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST;
- if (bind) mCaches.bindTexture(getTextureId());
+ if (bind) mCaches.textureState().bindTexture(getTextureId());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
}
@@ -189,7 +189,7 @@
if (!mTextureId) {
glGenTextures(1, &mTextureId);
- mCaches.bindTexture(mTextureId);
+ mCaches.textureState().bindTexture(mTextureId);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Initialize texture dimensions
glTexImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0,
diff --git a/libs/hwui/renderstate/Blend.cpp b/libs/hwui/renderstate/Blend.cpp
new file mode 100644
index 0000000..3e7b721
--- /dev/null
+++ b/libs/hwui/renderstate/Blend.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <renderstate/Blend.h>
+#include "Program.h"
+
+#include "ShadowTessellator.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Structure mapping Skia xfermodes to OpenGL blending factors.
+ */
+struct Blender {
+ SkXfermode::Mode mode;
+ GLenum src;
+ GLenum dst;
+};
+
+// In this array, the index of each Blender equals the value of the first
+// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode]
+const Blender kBlends[] = {
+ { SkXfermode::kClear_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kSrc_Mode, GL_ONE, GL_ZERO },
+ { SkXfermode::kDst_Mode, GL_ZERO, GL_ONE },
+ { SkXfermode::kSrcOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kDstOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
+ { SkXfermode::kSrcIn_Mode, GL_DST_ALPHA, GL_ZERO },
+ { SkXfermode::kDstIn_Mode, GL_ZERO, GL_SRC_ALPHA },
+ { SkXfermode::kSrcOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
+ { SkXfermode::kDstOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kSrcATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kDstATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
+ { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kPlus_Mode, GL_ONE, GL_ONE },
+ { SkXfermode::kModulate_Mode, GL_ZERO, GL_SRC_COLOR },
+ { SkXfermode::kScreen_Mode, GL_ONE, GL_ONE_MINUS_SRC_COLOR }
+};
+
+// This array contains the swapped version of each SkXfermode. For instance
+// this array's SrcOver blending mode is actually DstOver. You can refer to
+// createLayer() for more information on the purpose of this array.
+const Blender kBlendsSwap[] = {
+ { SkXfermode::kClear_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
+ { SkXfermode::kSrc_Mode, GL_ZERO, GL_ONE },
+ { SkXfermode::kDst_Mode, GL_ONE, GL_ZERO },
+ { SkXfermode::kSrcOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
+ { SkXfermode::kDstOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kSrcIn_Mode, GL_ZERO, GL_SRC_ALPHA },
+ { SkXfermode::kDstIn_Mode, GL_DST_ALPHA, GL_ZERO },
+ { SkXfermode::kSrcOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kDstOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
+ { SkXfermode::kSrcATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
+ { SkXfermode::kDstATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kPlus_Mode, GL_ONE, GL_ONE },
+ { SkXfermode::kModulate_Mode, GL_DST_COLOR, GL_ZERO },
+ { SkXfermode::kScreen_Mode, GL_ONE_MINUS_DST_COLOR, GL_ONE }
+};
+
+Blend::Blend()
+ : mEnabled(false)
+ , mSrcMode(GL_ZERO)
+ , mDstMode(GL_ZERO) {
+ // gl blending off by default
+}
+
+void Blend::enable(SkXfermode::Mode mode, bool swapSrcDst) {
+ // enable
+ if (!mEnabled) {
+ glEnable(GL_BLEND);
+ mEnabled = true;
+ }
+
+ // select blend mode
+ GLenum sourceMode = swapSrcDst ? kBlendsSwap[mode].src : kBlends[mode].src;
+ GLenum destMode = swapSrcDst ? kBlendsSwap[mode].dst : kBlends[mode].dst;
+
+ if (sourceMode != mSrcMode || destMode != mSrcMode) {
+ glBlendFunc(sourceMode, destMode);
+ mSrcMode = sourceMode;
+ mDstMode = destMode;
+ }
+}
+
+void Blend::disable() {
+ if (mEnabled) {
+ glDisable(GL_BLEND);
+ mEnabled = false;
+ }
+}
+
+void Blend::invalidate() {
+ syncEnabled();
+ mSrcMode = mDstMode = GL_ZERO;
+}
+
+void Blend::syncEnabled() {
+ if (mEnabled) {
+ glEnable(GL_BLEND);
+ } else {
+ glDisable(GL_BLEND);
+ }
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
+
diff --git a/libs/hwui/renderstate/Blend.h b/libs/hwui/renderstate/Blend.h
new file mode 100644
index 0000000..b82b477
--- /dev/null
+++ b/libs/hwui/renderstate/Blend.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef RENDERSTATE_BLEND_H
+#define RENDERSTATE_BLEND_H
+
+#include "Vertex.h"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <SkXfermode.h>
+#include <memory>
+
+namespace android {
+namespace uirenderer {
+
+class Blend {
+ friend class RenderState;
+public:
+ void enable(SkXfermode::Mode mode, bool swapSrcDst);
+ void disable();
+ void syncEnabled();
+private:
+ Blend();
+ void invalidate();
+ bool mEnabled;
+ GLenum mSrcMode;
+ GLenum mDstMode;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif // RENDERSTATE_BLEND_H
diff --git a/libs/hwui/renderstate/MeshState.cpp b/libs/hwui/renderstate/MeshState.cpp
new file mode 100644
index 0000000..7820a66
--- /dev/null
+++ b/libs/hwui/renderstate/MeshState.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "renderstate/MeshState.h"
+
+#include "Program.h"
+
+#include "ShadowTessellator.h"
+
+namespace android {
+namespace uirenderer {
+
+MeshState::MeshState()
+ : mCurrentPositionPointer(this)
+ , mCurrentPositionStride(0)
+ , mCurrentTexCoordsPointer(this)
+ , mCurrentTexCoordsStride(0)
+ , mTexCoordsArrayEnabled(false) {
+
+ glGenBuffers(1, &meshBuffer);
+ glBindBuffer(GL_ARRAY_BUFFER, meshBuffer);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(kMeshVertices), kMeshVertices, GL_STATIC_DRAW);
+
+ mCurrentBuffer = meshBuffer;
+ mCurrentIndicesBuffer = 0;
+ mCurrentPixelBuffer = 0;
+
+ mQuadListIndices = 0;
+ mShadowStripsIndices = 0;
+}
+
+MeshState::~MeshState() {
+ glDeleteBuffers(1, &meshBuffer);
+ mCurrentBuffer = 0;
+
+ glDeleteBuffers(1, &mQuadListIndices);
+ mQuadListIndices = 0;
+
+ glDeleteBuffers(1, &mShadowStripsIndices);
+ mShadowStripsIndices = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Buffer Objects
+///////////////////////////////////////////////////////////////////////////////
+
+bool MeshState::bindMeshBuffer() {
+ return bindMeshBuffer(meshBuffer);
+}
+
+bool MeshState::bindMeshBuffer(GLuint buffer) {
+ if (!buffer) buffer = meshBuffer;
+ if (mCurrentBuffer != buffer) {
+ glBindBuffer(GL_ARRAY_BUFFER, buffer);
+ mCurrentBuffer = buffer;
+ return true;
+ }
+ return false;
+}
+
+bool MeshState::unbindMeshBuffer() {
+ if (mCurrentBuffer) {
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ mCurrentBuffer = 0;
+ return true;
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Vertices
+///////////////////////////////////////////////////////////////////////////////
+
+void MeshState::bindPositionVertexPointer(const Program* currentProgram, bool force,
+ const GLvoid* vertices, GLsizei stride) {
+ if (force || vertices != mCurrentPositionPointer || stride != mCurrentPositionStride) {
+ GLuint slot = currentProgram->position;
+ glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices);
+ mCurrentPositionPointer = vertices;
+ mCurrentPositionStride = stride;
+ }
+}
+
+void MeshState::bindTexCoordsVertexPointer(const Program* currentProgram, bool force,
+ const GLvoid* vertices, GLsizei stride) {
+ if (force || vertices != mCurrentTexCoordsPointer || stride != mCurrentTexCoordsStride) {
+ GLuint slot = currentProgram->texCoords;
+ glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices);
+ mCurrentTexCoordsPointer = vertices;
+ mCurrentTexCoordsStride = stride;
+ }
+}
+
+void MeshState::resetVertexPointers() {
+ mCurrentPositionPointer = this;
+ mCurrentTexCoordsPointer = this;
+}
+
+void MeshState::resetTexCoordsVertexPointer() {
+ mCurrentTexCoordsPointer = this;
+}
+
+void MeshState::enableTexCoordsVertexArray() {
+ if (!mTexCoordsArrayEnabled) {
+ glEnableVertexAttribArray(Program::kBindingTexCoords);
+ mCurrentTexCoordsPointer = this;
+ mTexCoordsArrayEnabled = true;
+ }
+}
+
+void MeshState::disableTexCoordsVertexArray() {
+ if (mTexCoordsArrayEnabled) {
+ glDisableVertexAttribArray(Program::kBindingTexCoords);
+ mTexCoordsArrayEnabled = false;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Indices
+///////////////////////////////////////////////////////////////////////////////
+
+bool MeshState::bindIndicesBufferInternal(const GLuint buffer) {
+ if (mCurrentIndicesBuffer != buffer) {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
+ mCurrentIndicesBuffer = buffer;
+ return true;
+ }
+ return false;
+}
+
+bool MeshState::bindQuadIndicesBuffer() {
+ if (!mQuadListIndices) {
+ std::unique_ptr<uint16_t[]> regionIndices(new uint16_t[kMaxNumberOfQuads * 6]);
+ for (uint32_t i = 0; i < kMaxNumberOfQuads; i++) {
+ uint16_t quad = i * 4;
+ int index = i * 6;
+ regionIndices[index ] = quad; // top-left
+ regionIndices[index + 1] = quad + 1; // top-right
+ regionIndices[index + 2] = quad + 2; // bottom-left
+ regionIndices[index + 3] = quad + 2; // bottom-left
+ regionIndices[index + 4] = quad + 1; // top-right
+ regionIndices[index + 5] = quad + 3; // bottom-right
+ }
+
+ glGenBuffers(1, &mQuadListIndices);
+ bool force = bindIndicesBufferInternal(mQuadListIndices);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, kMaxNumberOfQuads * 6 * sizeof(uint16_t),
+ regionIndices.get(), GL_STATIC_DRAW);
+ return force;
+ }
+
+ return bindIndicesBufferInternal(mQuadListIndices);
+}
+
+bool MeshState::bindShadowIndicesBuffer() {
+ if (!mShadowStripsIndices) {
+ std::unique_ptr<uint16_t[]> shadowIndices(new uint16_t[MAX_SHADOW_INDEX_COUNT]);
+ ShadowTessellator::generateShadowIndices(shadowIndices.get());
+ glGenBuffers(1, &mShadowStripsIndices);
+ bool force = bindIndicesBufferInternal(mShadowStripsIndices);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_SHADOW_INDEX_COUNT * sizeof(uint16_t),
+ shadowIndices.get(), GL_STATIC_DRAW);
+ return force;
+ }
+
+ return bindIndicesBufferInternal(mShadowStripsIndices);
+}
+
+bool MeshState::unbindIndicesBuffer() {
+ if (mCurrentIndicesBuffer) {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ mCurrentIndicesBuffer = 0;
+ return true;
+ }
+ return false;
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
+
diff --git a/libs/hwui/renderstate/MeshState.h b/libs/hwui/renderstate/MeshState.h
new file mode 100644
index 0000000..76f73d4
--- /dev/null
+++ b/libs/hwui/renderstate/MeshState.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef RENDERSTATE_MESHSTATE_H
+#define RENDERSTATE_MESHSTATE_H
+
+#include "Vertex.h"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <memory>
+
+namespace android {
+namespace uirenderer {
+
+class Program;
+
+// Maximum number of quads that pre-allocated meshes can draw
+const uint32_t kMaxNumberOfQuads = 2048;
+
+// This array is never used directly but used as a memcpy source in the
+// OpenGLRenderer constructor
+const TextureVertex kMeshVertices[] = {
+ { 0, 0, 0, 0 },
+ { 1, 0, 1, 0 },
+ { 0, 1, 0, 1 },
+ { 1, 1, 1, 1 },
+};
+
+const GLsizei kVertexStride = sizeof(Vertex);
+const GLsizei kAlphaVertexStride = sizeof(AlphaVertex);
+const GLsizei kTextureVertexStride = sizeof(TextureVertex);
+
+const GLsizei kMeshTextureOffset = 2 * sizeof(float);
+const GLsizei kVertexAlphaOffset = 2 * sizeof(float);
+const GLsizei kVertexAAWidthOffset = 2 * sizeof(float);
+const GLsizei kVertexAALengthOffset = 3 * sizeof(float);
+const GLsizei kMeshCount = 4;
+
+class MeshState {
+private:
+ friend class RenderState;
+
+public:
+ ~MeshState();
+ ///////////////////////////////////////////////////////////////////////////////
+ // Buffer objects
+ ///////////////////////////////////////////////////////////////////////////////
+ /**
+ * Binds the VBO used to render simple textured quads.
+ */
+ bool bindMeshBuffer();
+
+ /**
+ * Binds the specified VBO if needed. If buffer == 0, binds default simple textured quad.
+ */
+ bool bindMeshBuffer(GLuint buffer);
+
+ /**
+ * Unbinds the VBO used to render simple textured quads.
+ */
+ bool unbindMeshBuffer();
+
+ ///////////////////////////////////////////////////////////////////////////////
+ // Vertices
+ ///////////////////////////////////////////////////////////////////////////////
+ /**
+ * Binds an attrib to the specified float vertex pointer.
+ * Assumes a stride of gTextureVertexStride and a size of 2.
+ */
+ void bindPositionVertexPointer(const Program* currentProgram, bool force,
+ const GLvoid* vertices, GLsizei stride = kTextureVertexStride);
+
+ /**
+ * Binds an attrib to the specified float vertex pointer.
+ * Assumes a stride of gTextureVertexStride and a size of 2.
+ */
+ void bindTexCoordsVertexPointer(const Program* currentProgram, bool force,
+ const GLvoid* vertices, GLsizei stride = kTextureVertexStride);
+
+ /**
+ * Resets the vertex pointers.
+ */
+ void resetVertexPointers();
+ void resetTexCoordsVertexPointer();
+
+ void enableTexCoordsVertexArray();
+ void disableTexCoordsVertexArray();
+
+ ///////////////////////////////////////////////////////////////////////////////
+ // Indices
+ ///////////////////////////////////////////////////////////////////////////////
+ /**
+ * Binds a global indices buffer that can draw up to
+ * gMaxNumberOfQuads quads.
+ */
+ bool bindQuadIndicesBuffer();
+ bool bindShadowIndicesBuffer();
+ bool unbindIndicesBuffer();
+
+private:
+ MeshState();
+ bool bindIndicesBufferInternal(const GLuint buffer);
+
+ // VBO to draw with
+ GLuint meshBuffer;
+
+ GLuint mCurrentBuffer;
+ GLuint mCurrentIndicesBuffer;
+ GLuint mCurrentPixelBuffer;
+
+ const void* mCurrentPositionPointer;
+ GLsizei mCurrentPositionStride;
+ const void* mCurrentTexCoordsPointer;
+ GLsizei mCurrentTexCoordsStride;
+
+ bool mTexCoordsArrayEnabled;
+
+ // Global index buffer
+ GLuint mQuadListIndices;
+ GLuint mShadowStripsIndices;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif // RENDERSTATE_MESHSTATE_H
diff --git a/libs/hwui/renderstate/PixelBufferState.cpp b/libs/hwui/renderstate/PixelBufferState.cpp
new file mode 100644
index 0000000..c23af52
--- /dev/null
+++ b/libs/hwui/renderstate/PixelBufferState.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "renderstate/PixelBufferState.h"
+
+namespace android {
+namespace uirenderer {
+
+PixelBufferState::PixelBufferState()
+ : mCurrentPixelBuffer(0) {
+}
+
+bool PixelBufferState::bind(GLuint buffer) {
+ if (mCurrentPixelBuffer != buffer) {
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer);
+ mCurrentPixelBuffer = buffer;
+ return true;
+ }
+ return false;
+}
+
+bool PixelBufferState::unbind() {
+ if (mCurrentPixelBuffer) {
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+ mCurrentPixelBuffer = 0;
+ return true;
+ }
+ return false;
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
+
diff --git a/libs/hwui/renderstate/PixelBufferState.h b/libs/hwui/renderstate/PixelBufferState.h
new file mode 100644
index 0000000..8dab21d
--- /dev/null
+++ b/libs/hwui/renderstate/PixelBufferState.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef RENDERSTATE_PIXELBUFFERSTATE_H
+#define RENDERSTATE_PIXELBUFFERSTATE_H
+
+#include <GLES3/gl3.h>
+
+namespace android {
+namespace uirenderer {
+
+class PixelBufferState {
+ friend class Caches; // TODO: move to RenderState
+public:
+ bool bind(GLuint buffer);
+ bool unbind();
+private:
+ PixelBufferState();
+ GLuint mCurrentPixelBuffer;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif // RENDERSTATE_PIXELBUFFERSTATE_H
diff --git a/libs/hwui/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
similarity index 77%
rename from libs/hwui/RenderState.cpp
rename to libs/hwui/renderstate/RenderState.cpp
index 45a97fb..58ec321 100644
--- a/libs/hwui/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include "RenderState.h"
+#include "renderstate/RenderState.h"
#include "renderthread/CanvasContext.h"
#include "renderthread/EglManager.h"
@@ -23,7 +23,6 @@
RenderState::RenderState(renderthread::RenderThread& thread)
: mRenderThread(thread)
- , mCaches(nullptr)
, mViewportWidth(0)
, mViewportHeight(0)
, mFramebuffer(0) {
@@ -31,16 +30,30 @@
}
RenderState::~RenderState() {
+ LOG_ALWAYS_FATAL_IF(mBlend || mMeshState || mScissor || mStencil,
+ "State object lifecycle not managed correctly");
}
void RenderState::onGLContextCreated() {
+ LOG_ALWAYS_FATAL_IF(mBlend || mMeshState || mScissor || mStencil,
+ "State object lifecycle not managed correctly");
+ mBlend = new Blend();
+ mMeshState = new MeshState();
+ mScissor = new Scissor();
+ mStencil = new Stencil();
+
// This is delayed because the first access of Caches makes GL calls
- mCaches = &Caches::getInstance();
+ if (!mCaches) {
+ mCaches = &Caches::createInstance(*this);
+ }
mCaches->init();
- mCaches->setRenderState(this);
mCaches->textureCache.setAssetAtlas(&mAssetAtlas);
}
+static void layerLostGlContext(Layer* layer) {
+ layer->onGlContextLost();
+}
+
void RenderState::onGLContextDestroyed() {
/*
size_t size = mActiveLayers.size();
@@ -73,7 +86,21 @@
LOG_ALWAYS_FATAL("%d layers have survived gl context destruction", size);
}
*/
+
+ // TODO: reset all cached state in state objects
+ std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerLostGlContext);
mAssetAtlas.terminate();
+
+ mCaches->terminate();
+
+ delete mBlend;
+ mBlend = nullptr;
+ delete mMeshState;
+ mMeshState = nullptr;
+ delete mScissor;
+ mScissor = nullptr;
+ delete mStencil;
+ mStencil = nullptr;
}
void RenderState::setViewport(GLsizei width, GLsizei height) {
@@ -108,11 +135,11 @@
mCaches->currentProgram = nullptr;
}
}
- mCaches->resetActiveTexture();
- mCaches->unbindMeshBuffer();
- mCaches->unbindIndicesBuffer();
- mCaches->resetVertexPointers();
- mCaches->disableTexCoordsVertexArray();
+ mCaches->textureState().resetActiveTexture();
+ meshState().unbindMeshBuffer();
+ meshState().unbindIndicesBuffer();
+ meshState().resetVertexPointers();
+ meshState().disableTexCoordsVertexArray();
debugOverdraw(false, false);
}
@@ -123,29 +150,23 @@
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
- mCaches->scissorEnabled = glIsEnabled(GL_SCISSOR_TEST);
- mCaches->enableScissor();
- mCaches->resetScissor();
+ scissor().invalidate();
+ blend().invalidate();
- mCaches->activeTexture(0);
- mCaches->resetBoundTextures();
-
- mCaches->blend = true;
- glEnable(GL_BLEND);
- glBlendFunc(mCaches->lastSrcMode, mCaches->lastDstMode);
- glBlendEquation(GL_FUNC_ADD);
+ mCaches->textureState().activateTexture(0);
+ mCaches->textureState().resetBoundTextures();
}
void RenderState::debugOverdraw(bool enable, bool clear) {
if (mCaches->debugOverdraw && mFramebuffer == 0) {
if (clear) {
- mCaches->disableScissor();
- mCaches->stencil.clear();
+ scissor().setEnabled(false);
+ stencil().clear();
}
if (enable) {
- mCaches->stencil.enableDebugWrite();
+ stencil().enableDebugWrite();
} else {
- mCaches->stencil.disable();
+ stencil().disable();
}
}
}
diff --git a/libs/hwui/RenderState.h b/libs/hwui/renderstate/RenderState.h
similarity index 81%
rename from libs/hwui/RenderState.h
rename to libs/hwui/renderstate/RenderState.h
index 629fe0d..4180f44 100644
--- a/libs/hwui/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -24,9 +24,13 @@
#include <utils/RefBase.h>
#include <private/hwui/DrawGlInfo.h>
-
+#include <renderstate/Blend.h>
#include "AssetAtlas.h"
#include "Caches.h"
+#include "renderstate/MeshState.h"
+#include "renderstate/PixelBufferState.h"
+#include "renderstate/Scissor.h"
+#include "renderstate/Stencil.h"
#include "utils/Macros.h"
namespace android {
@@ -58,10 +62,10 @@
void debugOverdraw(bool enable, bool clear);
- void registerLayer(const Layer* layer) {
+ void registerLayer(Layer* layer) {
mActiveLayers.insert(layer);
}
- void unregisterLayer(const Layer* layer) {
+ void unregisterLayer(Layer* layer) {
mActiveLayers.erase(layer);
}
@@ -80,7 +84,10 @@
void postDecStrong(VirtualLightRefBase* object);
AssetAtlas& assetAtlas() { return mAssetAtlas; }
-
+ Blend& blend() { return *mBlend; }
+ MeshState& meshState() { return *mMeshState; }
+ Scissor& scissor() { return *mScissor; }
+ Stencil& stencil() { return *mStencil; }
private:
friend class renderthread::RenderThread;
friend class Caches;
@@ -92,10 +99,17 @@
RenderState(renderthread::RenderThread& thread);
~RenderState();
+
renderthread::RenderThread& mRenderThread;
- Caches* mCaches;
+ Caches* mCaches = nullptr;
+
+ Blend* mBlend = nullptr;
+ MeshState* mMeshState = nullptr;
+ Scissor* mScissor = nullptr;
+ Stencil* mStencil = nullptr;
+
AssetAtlas mAssetAtlas;
- std::set<const Layer*> mActiveLayers;
+ std::set<Layer*> mActiveLayers;
std::set<renderthread::CanvasContext*> mRegisteredContexts;
GLsizei mViewportWidth;
diff --git a/libs/hwui/renderstate/Scissor.cpp b/libs/hwui/renderstate/Scissor.cpp
new file mode 100644
index 0000000..66c31a2
--- /dev/null
+++ b/libs/hwui/renderstate/Scissor.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "renderstate/Scissor.h"
+
+namespace android {
+namespace uirenderer {
+
+Scissor::Scissor()
+ : mEnabled(false)
+ , mScissorX(0)
+ , mScissorY(0)
+ , mScissorWidth(0)
+ , mScissorHeight(0) {
+}
+
+bool Scissor::setEnabled(bool enabled) {
+ if (mEnabled != enabled) {
+ if (enabled) {
+ glEnable(GL_SCISSOR_TEST);
+ } else {
+ glDisable(GL_SCISSOR_TEST);
+ }
+ mEnabled = enabled;
+ return true;
+ }
+ return false;
+}
+
+bool Scissor::set(GLint x, GLint y, GLint width, GLint height) {
+ if (mEnabled && (x != mScissorX || y != mScissorY
+ || width != mScissorWidth || height != mScissorHeight)) {
+
+ if (x < 0) {
+ width += x;
+ x = 0;
+ }
+ if (y < 0) {
+ height += y;
+ y = 0;
+ }
+ if (width < 0) {
+ width = 0;
+ }
+ if (height < 0) {
+ height = 0;
+ }
+ glScissor(x, y, width, height);
+
+ mScissorX = x;
+ mScissorY = y;
+ mScissorWidth = width;
+ mScissorHeight = height;
+
+ return true;
+ }
+ return false;
+}
+
+void Scissor::reset() {
+ mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0;
+}
+
+void Scissor::invalidate() {
+ mEnabled = glIsEnabled(GL_SCISSOR_TEST);
+ setEnabled(true);
+ reset();
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
+
diff --git a/libs/hwui/renderstate/Scissor.h b/libs/hwui/renderstate/Scissor.h
new file mode 100644
index 0000000..cc8b3dd
--- /dev/null
+++ b/libs/hwui/renderstate/Scissor.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef RENDERSTATE_SCISSOR_H
+#define RENDERSTATE_SCISSOR_H
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+namespace android {
+namespace uirenderer {
+
+class Scissor {
+ friend class RenderState;
+public:
+ bool setEnabled(bool enabled);
+ bool set(GLint x, GLint y, GLint width, GLint height);
+ void reset();
+ bool isEnabled() { return mEnabled; }
+private:
+ Scissor();
+ void invalidate();
+ bool mEnabled;
+ GLint mScissorX;
+ GLint mScissorY;
+ GLint mScissorWidth;
+ GLint mScissorHeight;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif // RENDERSTATE_SCISSOR_H
diff --git a/libs/hwui/Stencil.cpp b/libs/hwui/renderstate/Stencil.cpp
similarity index 98%
rename from libs/hwui/Stencil.cpp
rename to libs/hwui/renderstate/Stencil.cpp
index f56a02e..acbed14 100644
--- a/libs/hwui/Stencil.cpp
+++ b/libs/hwui/renderstate/Stencil.cpp
@@ -17,7 +17,7 @@
#include "Debug.h"
#include "Extensions.h"
#include "Properties.h"
-#include "Stencil.h"
+#include "renderstate/Stencil.h"
#include <GLES2/gl2ext.h>
diff --git a/libs/hwui/Stencil.h b/libs/hwui/renderstate/Stencil.h
similarity index 100%
rename from libs/hwui/Stencil.h
rename to libs/hwui/renderstate/Stencil.h
diff --git a/libs/hwui/renderstate/TextureState.cpp b/libs/hwui/renderstate/TextureState.cpp
new file mode 100644
index 0000000..1a638d2
--- /dev/null
+++ b/libs/hwui/renderstate/TextureState.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <renderstate/TextureState.h>
+
+namespace android {
+namespace uirenderer {
+
+// Must define as many texture units as specified by kTextureUnitsCount
+const GLenum kTextureUnits[] = {
+ GL_TEXTURE0,
+ GL_TEXTURE1,
+ GL_TEXTURE2
+};
+
+TextureState::TextureState()
+ : mTextureUnit(0) {
+ glActiveTexture(kTextureUnits[0]);
+ resetBoundTextures();
+
+ GLint maxTextureUnits;
+ glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
+ LOG_ALWAYS_FATAL_IF(maxTextureUnits < kTextureUnitsCount,
+ "At least %d texture units are required!", kTextureUnitsCount);
+}
+
+void TextureState::activateTexture(GLuint textureUnit) {
+ if (mTextureUnit != textureUnit) {
+ glActiveTexture(kTextureUnits[textureUnit]);
+ mTextureUnit = textureUnit;
+ }
+}
+
+void TextureState::resetActiveTexture() {
+ mTextureUnit = -1;
+}
+
+void TextureState::bindTexture(GLuint texture) {
+ if (mBoundTextures[mTextureUnit] != texture) {
+ glBindTexture(GL_TEXTURE_2D, texture);
+ mBoundTextures[mTextureUnit] = texture;
+ }
+}
+
+void TextureState::bindTexture(GLenum target, GLuint texture) {
+ if (target == GL_TEXTURE_2D) {
+ bindTexture(texture);
+ } else {
+ // GLConsumer directly calls glBindTexture() with
+ // target=GL_TEXTURE_EXTERNAL_OES, don't cache this target
+ // since the cached state could be stale
+ glBindTexture(target, texture);
+ }
+}
+
+void TextureState::deleteTexture(GLuint texture) {
+ // When glDeleteTextures() is called on a currently bound texture,
+ // OpenGL ES specifies that the texture is then considered unbound
+ // Consider the following series of calls:
+ //
+ // glGenTextures -> creates texture name 2
+ // glBindTexture(2)
+ // glDeleteTextures(2) -> 2 is now unbound
+ // glGenTextures -> can return 2 again
+ //
+ // If we don't call glBindTexture(2) after the second glGenTextures
+ // call, any texture operation will be performed on the default
+ // texture (name=0)
+
+ unbindTexture(texture);
+
+ glDeleteTextures(1, &texture);
+}
+
+void TextureState::resetBoundTextures() {
+ for (int i = 0; i < kTextureUnitsCount; i++) {
+ mBoundTextures[i] = 0;
+ }
+}
+
+void TextureState::unbindTexture(GLuint texture) {
+ for (int i = 0; i < kTextureUnitsCount; i++) {
+ if (mBoundTextures[i] == texture) {
+ mBoundTextures[i] = 0;
+ }
+ }
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
+
diff --git a/libs/hwui/renderstate/TextureState.h b/libs/hwui/renderstate/TextureState.h
new file mode 100644
index 0000000..5a57b9f
--- /dev/null
+++ b/libs/hwui/renderstate/TextureState.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef RENDERSTATE_TEXTURESTATE_H
+#define RENDERSTATE_TEXTURESTATE_H
+
+#include "Vertex.h"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <SkXfermode.h>
+#include <memory>
+
+namespace android {
+namespace uirenderer {
+
+class TextureState {
+ friend class Caches; // TODO: move to RenderState
+public:
+ /**
+ * Activate the specified texture unit. The texture unit must
+ * be specified using an integer number (0 for GL_TEXTURE0 etc.)
+ */
+ void activateTexture(GLuint textureUnit);
+
+ /**
+ * Invalidate the cached value of the active texture unit.
+ */
+ void resetActiveTexture();
+
+ /**
+ * Binds the specified texture as a GL_TEXTURE_2D texture.
+ * All texture bindings must be performed with this method or
+ * bindTexture(GLenum, GLuint).
+ */
+ void bindTexture(GLuint texture);
+
+ /**
+ * Binds the specified texture with the specified render target.
+ * All texture bindings must be performed with this method or
+ * bindTexture(GLuint).
+ */
+ void bindTexture(GLenum target, GLuint texture);
+
+ /**
+ * Deletes the specified texture and clears it from the cache
+ * of bound textures.
+ * All textures must be deleted using this method.
+ */
+ void deleteTexture(GLuint texture);
+
+ /**
+ * Signals that the cache of bound textures should be cleared.
+ * Other users of the context may have altered which textures are bound.
+ */
+ void resetBoundTextures();
+
+ /**
+ * Clear the cache of bound textures.
+ */
+ void unbindTexture(GLuint texture);
+private:
+ // total number of texture units available for use
+ static const int kTextureUnitsCount = 3;
+
+ TextureState();
+ GLuint mTextureUnit;
+
+ // Caches texture bindings for the GL_TEXTURE_2D target
+ GLuint mBoundTextures[kTextureUnitsCount];
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif // RENDERSTATE_BLEND_H
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 71ecba5..6346479 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -16,19 +16,19 @@
#include "CanvasContext.h"
-#include <algorithm>
-#include <private/hwui/DrawGlInfo.h>
-#include <strings.h>
-
#include "EglManager.h"
#include "RenderThread.h"
#include "../AnimationContext.h"
#include "../Caches.h"
#include "../DeferredLayerUpdater.h"
-#include "../RenderState.h"
+#include "../renderstate/RenderState.h"
+#include "../renderstate/Stencil.h"
#include "../LayerRenderer.h"
#include "../OpenGLRenderer.h"
-#include "../Stencil.h"
+
+#include <algorithm>
+#include <private/hwui/DrawGlInfo.h>
+#include <strings.h>
#define TRIM_MEMORY_COMPLETE 80
#define TRIM_MEMORY_UI_HIDDEN 20
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 0aa0439..3afca2f 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -16,16 +16,19 @@
#include "EglManager.h"
+#include "../Caches.h"
+#include "../renderstate/RenderState.h"
+#include "RenderThread.h"
+
#include <cutils/log.h>
#include <cutils/properties.h>
-
-#include "../Caches.h"
-#include "../RenderState.h"
-#include "RenderThread.h"
+#include <EGL/eglext.h>
#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions"
#define GLES_VERSION 2
+#define WAIT_FOR_GPU_COMPLETION 0
+
// Android-specific addition that is used to show when frames began in systrace
EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
@@ -214,9 +217,6 @@
if (mEglDisplay == EGL_NO_DISPLAY) return;
usePBufferSurface();
- if (Caches::hasInstance()) {
- Caches::getInstance().terminate();
- }
mRenderThread.renderState().onGLContextDestroyed();
eglDestroyContext(mEglDisplay, mEglContext);
@@ -263,6 +263,14 @@
bool EglManager::swapBuffers(EGLSurface surface) {
mInFrame = false;
+
+#if WAIT_FOR_GPU_COMPLETION
+ {
+ ATRACE_NAME("Finishing GPU work");
+ fence();
+ }
+#endif
+
eglSwapBuffers(mEglDisplay, surface);
EGLint err = eglGetError();
if (CC_LIKELY(err == EGL_SUCCESS)) {
@@ -281,6 +289,13 @@
return false;
}
+void EglManager::fence() {
+ EGLSyncKHR fence = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_FENCE_KHR, NULL);
+ eglClientWaitSyncKHR(mEglDisplay, fence,
+ EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, EGL_FOREVER_KHR);
+ eglDestroySyncKHR(mEglDisplay, fence);
+}
+
void EglManager::cancelFrame() {
mInFrame = false;
}
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index e12db3a..b1a18a9 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -55,6 +55,8 @@
void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize);
+ void fence();
+
private:
friend class RenderThread;
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index a2f0625..9a0fbad 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -16,17 +16,15 @@
#include "RenderThread.h"
-#if defined(HAVE_PTHREADS)
-#include <sys/resource.h>
-#endif
-#include <gui/DisplayEventReceiver.h>
-#include <utils/Log.h>
-
-#include "../RenderState.h"
+#include "../renderstate/RenderState.h"
#include "CanvasContext.h"
#include "EglManager.h"
#include "RenderProxy.h"
+#include <gui/DisplayEventReceiver.h>
+#include <sys/resource.h>
+#include <utils/Log.h>
+
namespace android {
using namespace uirenderer::renderthread;
ANDROID_SINGLETON_STATIC_INSTANCE(RenderThread);
@@ -250,9 +248,7 @@
}
bool RenderThread::threadLoop() {
-#if defined(HAVE_PTHREADS)
setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY);
-#endif
initThreadLocals();
int timeoutMillis = -1;
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
index cb5401c..3c30aab 100644
--- a/libs/hwui/thread/TaskManager.cpp
+++ b/libs/hwui/thread/TaskManager.cpp
@@ -14,10 +14,8 @@
* limitations under the License.
*/
-#include <sys/sysinfo.h>
-#if defined(HAVE_PTHREADS)
#include <sys/resource.h>
-#endif
+#include <sys/sysinfo.h>
#include "TaskManager.h"
#include "Task.h"
@@ -83,9 +81,7 @@
///////////////////////////////////////////////////////////////////////////////
status_t TaskManager::WorkerThread::readyToRun() {
-#if defined(HAVE_PTHREADS)
setpriority(PRIO_PROCESS, 0, PRIORITY_FOREGROUND);
-#endif
return NO_ERROR;
}
diff --git a/libs/hwui/utils/Macros.h b/libs/hwui/utils/Macros.h
index 5b7c87c..fe43fdb 100644
--- a/libs/hwui/utils/Macros.h
+++ b/libs/hwui/utils/Macros.h
@@ -18,8 +18,8 @@
#define PREVENT_COPY_AND_ASSIGN(Type) \
private: \
- Type(const Type&); \
- void operator=(const Type&)
+ Type(const Type&) = delete; \
+ void operator=(const Type&) = delete
#define DESCRIPTION_TYPE(Type) \
int compare(const Type& rhs) const { return memcmp(this, &rhs, sizeof(Type));} \
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index fcf222b..bf3387b 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -170,6 +170,9 @@
* Converts a coordinate to a String representation. The outputType
* may be one of FORMAT_DEGREES, FORMAT_MINUTES, or FORMAT_SECONDS.
* The coordinate must be a valid double between -180.0 and 180.0.
+ * This conversion is performed in a method that is dependent on the
+ * default locale, and so is not guaranteed to round-trip with
+ * {@link #convert(String)}.
*
* @throws IllegalArgumentException if coordinate is less than
* -180.0, greater than 180.0, or is not a number.
@@ -217,7 +220,9 @@
/**
* Converts a String in one of the formats described by
* FORMAT_DEGREES, FORMAT_MINUTES, or FORMAT_SECONDS into a
- * double.
+ * double. This conversion is performed in a locale agnostic
+ * method, and so is not guaranteed to round-trip with
+ * {@link #convert(double, int)}.
*
* @throws NullPointerException if coordinate is null
* @throws IllegalArgumentException if the coordinate is not
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 9bcf3c8..f448dc2 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -346,6 +346,31 @@
*/
public static final int ADJUST_SAME = 0;
+ /**
+ * Mute the volume. Has no effect if the stream is already muted.
+ *
+ * @see #adjustVolume(int, int)
+ * @see #adjustStreamVolume(int, int, int)
+ */
+ public static final int ADJUST_MUTE = -100;
+
+ /**
+ * Unmute the volume. Has no effect if the stream is not muted.
+ *
+ * @see #adjustVolume(int, int)
+ * @see #adjustStreamVolume(int, int, int)
+ */
+ public static final int ADJUST_UNMUTE = 100;
+
+ /**
+ * Toggle the mute state. If muted the stream will be unmuted. If not muted
+ * the stream will be muted.
+ *
+ * @see #adjustVolume(int, int)
+ * @see #adjustStreamVolume(int, int, int)
+ */
+ public static final int ADJUST_TOGGLE_MUTE = 101;
+
// Flags should be powers of 2!
/**
@@ -777,13 +802,17 @@
* screen is showing. Another example, if music is playing in the background
* and a call is not active, the music stream will be adjusted.
* <p>
- * This method should only be used by applications that replace the platform-wide
- * management of audio settings or the main telephony application.
- * <p>This method has no effect if the device implements a fixed volume policy
+ * This method should only be used by applications that replace the
+ * platform-wide management of audio settings or the main telephony
+ * application.
+ * <p>
+ * This method has no effect if the device implements a fixed volume policy
* as indicated by {@link #isVolumeFixed()}.
+ *
* @param direction The direction to adjust the volume. One of
- * {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
- * {@link #ADJUST_SAME}.
+ * {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE},
+ * {@link #ADJUST_SAME}, {@link #ADJUST_MUTE},
+ * {@link #ADJUST_UNMUTE}, or {@link #ADJUST_TOGGLE_MUTE}.
* @param flags One or more flags.
* @see #adjustSuggestedStreamVolume(int, int, int)
* @see #adjustStreamVolume(int, int, int)
@@ -808,16 +837,20 @@
* Adjusts the volume of the most relevant stream, or the given fallback
* stream.
* <p>
- * This method should only be used by applications that replace the platform-wide
- * management of audio settings or the main telephony application.
- *
- * <p>This method has no effect if the device implements a fixed volume policy
+ * This method should only be used by applications that replace the
+ * platform-wide management of audio settings or the main telephony
+ * application.
+ * <p>
+ * This method has no effect if the device implements a fixed volume policy
* as indicated by {@link #isVolumeFixed()}.
+ *
* @param direction The direction to adjust the volume. One of
- * {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE}, or
- * {@link #ADJUST_SAME}.
+ * {@link #ADJUST_LOWER}, {@link #ADJUST_RAISE},
+ * {@link #ADJUST_SAME}, {@link #ADJUST_MUTE},
+ * {@link #ADJUST_UNMUTE}, or {@link #ADJUST_TOGGLE_MUTE}.
* @param suggestedStreamType The stream type that will be used if there
- * isn't a relevant stream. {@link #USE_DEFAULT_STREAM_TYPE} is valid here.
+ * isn't a relevant stream. {@link #USE_DEFAULT_STREAM_TYPE} is
+ * valid here.
* @param flags One or more flags.
* @see #adjustVolume(int, int)
* @see #adjustStreamVolume(int, int, int)
@@ -1088,72 +1121,72 @@
}
/**
- * Solo or unsolo a particular stream. All other streams are muted.
+ * Solo or unsolo a particular stream.
* <p>
- * The solo command is protected against client process death: if a process
- * with an active solo request on a stream dies, all streams that were muted
- * because of this request will be unmuted automatically.
- * <p>
- * The solo requests for a given stream are cumulative: the AudioManager
- * can receive several solo requests from one or more clients and the stream
- * will be unsoloed only when the same number of unsolo requests are received.
- * <p>
- * For a better user experience, applications MUST unsolo a soloed stream
- * in onPause() and solo is again in onResume() if appropriate.
- * <p>This method has no effect if the device implements a fixed volume policy
- * as indicated by {@link #isVolumeFixed()}.
+ * Do not use. This method has been deprecated and is now a no-op.
+ * {@link #requestAudioFocus} should be used for exclusive audio playback.
*
* @param streamType The stream to be soloed/unsoloed.
- * @param state The required solo state: true for solo ON, false for solo OFF
- *
+ * @param state The required solo state: true for solo ON, false for solo
+ * OFF
* @see #isVolumeFixed()
+ * @deprecated Do not use. If you need exclusive audio playback use
+ * {@link #requestAudioFocus}.
*/
+ @Deprecated
public void setStreamSolo(int streamType, boolean state) {
- IAudioService service = getService();
- try {
- service.setStreamSolo(streamType, state, mICallBack);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in setStreamSolo", e);
- }
+ Log.w(TAG, "setStreamSolo has been deprecated. Do not use.");
}
/**
* Mute or unmute an audio stream.
* <p>
- * The mute command is protected against client process death: if a process
- * with an active mute request on a stream dies, this stream will be unmuted
- * automatically.
+ * This method should only be used by applications that replace the
+ * platform-wide management of audio settings or the main telephony
+ * application.
* <p>
- * The mute requests for a given stream are cumulative: the AudioManager
- * can receive several mute requests from one or more clients and the stream
- * will be unmuted only when the same number of unmute requests are received.
- * <p>
- * For a better user experience, applications MUST unmute a muted stream
- * in onPause() and mute is again in onResume() if appropriate.
- * <p>
- * This method should only be used by applications that replace the platform-wide
- * management of audio settings or the main telephony application.
- * <p>This method has no effect if the device implements a fixed volume policy
+ * This method has no effect if the device implements a fixed volume policy
* as indicated by {@link #isVolumeFixed()}.
+ * <p>
+ * This method was deprecated in API level 22. Prior to API level 22 this
+ * method had significantly different behavior and should be used carefully.
+ * The following applies only to pre-22 platforms:
+ * <ul>
+ * <li>The mute command is protected against client process death: if a
+ * process with an active mute request on a stream dies, this stream will be
+ * unmuted automatically.</li>
+ * <li>The mute requests for a given stream are cumulative: the AudioManager
+ * can receive several mute requests from one or more clients and the stream
+ * will be unmuted only when the same number of unmute requests are
+ * received.</li>
+ * <li>For a better user experience, applications MUST unmute a muted stream
+ * in onPause() and mute is again in onResume() if appropriate.</li>
+ * </ul>
*
* @param streamType The stream to be muted/unmuted.
- * @param state The required mute state: true for mute ON, false for mute OFF
- *
+ * @param state The required mute state: true for mute ON, false for mute
+ * OFF
* @see #isVolumeFixed()
+ * @deprecated Use {@link #adjustStreamVolume(int, int, int)} with
+ * {@link #ADJUST_MUTE} or {@link #ADJUST_UNMUTE} instead.
*/
+ @Deprecated
public void setStreamMute(int streamType, boolean state) {
- IAudioService service = getService();
- try {
- service.setStreamMute(streamType, state, mICallBack);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in setStreamMute", e);
+ Log.w(TAG, "setStreamMute is deprecated. adjustStreamVolume should be used instead.");
+ int direction = state ? ADJUST_MUTE : ADJUST_UNMUTE;
+ if (streamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
+ adjustSuggestedStreamVolume(direction, streamType, 0);
+ } else {
+ adjustStreamVolume(streamType, direction, 0);
}
}
/**
- * get stream mute state.
+ * Returns the current mute state for a particular stream.
*
- * @hide
+ * @param streamType The stream to get mute state for.
+ * @return The mute state for the given stream.
+ * @see #adjustStreamVolume(int, int, int)
*/
public boolean isStreamMute(int streamType) {
IAudioService service = getService();
@@ -1166,29 +1199,6 @@
}
/**
- * set master mute state.
- *
- * @hide
- */
- public void setMasterMute(boolean state) {
- setMasterMute(state, FLAG_SHOW_UI);
- }
-
- /**
- * set master mute state with optional flags.
- *
- * @hide
- */
- public void setMasterMute(boolean state, int flags) {
- IAudioService service = getService();
- try {
- service.setMasterMute(state, flags, mContext.getOpPackageName(), mICallBack);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in setMasterMute", e);
- }
- }
-
- /**
* get master mute state.
*
* @hide
diff --git a/media/java/android/media/AudioManagerInternal.java b/media/java/android/media/AudioManagerInternal.java
index 616bdd1..873c142 100644
--- a/media/java/android/media/AudioManagerInternal.java
+++ b/media/java/android/media/AudioManagerInternal.java
@@ -41,9 +41,6 @@
public abstract void adjustMasterVolumeForUid(int steps, int flags, String callingPackage,
int uid);
- public abstract void setMasterMuteForUid(boolean state, int flags, String callingPackage,
- IBinder cb, int uid);
-
public abstract void setRingerModeDelegate(RingerModeDelegate delegate);
public abstract int getRingerModeInternal();
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 766a837..d96fee6 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -748,7 +748,7 @@
setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]]);
}
// apply stream volume
- if (!mStreamStates[streamType].isMuted_syncVSS()) {
+ if (!mStreamStates[streamType].mIsMuted) {
mStreamStates[streamType].applyAllVolumes();
}
}
@@ -970,6 +970,7 @@
if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream="+suggestedStreamType
+ ", flags=" + flags);
int streamType;
+ boolean isMute = isMuteAdjust(direction);
if (mVolumeControlStream != -1) {
streamType = mVolumeControlStream;
} else {
@@ -984,7 +985,8 @@
}
// For notifications/ring, show the ui before making any adjustments
- if (mVolumeController.suppressAdjustment(resolvedStream, flags)) {
+ // Don't suppress mute/unmute requests
+ if (mVolumeController.suppressAdjustment(resolvedStream, flags, isMute)) {
direction = 0;
flags &= ~AudioManager.FLAG_PLAY_SOUND;
flags &= ~AudioManager.FLAG_VIBRATE;
@@ -1011,10 +1013,17 @@
ensureValidDirection(direction);
ensureValidStreamType(streamType);
+ boolean isMuteAdjust = isMuteAdjust(direction);
+
// use stream type alias here so that streams with same alias have the same behavior,
// including with regard to silent mode control (e.g the use of STREAM_RING below and in
// checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION)
int streamTypeAlias = mStreamVolumeAlias[streamType];
+
+ if (isMuteAdjust && !isStreamAffectedByMute(streamTypeAlias)) {
+ return;
+ }
+
VolumeStreamState streamState = mStreamStates[streamTypeAlias];
final int device = getDeviceForStream(streamTypeAlias);
@@ -1100,13 +1109,37 @@
}
}
- if ((direction == AudioManager.ADJUST_RAISE) &&
+ if (isMuteAdjust) {
+ boolean state;
+ if (direction == AudioManager.ADJUST_TOGGLE_MUTE) {
+ state = !streamState.mIsMuted;
+ } else {
+ state = direction == AudioManager.ADJUST_MUTE;
+ }
+ if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
+ setSystemAudioMute(state);
+ }
+ for (int stream = 0; stream < mStreamStates.length; stream++) {
+ if (streamTypeAlias == mStreamVolumeAlias[stream]) {
+ mStreamStates[stream].mute(state);
+
+ Intent intent = new Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION);
+ intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, stream);
+ intent.putExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, state);
+ sendBroadcastToAll(intent);
+ }
+ }
+ } else if ((direction == AudioManager.ADJUST_RAISE) &&
!checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {
- Log.e(TAG, "adjustStreamVolume() safe volume index = "+oldIndex);
+ Log.e(TAG, "adjustStreamVolume() safe volume index = " + oldIndex);
mVolumeController.postDisplaySafeVolumeWarning(flags);
- } else if (streamState.adjustIndex(direction * step, device)) {
- // Post message to set system volume (it in turn will post a message
- // to persist). Do not change volume if stream is muted.
+ } else if (streamState.adjustIndex(direction * step, device) || streamState.mIsMuted) {
+ // Post message to set system volume (it in turn will post a
+ // message to persist).
+ if (streamState.mIsMuted) {
+ // Unmute the stream if it was previously muted
+ streamState.mute(false);
+ }
sendMsg(mAudioHandler,
MSG_SET_DEVICE_VOLUME,
SENDMSG_QUEUE,
@@ -1116,7 +1149,7 @@
0);
}
- // Check if volume update should be send to Hdmi system audio.
+ // Check if volume update should be sent to Hdmi system audio.
int newIndex = mStreamStates[streamType].getIndex(device);
if (streamTypeAlias == AudioSystem.STREAM_MUSIC) {
setSystemAudioVolume(oldIndex, newIndex, getStreamMaxVolume(streamType), flags);
@@ -1129,7 +1162,7 @@
oldIndex != newIndex) {
synchronized (mHdmiPlaybackClient) {
int keyCode = (direction == -1) ? KeyEvent.KEYCODE_VOLUME_DOWN :
- KeyEvent.KEYCODE_VOLUME_UP;
+ KeyEvent.KEYCODE_VOLUME_UP;
mHdmiPlaybackClient.sendKeyEvent(keyCode, true);
mHdmiPlaybackClient.sendKeyEvent(keyCode, false);
}
@@ -1172,6 +1205,10 @@
if (mUseFixedVolume) {
return;
}
+ if (isMuteAdjust(steps)) {
+ setMasterMuteInternal(steps, flags, callingPackage, uid);
+ return;
+ }
ensureValidSteps(steps);
int volume = Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME);
int delta = 0;
@@ -1500,46 +1537,6 @@
}
}
- /** @see AudioManager#setStreamSolo(int, boolean) */
- public void setStreamSolo(int streamType, boolean state, IBinder cb) {
- if (mUseFixedVolume) {
- return;
- }
- int streamAlias = mStreamVolumeAlias[streamType];
- for (int stream = 0; stream < mStreamStates.length; stream++) {
- if (!isStreamAffectedByMute(streamAlias) || streamAlias == mStreamVolumeAlias[stream]) {
- continue;
- }
- mStreamStates[stream].mute(cb, state);
- }
- }
-
- /** @see AudioManager#setStreamMute(int, boolean) */
- public void setStreamMute(int streamType, boolean state, IBinder cb) {
- if (mUseFixedVolume) {
- return;
- }
- if (streamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
- streamType = getActiveStreamType(streamType);
- }
- int streamAlias = mStreamVolumeAlias[streamType];
- if (isStreamAffectedByMute(streamAlias)) {
- if (streamAlias == AudioSystem.STREAM_MUSIC) {
- setSystemAudioMute(state);
- }
- for (int stream = 0; stream < mStreamStates.length; stream++) {
- if (streamAlias == mStreamVolumeAlias[stream]) {
- mStreamStates[stream].mute(cb, state);
-
- Intent intent = new Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION);
- intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, stream);
- intent.putExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, state);
- sendBroadcastToAll(intent);
- }
- }
- }
- }
-
private void setSystemAudioMute(boolean state) {
if (mHdmiManager == null || mHdmiTvClient == null) return;
synchronized (mHdmiManager) {
@@ -1561,7 +1558,7 @@
streamType = getActiveStreamType(streamType);
}
synchronized (VolumeStreamState.class) {
- return mStreamStates[streamType].isMuted_syncVSS();
+ return mStreamStates[streamType].mIsMuted;
}
}
@@ -1665,20 +1662,17 @@
}
}
- /** @see AudioManager#setMasterMute(boolean, int) */
- public void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb) {
- setMasterMuteInternal(state, flags, callingPackage, cb, Binder.getCallingUid());
- }
-
- private void setMasterMuteInternal(boolean state, int flags, String callingPackage, IBinder cb,
- int uid) {
- if (mUseFixedVolume) {
- return;
- }
+ private void setMasterMuteInternal(int adjust, int flags, String callingPackage, int uid) {
if (mAppOps.noteOp(AppOpsManager.OP_AUDIO_MASTER_VOLUME, uid, callingPackage)
!= AppOpsManager.MODE_ALLOWED) {
return;
}
+ boolean state;
+ if (adjust == AudioManager.ADJUST_TOGGLE_MUTE) {
+ state = !AudioSystem.getMasterMute();
+ } else {
+ state = adjust == AudioManager.ADJUST_MUTE;
+ }
if (state != AudioSystem.getMasterMute()) {
setSystemAudioMute(state);
AudioSystem.setMasterMute(state);
@@ -1714,7 +1708,7 @@
int index = mStreamStates[streamType].getIndex(device);
// by convention getStreamVolume() returns 0 when a stream is muted.
- if (mStreamStates[streamType].isMuted_syncVSS()) {
+ if (mStreamStates[streamType].mIsMuted) {
index = 0;
}
if (index != 0 && (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
@@ -1934,11 +1928,11 @@
}
}
}
- mStreamStates[streamType].mute(null, false);
+ mStreamStates[streamType].mute(false);
mRingerModeMutedStreams &= ~(1 << streamType);
} else {
// mute
- mStreamStates[streamType].mute(null, true);
+ mStreamStates[streamType].mute(true);
mRingerModeMutedStreams |= (1 << streamType);
}
}
@@ -2426,13 +2420,9 @@
streamState.readSettings();
synchronized (VolumeStreamState.class) {
// unmute stream that was muted but is not affect by mute anymore
- if (streamState.isMuted_syncVSS() && ((!isStreamAffectedByMute(streamType) &&
+ if (streamState.mIsMuted && ((!isStreamAffectedByMute(streamType) &&
!isStreamMutedByRingerMode(streamType)) || mUseFixedVolume)) {
- int size = streamState.mDeathHandlers.size();
- for (int i = 0; i < size; i++) {
- streamState.mDeathHandlers.get(i).mMuteCount = 1;
- streamState.mDeathHandlers.get(i).mute_syncVSS(false);
- }
+ streamState.mIsMuted = false;
}
}
}
@@ -3221,8 +3211,16 @@
}
private void ensureValidDirection(int direction) {
- if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) {
- throw new IllegalArgumentException("Bad direction " + direction);
+ switch (direction) {
+ case AudioManager.ADJUST_LOWER:
+ case AudioManager.ADJUST_RAISE:
+ case AudioManager.ADJUST_SAME:
+ case AudioManager.ADJUST_MUTE:
+ case AudioManager.ADJUST_UNMUTE:
+ case AudioManager.ADJUST_TOGGLE_MUTE:
+ break;
+ default:
+ throw new IllegalArgumentException("Bad direction " + direction);
}
}
@@ -3238,6 +3236,11 @@
}
}
+ private boolean isMuteAdjust(int adjust) {
+ return adjust == AudioManager.ADJUST_MUTE || adjust == AudioManager.ADJUST_UNMUTE
+ || adjust == AudioManager.ADJUST_TOGGLE_MUTE;
+ }
+
private boolean isInCommunication() {
boolean IsInCall = false;
@@ -3467,11 +3470,11 @@
public class VolumeStreamState {
private final int mStreamType;
+ private boolean mIsMuted;
private String mVolumeIndexSettingName;
private int mIndexMax;
private final ConcurrentHashMap<Integer, Integer> mIndex =
new ConcurrentHashMap<Integer, Integer>(8, 0.75f, 4);
- private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo clients death
private VolumeStreamState(String settingName, int streamType) {
@@ -3482,9 +3485,6 @@
AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
mIndexMax *= 10;
- // mDeathHandlers must be created before calling readSettings()
- mDeathHandlers = new ArrayList<VolumeDeathHandler>();
-
readSettings();
}
@@ -3549,7 +3549,7 @@
// must be called while synchronized VolumeStreamState.class
public void applyDeviceVolume_syncVSS(int device) {
int index;
- if (isMuted_syncVSS()) {
+ if (mIsMuted) {
index = 0;
} else if (((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && mAvrcpAbsVolSupported)
|| ((device & mFullVolumeDevices) != 0)) {
@@ -3565,7 +3565,7 @@
// apply default volume first: by convention this will reset all
// devices volumes in audio policy manager to the supplied value
int index;
- if (isMuted_syncVSS()) {
+ if (mIsMuted) {
index = 0;
} else {
index = (getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5)/10;
@@ -3578,7 +3578,7 @@
Map.Entry entry = (Map.Entry)i.next();
int device = ((Integer)entry.getKey()).intValue();
if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
- if (isMuted_syncVSS()) {
+ if (mIsMuted) {
index = 0;
} else if (((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
mAvrcpAbsVolSupported)
@@ -3688,14 +3688,20 @@
}
}
- public void mute(IBinder cb, boolean state) {
+ public void mute(boolean state) {
synchronized (VolumeStreamState.class) {
- VolumeDeathHandler handler = getDeathHandler_syncVSS(cb, state);
- if (handler == null) {
- Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
- return;
+ if (state != mIsMuted) {
+ mIsMuted = state;
+ // Set the new mute volume. This propagates the values to
+ // the audio system, otherwise the volume won't be changed
+ // at the lower level.
+ sendMsg(mAudioHandler,
+ MSG_SET_ALL_VOLUMES,
+ SENDMSG_QUEUE,
+ 0,
+ 0,
+ this, 0);
}
- handler.mute_syncVSS(state);
}
}
@@ -3733,117 +3739,9 @@
return index;
}
- private class VolumeDeathHandler implements IBinder.DeathRecipient {
- private IBinder mICallback; // To be notified of client's death
- private int mMuteCount; // Number of active mutes for this client
-
- VolumeDeathHandler(IBinder cb) {
- mICallback = cb;
- }
-
- // must be called while synchronized VolumeStreamState.class
- public void mute_syncVSS(boolean state) {
- boolean updateVolume = false;
- if (state) {
- if (mMuteCount == 0) {
- // Register for client death notification
- try {
- // mICallback can be 0 if muted by AudioService
- if (mICallback != null) {
- mICallback.linkToDeath(this, 0);
- }
- VolumeStreamState.this.mDeathHandlers.add(this);
- // If the stream is not yet muted by any client, set level to 0
- if (!VolumeStreamState.this.isMuted_syncVSS()) {
- updateVolume = true;
- }
- } catch (RemoteException e) {
- // Client has died!
- binderDied();
- return;
- }
- } else {
- Log.w(TAG, "stream: "+mStreamType+" was already muted by this client");
- }
- mMuteCount++;
- } else {
- if (mMuteCount == 0) {
- Log.e(TAG, "unexpected unmute for stream: "+mStreamType);
- } else {
- mMuteCount--;
- if (mMuteCount == 0) {
- // Unregister from client death notification
- VolumeStreamState.this.mDeathHandlers.remove(this);
- // mICallback can be 0 if muted by AudioService
- if (mICallback != null) {
- mICallback.unlinkToDeath(this, 0);
- }
- if (!VolumeStreamState.this.isMuted_syncVSS()) {
- updateVolume = true;
- }
- }
- }
- }
- if (updateVolume) {
- sendMsg(mAudioHandler,
- MSG_SET_ALL_VOLUMES,
- SENDMSG_QUEUE,
- 0,
- 0,
- VolumeStreamState.this, 0);
- }
- }
-
- public void binderDied() {
- Log.w(TAG, "Volume service client died for stream: "+mStreamType);
- synchronized (VolumeStreamState.class) {
- if (mMuteCount != 0) {
- // Reset all active mute requests from this client.
- mMuteCount = 1;
- mute_syncVSS(false);
- }
- }
- }
- }
-
- private int muteCount() {
- int count = 0;
- int size = mDeathHandlers.size();
- for (int i = 0; i < size; i++) {
- count += mDeathHandlers.get(i).mMuteCount;
- }
- return count;
- }
-
- // must be called while synchronized VolumeStreamState.class
- private boolean isMuted_syncVSS() {
- return muteCount() != 0;
- }
-
- // must be called while synchronized VolumeStreamState.class
- private VolumeDeathHandler getDeathHandler_syncVSS(IBinder cb, boolean state) {
- VolumeDeathHandler handler;
- int size = mDeathHandlers.size();
- for (int i = 0; i < size; i++) {
- handler = mDeathHandlers.get(i);
- if (cb == handler.mICallback) {
- return handler;
- }
- }
- // If this is the first mute request for this client, create a new
- // client death handler. Otherwise, it is an out of sequence unmute request.
- if (state) {
- handler = new VolumeDeathHandler(cb);
- } else {
- Log.w(TAG, "stream was not muted by this client");
- handler = null;
- }
- return handler;
- }
-
private void dump(PrintWriter pw) {
- pw.print(" Mute count: ");
- pw.println(muteCount());
+ pw.print(" Muted: ");
+ pw.println(mIsMuted);
pw.print(" Max: ");
pw.println((mIndexMax + 5) / 10);
pw.print(" Current: ");
@@ -4711,7 +4609,7 @@
synchronized (mLastDeviceConnectMsgTime) {
long time = SystemClock.uptimeMillis();
if (mLastDeviceConnectMsgTime > time) {
- delay = (int)(mLastDeviceConnectMsgTime - time);
+ delay = (int)(mLastDeviceConnectMsgTime - time) + 30;
}
}
}
@@ -5648,7 +5546,10 @@
Settings.Secure.LONG_PRESS_TIMEOUT, 500, UserHandle.USER_CURRENT);
}
- public boolean suppressAdjustment(int resolvedStream, int flags) {
+ public boolean suppressAdjustment(int resolvedStream, int flags, boolean isMute) {
+ if (isMute) {
+ return false;
+ }
boolean suppress = false;
if (resolvedStream == AudioSystem.STREAM_RING && mController != null) {
final long now = SystemClock.uptimeMillis();
@@ -5801,12 +5702,6 @@
public void setRingerModeInternal(int ringerMode, String caller) {
AudioService.this.setRingerModeInternal(ringerMode, caller);
}
-
- @Override
- public void setMasterMuteForUid(boolean state, int flags, String callingPackage, IBinder cb,
- int uid) {
- setMasterMuteInternal(state, flags, callingPackage, cb, uid);
- }
}
//==========================================================================================
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index fad3cec..bfb78a1 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -52,16 +52,10 @@
void setMasterVolume(int index, int flags, String callingPackage);
- void setStreamSolo(int streamType, boolean state, IBinder cb);
-
- void setStreamMute(int streamType, boolean state, IBinder cb);
-
boolean isStreamMute(int streamType);
void forceRemoteSubmixFullVolume(boolean startForcing, IBinder cb);
- void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb);
-
boolean isMasterMute();
int getStreamVolume(int streamType);
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index 0d6b91a..53ab264 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -214,6 +214,11 @@
* the underlying data could be mapped as a pointer in JNI without doing
* any copies with {@code GetDirectBufferAddress}.</p>
*
+ * <p>For raw formats, each plane is only guaranteed to contain data
+ * up to the last pixel in the last row. In other words, the stride
+ * after the last row may not be mapped into the buffer. This is a
+ * necessary requirement for any interleaved format.</p>
+ *
* @return the byte buffer containing the image data for this plane.
*/
public abstract ByteBuffer getBuffer();
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 4f74bdd..32d5b82 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -543,11 +543,6 @@
public int load(String path, int priority)
{
- // pass network streams to player
- if (path.startsWith("http:"))
- return _load(path, priority);
-
- // try local path
int id = 0;
try {
File f = new File(path);
@@ -562,6 +557,7 @@
return id;
}
+ @Override
public int load(Context context, int resId, int priority) {
AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId);
int id = 0;
@@ -576,6 +572,7 @@
return id;
}
+ @Override
public int load(AssetFileDescriptor afd, int priority) {
if (afd != null) {
long len = afd.getLength();
@@ -588,16 +585,17 @@
}
}
+ @Override
public int load(FileDescriptor fd, long offset, long length, int priority) {
return _load(fd, offset, length, priority);
}
- private native final int _load(String uri, int priority);
-
private native final int _load(FileDescriptor fd, long offset, long length, int priority);
+ @Override
public native final boolean unload(int soundID);
+ @Override
public final int play(int soundID, float leftVolume, float rightVolume,
int priority, int loop, float rate) {
if (isRestricted()) {
@@ -620,16 +618,22 @@
}
}
+ @Override
public native final void pause(int streamID);
+ @Override
public native final void resume(int streamID);
+ @Override
public native final void autoPause();
+ @Override
public native final void autoResume();
+ @Override
public native final void stop(int streamID);
+ @Override
public final void setVolume(int streamID, float leftVolume, float rightVolume) {
if (isRestricted()) {
return;
@@ -639,16 +643,21 @@
private native final void _setVolume(int streamID, float leftVolume, float rightVolume);
+ @Override
public void setVolume(int streamID, float volume) {
setVolume(streamID, volume, volume);
}
+ @Override
public native final void setPriority(int streamID, int priority);
+ @Override
public native final void setLoop(int streamID, int loop);
+ @Override
public native final void setRate(int streamID, float rate);
+ @Override
public void setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener)
{
synchronized(mLock) {
@@ -729,52 +738,69 @@
return 0;
}
+ @Override
public int load(Context context, int resId, int priority) {
return 0;
}
+ @Override
public int load(AssetFileDescriptor afd, int priority) {
return 0;
}
+ @Override
public int load(FileDescriptor fd, long offset, long length, int priority) {
return 0;
}
+ @Override
public final boolean unload(int soundID) {
return true;
}
+ @Override
public final int play(int soundID, float leftVolume, float rightVolume,
int priority, int loop, float rate) {
return 0;
}
+ @Override
public final void pause(int streamID) { }
+ @Override
public final void resume(int streamID) { }
+ @Override
public final void autoPause() { }
+ @Override
public final void autoResume() { }
+ @Override
public final void stop(int streamID) { }
+ @Override
public final void setVolume(int streamID,
float leftVolume, float rightVolume) { }
+ @Override
public void setVolume(int streamID, float volume) {
}
+ @Override
public final void setPriority(int streamID, int priority) { }
+ @Override
public final void setLoop(int streamID, int loop) { }
+ @Override
public final void setRate(int streamID, float rate) { }
+ @Override
public void setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener) {
}
+ @Override
public final void release() { }
}
}
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index e82567c..ef8d169 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -33,6 +33,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.service.media.MediaBrowserService;
import android.service.media.IMediaBrowserService;
import android.service.media.IMediaBrowserServiceCallbacks;
@@ -347,8 +348,8 @@
*/
public void unsubscribe(@NonNull String parentId) {
// Check arguments.
- if (parentId == null) {
- throw new IllegalArgumentException("parentId is null");
+ if (TextUtils.isEmpty(parentId)) {
+ throw new IllegalArgumentException("parentId is empty.");
}
// Remove from our list.
@@ -367,6 +368,60 @@
}
/**
+ * Retrieves a specific {@link MediaItem} from the connected service. Not
+ * all services may support this, so falling back to subscribing to the
+ * parent's id should be used when unavailable.
+ *
+ * @param mediaId The id of the item to retrieve.
+ * @param cb The callback to receive the result on.
+ */
+ public void getMediaItem(@NonNull String mediaId, @NonNull final MediaItemCallback cb) {
+ if (TextUtils.isEmpty(mediaId)) {
+ throw new IllegalArgumentException("mediaId is empty.");
+ }
+ if (cb == null) {
+ throw new IllegalArgumentException("cb is null.");
+ }
+ if (mState != CONNECT_STATE_CONNECTED) {
+ Log.i(TAG, "Not connected, unable to retrieve the MediaItem.");
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ cb.onError();
+ }
+ });
+ return;
+ }
+ ResultReceiver receiver = new ResultReceiver(mHandler) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ if (resultCode != 0 || resultData == null
+ || !resultData.containsKey(MediaBrowserService.KEY_MEDIA_ITEM)) {
+ cb.onError();
+ return;
+ }
+ Parcelable item = resultData.getParcelable(MediaBrowserService.KEY_MEDIA_ITEM);
+ if (!(item instanceof MediaItem)) {
+ cb.onError();
+ }
+ cb.onMediaItemLoaded((MediaItem) resultData.getParcelable(
+ MediaBrowserService.KEY_MEDIA_ITEM));
+ }
+ };
+ try {
+ mServiceBinder.getMediaItem(mediaId, receiver);
+ } catch (RemoteException e) {
+ Log.i(TAG, "Remote error getting media item.");
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ cb.onError();
+ }
+ });
+ }
+ }
+
+ /**
* For debugging.
*/
private static String getStateLabel(int state) {
@@ -690,6 +745,27 @@
}
/**
+ * Callback for receiving the result of {@link #getMediaItem}.
+ */
+ public static abstract class MediaItemCallback {
+
+ /**
+ * Called when the item has been returned by the browser service.
+ *
+ * @param item The item that was returned or null if it doesn't exist.
+ */
+ public void onMediaItemLoaded(MediaItem item) {
+ }
+
+ /**
+ * Called when the id doesn't exist or there was an error retrieving the
+ * item.
+ */
+ public void onError() {
+ }
+ }
+
+ /**
* ServiceConnection to the other app.
*/
private class MediaServiceConnection implements ServiceConnection {
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index 7ea269b..9954de5 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -222,13 +222,9 @@
direction, flags);
} else if (isMute) {
if (down) {
- // We need to send two volume events on down, one to mute
- // and one to show the UI
mSessionManager.dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE,
- MediaSessionManager.DIRECTION_MUTE, flags);
+ AudioManager.ADJUST_TOGGLE_MUTE, flags);
}
- mSessionManager.dispatchAdjustVolume(AudioManager.USE_DEFAULT_STREAM_TYPE,
- 0 /* direction, causes UI to show on down */, flags);
}
}
}
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index a4ef851..b4fff8f 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -59,14 +59,6 @@
private Context mContext;
/**
- * Special flag for sending the mute key to dispatchAdjustVolume used by the
- * system.
- *
- * @hide
- */
- public static final int DIRECTION_MUTE = -99;
-
- /**
* @hide
*/
public MediaSessionManager(Context context) {
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 8c478ed..f29be0d 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -310,7 +310,9 @@
public void run() {
try {
if (DEBUG) Log.d(TAG, "notifySessionEvent(" + eventType + ")");
- mSessionCallback.onSessionEvent(eventType, eventArgs);
+ if (mSessionCallback != null) {
+ mSessionCallback.onSessionEvent(eventType, eventArgs);
+ }
} catch (RemoteException e) {
Log.w(TAG, "error in sending event (event=" + eventType + ")");
}
@@ -329,7 +331,9 @@
public void run() {
try {
if (DEBUG) Log.d(TAG, "notifyChannelRetuned");
- mSessionCallback.onChannelRetuned(channelUri);
+ if (mSessionCallback != null) {
+ mSessionCallback.onChannelRetuned(channelUri);
+ }
} catch (RemoteException e) {
Log.w(TAG, "error in notifyChannelRetuned");
}
@@ -366,7 +370,9 @@
public void run() {
try {
if (DEBUG) Log.d(TAG, "notifyTracksChanged");
- mSessionCallback.onTracksChanged(tracks);
+ if (mSessionCallback != null) {
+ mSessionCallback.onTracksChanged(tracks);
+ }
} catch (RemoteException e) {
Log.w(TAG, "error in notifyTracksChanged");
}
@@ -394,7 +400,9 @@
public void run() {
try {
if (DEBUG) Log.d(TAG, "notifyTrackSelected");
- mSessionCallback.onTrackSelected(type, trackId);
+ if (mSessionCallback != null) {
+ mSessionCallback.onTrackSelected(type, trackId);
+ }
} catch (RemoteException e) {
Log.w(TAG, "error in notifyTrackSelected");
}
@@ -415,7 +423,9 @@
public void run() {
try {
if (DEBUG) Log.d(TAG, "notifyVideoAvailable");
- mSessionCallback.onVideoAvailable();
+ if (mSessionCallback != null) {
+ mSessionCallback.onVideoAvailable();
+ }
} catch (RemoteException e) {
Log.w(TAG, "error in notifyVideoAvailable");
}
@@ -447,7 +457,9 @@
public void run() {
try {
if (DEBUG) Log.d(TAG, "notifyVideoUnavailable");
- mSessionCallback.onVideoUnavailable(reason);
+ if (mSessionCallback != null) {
+ mSessionCallback.onVideoUnavailable(reason);
+ }
} catch (RemoteException e) {
Log.w(TAG, "error in notifyVideoUnavailable");
}
@@ -486,7 +498,9 @@
public void run() {
try {
if (DEBUG) Log.d(TAG, "notifyContentAllowed");
- mSessionCallback.onContentAllowed();
+ if (mSessionCallback != null) {
+ mSessionCallback.onContentAllowed();
+ }
} catch (RemoteException e) {
Log.w(TAG, "error in notifyContentAllowed");
}
@@ -526,7 +540,9 @@
public void run() {
try {
if (DEBUG) Log.d(TAG, "notifyContentBlocked");
- mSessionCallback.onContentBlocked(rating.flattenToString());
+ if (mSessionCallback != null) {
+ mSessionCallback.onContentBlocked(rating.flattenToString());
+ }
} catch (RemoteException e) {
Log.w(TAG, "error in notifyContentBlocked");
}
@@ -557,7 +573,9 @@
try {
if (DEBUG) Log.d(TAG, "layoutSurface (l=" + left + ", t=" + top + ", r="
+ right + ", b=" + bottom + ",)");
- mSessionCallback.onLayoutSurface(left, top, right, bottom);
+ if (mSessionCallback != null) {
+ mSessionCallback.onLayoutSurface(left, top, right, bottom);
+ }
} catch (RemoteException e) {
Log.w(TAG, "error in layoutSurface");
}
diff --git a/media/java/android/service/media/IMediaBrowserService.aidl b/media/java/android/service/media/IMediaBrowserService.aidl
index 01285ee..f01fc07 100644
--- a/media/java/android/service/media/IMediaBrowserService.aidl
+++ b/media/java/android/service/media/IMediaBrowserService.aidl
@@ -6,6 +6,7 @@
import android.service.media.IMediaBrowserServiceCallbacks;
import android.net.Uri;
import android.os.Bundle;
+import android.os.ResultReceiver;
/**
* Media API allows clients to browse through hierarchy of a user’s media collection,
@@ -18,4 +19,5 @@
void addSubscription(String uri, IMediaBrowserServiceCallbacks callbacks);
void removeSubscription(String uri, IMediaBrowserServiceCallbacks callbacks);
+ void getMediaItem(String uri, in ResultReceiver cb);
}
\ No newline at end of file
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index 47cb94b..8287344 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -32,8 +32,10 @@
import android.os.IBinder;
import android.os.Handler;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.service.media.IMediaBrowserService;
import android.service.media.IMediaBrowserServiceCallbacks;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -74,6 +76,13 @@
@SdkConstant(SdkConstantType.SERVICE_ACTION)
public static final String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
+ /**
+ * A key for passing the MediaItem to the ResultReceiver in getMediaItem.
+ *
+ * @hide
+ */
+ public static final String KEY_MEDIA_ITEM = "media_item";
+
private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap();
private final Handler mHandler = new Handler();
private ServiceBinder mBinder;
@@ -261,6 +270,33 @@
}
});
}
+
+ @Override
+ public void getMediaItem(final String mediaId, final ResultReceiver receiver) {
+ if (TextUtils.isEmpty(mediaId) || receiver == null) {
+ return;
+ }
+
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ final Result<MediaBrowser.MediaItem> result
+ = new Result<MediaBrowser.MediaItem>(mediaId) {
+ @Override
+ void onResultSent(MediaBrowser.MediaItem item) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(KEY_MEDIA_ITEM, item);
+ receiver.send(0, bundle);
+ }
+ };
+ try {
+ MediaBrowserService.this.getMediaItem(mediaId, result);
+ } catch (UnsupportedOperationException e) {
+ receiver.send(-1, null);
+ }
+ }
+ });
+ }
}
@Override
@@ -284,20 +320,21 @@
/**
* Called to get the root information for browsing by a particular client.
* <p>
- * The implementation should verify that the client package has
- * permission to access browse media information before returning
- * the root id; it should return null if the client is not
- * allowed to access this information.
+ * The implementation should verify that the client package has permission
+ * to access browse media information before returning the root id; it
+ * should return null if the client is not allowed to access this
+ * information.
* </p>
*
- * @param clientPackageName The package name of the application
- * which is requesting access to browse media.
- * @param clientUid The uid of the application which is requesting
- * access to browse media.
+ * @param clientPackageName The package name of the application which is
+ * requesting access to browse media.
+ * @param clientUid The uid of the application which is requesting access to
+ * browse media.
* @param rootHints An optional bundle of service-specific arguments to send
- * to the media browse service when connecting and retrieving the root id
- * for browsing, or null if none. The contents of this bundle may affect
- * the information returned when browsing.
+ * to the media browse service when connecting and retrieving the
+ * root id for browsing, or null if none. The contents of this
+ * bundle may affect the information returned when browsing.
+ * @return The {@link BrowserRoot} for accessing this app's content or null.
*/
public abstract @Nullable BrowserRoot onGetRoot(@NonNull String clientPackageName,
int clientUid, @Nullable Bundle rootHints);
@@ -305,24 +342,51 @@
/**
* Called to get information about the children of a media item.
* <p>
- * Implementations must call result.{@link Result#sendResult result.sendResult} with the list
- * of children. If loading the children will be an expensive operation that should be performed
- * on another thread, result.{@link Result#detach result.detach} may be called before returning
- * from this function, and then {@link Result#sendResult result.sendResult} called when
- * the loading is complete.
+ * Implementations must call {@link Result#sendResult result.sendResult}
+ * with the list of children. If loading the children will be an expensive
+ * operation that should be performed on another thread,
+ * {@link Result#detach result.detach} may be called before returning from
+ * this function, and then {@link Result#sendResult result.sendResult}
+ * called when the loading is complete.
*
- * @param parentId The id of the parent media item whose
- * children are to be queried.
- * @return The list of children, or null if the id is invalid.
+ * @param parentId The id of the parent media item whose children are to be
+ * queried.
+ * @param result The Result to send the list of children to, or null if the
+ * id is invalid.
*/
public abstract void onLoadChildren(@NonNull String parentId,
@NonNull Result<List<MediaBrowser.MediaItem>> result);
/**
+ * Called to get a specific media item. The mediaId should be the same id
+ * that would be returned for this item when it is in a list of child items.
+ * <p>
+ * Implementations must call {@link Result#sendResult result.sendResult}. If
+ * loading the item will be an expensive operation {@link Result#detach
+ * result.detach} may be called before returning from this function, and
+ * then {@link Result#sendResult result.sendResult} called when the item has
+ * been loaded.
+ * <p>
+ * The default implementation throws an exception.
+ *
+ * @param mediaId The id for the specific
+ * {@link android.media.browse.MediaBrowser.MediaItem}.
+ * @param result The Result to send the item to, or null if the id is
+ * invalid.
+ * @throws UnsupportedOperationException
+ */
+ public void getMediaItem(String mediaId, Result<MediaBrowser.MediaItem> result)
+ throws UnsupportedOperationException {
+ throw new UnsupportedOperationException("getMediaItem is not supported.");
+ }
+
+ /**
* Call to set the media session.
* <p>
* This should be called as soon as possible during the service's startup.
* It may only be called once.
+ *
+ * @param token The token for the service's {@link MediaSession}.
*/
public void setSessionToken(final MediaSession.Token token) {
if (token == null) {
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 7e68c78..5406130 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -351,7 +351,7 @@
int bytesPerPixel = 0;
dataSize = ySize = cSize = cStride = 0;
- int32_t fmt = buffer->format;
+ int32_t fmt = buffer->flexFormat;
bool usingRGBAOverride = usingRGBAToJpegOverride(fmt, readerFormat);
fmt = applyFormatOverrides(fmt, readerFormat);
@@ -363,18 +363,21 @@
(idx == 1) ?
buffer->dataCb :
buffer->dataCr;
+ // only map until last pixel
if (idx == 0) {
- dataSize = buffer->stride * buffer->height;
+ dataSize = buffer->stride * (buffer->height - 1) + buffer->width;
} else {
- dataSize = buffer->chromaStride * buffer->height / 2;
+ dataSize = buffer->chromaStride * (buffer->height / 2 - 1) +
+ buffer->chromaStep * (buffer->width / 2 - 1) + 1;
}
break;
// NV21
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
cr = buffer->data + (buffer->stride * buffer->height);
cb = cr + 1;
- ySize = buffer->width * buffer->height;
- cSize = buffer->width * buffer->height / 2;
+ // only map until last pixel
+ ySize = buffer->width * (buffer->height - 1) + buffer->width;
+ cSize = buffer->width * (buffer->height / 2 - 1) + buffer->width - 1;
pData =
(idx == 0) ?
@@ -488,7 +491,7 @@
int pixelStride = 0;
ALOG_ASSERT(buffer != NULL, "buffer is NULL");
- int32_t fmt = buffer->format;
+ int32_t fmt = buffer->flexFormat;
fmt = applyFormatOverrides(fmt, readerFormat);
@@ -548,7 +551,7 @@
int rowStride = 0;
ALOG_ASSERT(buffer != NULL, "buffer is NULL");
- int32_t fmt = buffer->format;
+ int32_t fmt = buffer->flexFormat;
fmt = applyFormatOverrides(fmt, readerFormat);
@@ -796,7 +799,7 @@
return ACQUIRE_NO_BUFFERS;
}
- if (buffer->format == HAL_PIXEL_FORMAT_YCrCb_420_SP) {
+ if (buffer->flexFormat == HAL_PIXEL_FORMAT_YCrCb_420_SP) {
jniThrowException(env, "java/lang/UnsupportedOperationException",
"NV21 format is not supported by ImageReader");
return -1;
@@ -825,8 +828,10 @@
}
int bufFmt = buffer->format;
+ if (imgReaderFmt == HAL_PIXEL_FORMAT_YCbCr_420_888) {
+ bufFmt = buffer->flexFormat;
+ }
if (imgReaderFmt != bufFmt) {
-
if (imgReaderFmt == HAL_PIXEL_FORMAT_YCbCr_420_888 && (bufFmt ==
HAL_PIXEL_FORMAT_YCrCb_420_SP || bufFmt == HAL_PIXEL_FORMAT_YV12)) {
// Special casing for when producer switches to a format compatible with flexible YUV
@@ -848,7 +853,7 @@
String8 msg;
msg.appendFormat("The producer output buffer format 0x%x doesn't "
"match the ImageReader's configured buffer format 0x%x.",
- buffer->format, ctx->getBufferFormat());
+ bufFmt, ctx->getBufferFormat());
jniThrowException(env, "java/lang/UnsupportedOperationException",
msg.string());
return -1;
diff --git a/media/jni/soundpool/Android.mk b/media/jni/soundpool/Android.mk
index 3382512..71ab013 100644
--- a/media/jni/soundpool/Android.mk
+++ b/media/jni/soundpool/Android.mk
@@ -2,7 +2,9 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- android_media_SoundPool_SoundPoolImpl.cpp
+ android_media_SoundPool_SoundPoolImpl.cpp \
+ SoundPool.cpp \
+ SoundPoolThread.cpp
LOCAL_SHARED_LIBRARIES := \
liblog \
@@ -10,7 +12,9 @@
libutils \
libandroid_runtime \
libnativehelper \
- libmedia
+ libmedia \
+ libmediandk \
+ libbinder
LOCAL_MODULE:= libsoundpool
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
new file mode 100644
index 0000000..a8b91d2
--- /dev/null
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -0,0 +1,1018 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoundPool"
+
+#include <inttypes.h>
+
+#include <utils/Log.h>
+
+#define USE_SHARED_MEM_BUFFER
+
+#include <media/AudioTrack.h>
+#include <media/IMediaHTTPService.h>
+#include <media/mediaplayer.h>
+#include <media/stagefright/MediaExtractor.h>
+#include "SoundPool.h"
+#include "SoundPoolThread.h"
+#include <media/AudioPolicyHelper.h>
+#include <ndk/NdkMediaCodec.h>
+#include <ndk/NdkMediaExtractor.h>
+#include <ndk/NdkMediaFormat.h>
+
+namespace android
+{
+
+int kDefaultBufferCount = 4;
+uint32_t kMaxSampleRate = 48000;
+uint32_t kDefaultSampleRate = 44100;
+uint32_t kDefaultFrameCount = 1200;
+size_t kDefaultHeapSize = 1024 * 1024; // 1MB
+
+
+SoundPool::SoundPool(int maxChannels, const audio_attributes_t* pAttributes)
+{
+ ALOGV("SoundPool constructor: maxChannels=%d, attr.usage=%d, attr.flags=0x%x, attr.tags=%s",
+ maxChannels, pAttributes->usage, pAttributes->flags, pAttributes->tags);
+
+ // check limits
+ mMaxChannels = maxChannels;
+ if (mMaxChannels < 1) {
+ mMaxChannels = 1;
+ }
+ else if (mMaxChannels > 32) {
+ mMaxChannels = 32;
+ }
+ ALOGW_IF(maxChannels != mMaxChannels, "App requested %d channels", maxChannels);
+
+ mQuit = false;
+ mDecodeThread = 0;
+ memcpy(&mAttributes, pAttributes, sizeof(audio_attributes_t));
+ mAllocated = 0;
+ mNextSampleID = 0;
+ mNextChannelID = 0;
+
+ mCallback = 0;
+ mUserData = 0;
+
+ mChannelPool = new SoundChannel[mMaxChannels];
+ for (int i = 0; i < mMaxChannels; ++i) {
+ mChannelPool[i].init(this);
+ mChannels.push_back(&mChannelPool[i]);
+ }
+
+ // start decode thread
+ startThreads();
+}
+
+SoundPool::~SoundPool()
+{
+ ALOGV("SoundPool destructor");
+ mDecodeThread->quit();
+ quit();
+
+ Mutex::Autolock lock(&mLock);
+
+ mChannels.clear();
+ if (mChannelPool)
+ delete [] mChannelPool;
+ // clean up samples
+ ALOGV("clear samples");
+ mSamples.clear();
+
+ if (mDecodeThread)
+ delete mDecodeThread;
+}
+
+void SoundPool::addToRestartList(SoundChannel* channel)
+{
+ Mutex::Autolock lock(&mRestartLock);
+ if (!mQuit) {
+ mRestart.push_back(channel);
+ mCondition.signal();
+ }
+}
+
+void SoundPool::addToStopList(SoundChannel* channel)
+{
+ Mutex::Autolock lock(&mRestartLock);
+ if (!mQuit) {
+ mStop.push_back(channel);
+ mCondition.signal();
+ }
+}
+
+int SoundPool::beginThread(void* arg)
+{
+ SoundPool* p = (SoundPool*)arg;
+ return p->run();
+}
+
+int SoundPool::run()
+{
+ mRestartLock.lock();
+ while (!mQuit) {
+ mCondition.wait(mRestartLock);
+ ALOGV("awake");
+ if (mQuit) break;
+
+ while (!mStop.empty()) {
+ SoundChannel* channel;
+ ALOGV("Getting channel from stop list");
+ List<SoundChannel* >::iterator iter = mStop.begin();
+ channel = *iter;
+ mStop.erase(iter);
+ mRestartLock.unlock();
+ if (channel != 0) {
+ Mutex::Autolock lock(&mLock);
+ channel->stop();
+ }
+ mRestartLock.lock();
+ if (mQuit) break;
+ }
+
+ while (!mRestart.empty()) {
+ SoundChannel* channel;
+ ALOGV("Getting channel from list");
+ List<SoundChannel*>::iterator iter = mRestart.begin();
+ channel = *iter;
+ mRestart.erase(iter);
+ mRestartLock.unlock();
+ if (channel != 0) {
+ Mutex::Autolock lock(&mLock);
+ channel->nextEvent();
+ }
+ mRestartLock.lock();
+ if (mQuit) break;
+ }
+ }
+
+ mStop.clear();
+ mRestart.clear();
+ mCondition.signal();
+ mRestartLock.unlock();
+ ALOGV("goodbye");
+ return 0;
+}
+
+void SoundPool::quit()
+{
+ mRestartLock.lock();
+ mQuit = true;
+ mCondition.signal();
+ mCondition.wait(mRestartLock);
+ ALOGV("return from quit");
+ mRestartLock.unlock();
+}
+
+bool SoundPool::startThreads()
+{
+ createThreadEtc(beginThread, this, "SoundPool");
+ if (mDecodeThread == NULL)
+ mDecodeThread = new SoundPoolThread(this);
+ return mDecodeThread != NULL;
+}
+
+SoundChannel* SoundPool::findChannel(int channelID)
+{
+ for (int i = 0; i < mMaxChannels; ++i) {
+ if (mChannelPool[i].channelID() == channelID) {
+ return &mChannelPool[i];
+ }
+ }
+ return NULL;
+}
+
+SoundChannel* SoundPool::findNextChannel(int channelID)
+{
+ for (int i = 0; i < mMaxChannels; ++i) {
+ if (mChannelPool[i].nextChannelID() == channelID) {
+ return &mChannelPool[i];
+ }
+ }
+ return NULL;
+}
+
+int SoundPool::load(int fd, int64_t offset, int64_t length, int priority __unused)
+{
+ ALOGV("load: fd=%d, offset=%" PRId64 ", length=%" PRId64 ", priority=%d",
+ fd, offset, length, priority);
+ Mutex::Autolock lock(&mLock);
+ sp<Sample> sample = new Sample(++mNextSampleID, fd, offset, length);
+ mSamples.add(sample->sampleID(), sample);
+ doLoad(sample);
+ return sample->sampleID();
+}
+
+void SoundPool::doLoad(sp<Sample>& sample)
+{
+ ALOGV("doLoad: loading sample sampleID=%d", sample->sampleID());
+ sample->startLoad();
+ mDecodeThread->loadSample(sample->sampleID());
+}
+
+bool SoundPool::unload(int sampleID)
+{
+ ALOGV("unload: sampleID=%d", sampleID);
+ Mutex::Autolock lock(&mLock);
+ return mSamples.removeItem(sampleID);
+}
+
+int SoundPool::play(int sampleID, float leftVolume, float rightVolume,
+ int priority, int loop, float rate)
+{
+ ALOGV("play sampleID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f",
+ sampleID, leftVolume, rightVolume, priority, loop, rate);
+ sp<Sample> sample;
+ SoundChannel* channel;
+ int channelID;
+
+ Mutex::Autolock lock(&mLock);
+
+ if (mQuit) {
+ return 0;
+ }
+ // is sample ready?
+ sample = findSample(sampleID);
+ if ((sample == 0) || (sample->state() != Sample::READY)) {
+ ALOGW(" sample %d not READY", sampleID);
+ return 0;
+ }
+
+ dump();
+
+ // allocate a channel
+ channel = allocateChannel_l(priority);
+
+ // no channel allocated - return 0
+ if (!channel) {
+ ALOGV("No channel allocated");
+ return 0;
+ }
+
+ channelID = ++mNextChannelID;
+
+ ALOGV("play channel %p state = %d", channel, channel->state());
+ channel->play(sample, channelID, leftVolume, rightVolume, priority, loop, rate);
+ return channelID;
+}
+
+SoundChannel* SoundPool::allocateChannel_l(int priority)
+{
+ List<SoundChannel*>::iterator iter;
+ SoundChannel* channel = NULL;
+
+ // allocate a channel
+ if (!mChannels.empty()) {
+ iter = mChannels.begin();
+ if (priority >= (*iter)->priority()) {
+ channel = *iter;
+ mChannels.erase(iter);
+ ALOGV("Allocated active channel");
+ }
+ }
+
+ // update priority and put it back in the list
+ if (channel) {
+ channel->setPriority(priority);
+ for (iter = mChannels.begin(); iter != mChannels.end(); ++iter) {
+ if (priority < (*iter)->priority()) {
+ break;
+ }
+ }
+ mChannels.insert(iter, channel);
+ }
+ return channel;
+}
+
+// move a channel from its current position to the front of the list
+void SoundPool::moveToFront_l(SoundChannel* channel)
+{
+ for (List<SoundChannel*>::iterator iter = mChannels.begin(); iter != mChannels.end(); ++iter) {
+ if (*iter == channel) {
+ mChannels.erase(iter);
+ mChannels.push_front(channel);
+ break;
+ }
+ }
+}
+
+void SoundPool::pause(int channelID)
+{
+ ALOGV("pause(%d)", channelID);
+ Mutex::Autolock lock(&mLock);
+ SoundChannel* channel = findChannel(channelID);
+ if (channel) {
+ channel->pause();
+ }
+}
+
+void SoundPool::autoPause()
+{
+ ALOGV("autoPause()");
+ Mutex::Autolock lock(&mLock);
+ for (int i = 0; i < mMaxChannels; ++i) {
+ SoundChannel* channel = &mChannelPool[i];
+ channel->autoPause();
+ }
+}
+
+void SoundPool::resume(int channelID)
+{
+ ALOGV("resume(%d)", channelID);
+ Mutex::Autolock lock(&mLock);
+ SoundChannel* channel = findChannel(channelID);
+ if (channel) {
+ channel->resume();
+ }
+}
+
+void SoundPool::autoResume()
+{
+ ALOGV("autoResume()");
+ Mutex::Autolock lock(&mLock);
+ for (int i = 0; i < mMaxChannels; ++i) {
+ SoundChannel* channel = &mChannelPool[i];
+ channel->autoResume();
+ }
+}
+
+void SoundPool::stop(int channelID)
+{
+ ALOGV("stop(%d)", channelID);
+ Mutex::Autolock lock(&mLock);
+ SoundChannel* channel = findChannel(channelID);
+ if (channel) {
+ channel->stop();
+ } else {
+ channel = findNextChannel(channelID);
+ if (channel)
+ channel->clearNextEvent();
+ }
+}
+
+void SoundPool::setVolume(int channelID, float leftVolume, float rightVolume)
+{
+ Mutex::Autolock lock(&mLock);
+ SoundChannel* channel = findChannel(channelID);
+ if (channel) {
+ channel->setVolume(leftVolume, rightVolume);
+ }
+}
+
+void SoundPool::setPriority(int channelID, int priority)
+{
+ ALOGV("setPriority(%d, %d)", channelID, priority);
+ Mutex::Autolock lock(&mLock);
+ SoundChannel* channel = findChannel(channelID);
+ if (channel) {
+ channel->setPriority(priority);
+ }
+}
+
+void SoundPool::setLoop(int channelID, int loop)
+{
+ ALOGV("setLoop(%d, %d)", channelID, loop);
+ Mutex::Autolock lock(&mLock);
+ SoundChannel* channel = findChannel(channelID);
+ if (channel) {
+ channel->setLoop(loop);
+ }
+}
+
+void SoundPool::setRate(int channelID, float rate)
+{
+ ALOGV("setRate(%d, %f)", channelID, rate);
+ Mutex::Autolock lock(&mLock);
+ SoundChannel* channel = findChannel(channelID);
+ if (channel) {
+ channel->setRate(rate);
+ }
+}
+
+// call with lock held
+void SoundPool::done_l(SoundChannel* channel)
+{
+ ALOGV("done_l(%d)", channel->channelID());
+ // if "stolen", play next event
+ if (channel->nextChannelID() != 0) {
+ ALOGV("add to restart list");
+ addToRestartList(channel);
+ }
+
+ // return to idle state
+ else {
+ ALOGV("move to front");
+ moveToFront_l(channel);
+ }
+}
+
+void SoundPool::setCallback(SoundPoolCallback* callback, void* user)
+{
+ Mutex::Autolock lock(&mCallbackLock);
+ mCallback = callback;
+ mUserData = user;
+}
+
+void SoundPool::notify(SoundPoolEvent event)
+{
+ Mutex::Autolock lock(&mCallbackLock);
+ if (mCallback != NULL) {
+ mCallback(event, this, mUserData);
+ }
+}
+
+void SoundPool::dump()
+{
+ for (int i = 0; i < mMaxChannels; ++i) {
+ mChannelPool[i].dump();
+ }
+}
+
+
+Sample::Sample(int sampleID, int fd, int64_t offset, int64_t length)
+{
+ init();
+ mSampleID = sampleID;
+ mFd = dup(fd);
+ mOffset = offset;
+ mLength = length;
+ ALOGV("create sampleID=%d, fd=%d, offset=%" PRId64 " length=%" PRId64,
+ mSampleID, mFd, mLength, mOffset);
+}
+
+void Sample::init()
+{
+ mSize = 0;
+ mRefCount = 0;
+ mSampleID = 0;
+ mState = UNLOADED;
+ mFd = -1;
+ mOffset = 0;
+ mLength = 0;
+}
+
+Sample::~Sample()
+{
+ ALOGV("Sample::destructor sampleID=%d, fd=%d", mSampleID, mFd);
+ if (mFd > 0) {
+ ALOGV("close(%d)", mFd);
+ ::close(mFd);
+ }
+}
+
+static status_t decode(int fd, int64_t offset, int64_t length,
+ uint32_t *rate, int *numChannels, audio_format_t *audioFormat,
+ sp<MemoryHeapBase> heap, size_t *memsize) {
+
+ ALOGV("fd %d, offset %" PRId64 ", size %" PRId64, fd, offset, length);
+ AMediaExtractor *ex = AMediaExtractor_new();
+ status_t err = AMediaExtractor_setDataSourceFd(ex, fd, offset, length);
+
+ if (err != AMEDIA_OK) {
+ return err;
+ }
+
+ *audioFormat = AUDIO_FORMAT_PCM_16_BIT;
+
+ size_t numTracks = AMediaExtractor_getTrackCount(ex);
+ for (size_t i = 0; i < numTracks; i++) {
+ AMediaFormat *format = AMediaExtractor_getTrackFormat(ex, i);
+ const char *mime;
+ if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) {
+ AMediaExtractor_delete(ex);
+ AMediaFormat_delete(format);
+ return UNKNOWN_ERROR;
+ }
+ if (strncmp(mime, "audio/", 6) == 0) {
+
+ AMediaCodec *codec = AMediaCodec_createDecoderByType(mime);
+ if (AMediaCodec_configure(codec, format,
+ NULL /* window */, NULL /* drm */, 0 /* flags */) != AMEDIA_OK
+ || AMediaCodec_start(codec) != AMEDIA_OK
+ || AMediaExtractor_selectTrack(ex, i) != AMEDIA_OK) {
+ AMediaExtractor_delete(ex);
+ AMediaCodec_delete(codec);
+ AMediaFormat_delete(format);
+ return UNKNOWN_ERROR;
+ }
+
+ bool sawInputEOS = false;
+ bool sawOutputEOS = false;
+ uint8_t* writePos = static_cast<uint8_t*>(heap->getBase());
+ size_t available = heap->getSize();
+ size_t written = 0;
+
+ AMediaFormat_delete(format);
+ format = AMediaCodec_getOutputFormat(codec);
+
+ while (!sawOutputEOS) {
+ if (!sawInputEOS) {
+ ssize_t bufidx = AMediaCodec_dequeueInputBuffer(codec, 5000);
+ ALOGV("input buffer %zd", bufidx);
+ if (bufidx >= 0) {
+ size_t bufsize;
+ uint8_t *buf = AMediaCodec_getInputBuffer(codec, bufidx, &bufsize);
+ int sampleSize = AMediaExtractor_readSampleData(ex, buf, bufsize);
+ ALOGV("read %d", sampleSize);
+ if (sampleSize < 0) {
+ sampleSize = 0;
+ sawInputEOS = true;
+ ALOGV("EOS");
+ }
+ int64_t presentationTimeUs = AMediaExtractor_getSampleTime(ex);
+
+ AMediaCodec_queueInputBuffer(codec, bufidx,
+ 0 /* offset */, sampleSize, presentationTimeUs,
+ sawInputEOS ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
+ AMediaExtractor_advance(ex);
+ }
+ }
+
+ AMediaCodecBufferInfo info;
+ int status = AMediaCodec_dequeueOutputBuffer(codec, &info, 1);
+ ALOGV("dequeueoutput returned: %d", status);
+ if (status >= 0) {
+ if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
+ ALOGV("output EOS");
+ sawOutputEOS = true;
+ }
+ ALOGV("got decoded buffer size %d", info.size);
+
+ uint8_t *buf = AMediaCodec_getOutputBuffer(codec, status, NULL /* out_size */);
+ size_t dataSize = info.size;
+ if (dataSize > available) {
+ dataSize = available;
+ }
+ memcpy(writePos, buf + info.offset, dataSize);
+ writePos += dataSize;
+ written += dataSize;
+ available -= dataSize;
+ AMediaCodec_releaseOutputBuffer(codec, status, false /* render */);
+ if (available == 0) {
+ // there might be more data, but there's no space for it
+ sawOutputEOS = true;
+ }
+ } else if (status == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
+ ALOGV("output buffers changed");
+ } else if (status == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
+ AMediaFormat_delete(format);
+ format = AMediaCodec_getOutputFormat(codec);
+ ALOGV("format changed to: %s", AMediaFormat_toString(format));
+ } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+ ALOGV("no output buffer right now");
+ } else {
+ ALOGV("unexpected info code: %d", status);
+ }
+ }
+
+ AMediaCodec_stop(codec);
+ AMediaCodec_delete(codec);
+ AMediaExtractor_delete(ex);
+ if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, (int32_t*) rate) ||
+ !AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, numChannels)) {
+ AMediaFormat_delete(format);
+ return UNKNOWN_ERROR;
+ }
+ AMediaFormat_delete(format);
+ *memsize = written;
+ return OK;
+ }
+ AMediaFormat_delete(format);
+ }
+ AMediaExtractor_delete(ex);
+ return UNKNOWN_ERROR;
+}
+
+status_t Sample::doLoad()
+{
+ uint32_t sampleRate;
+ int numChannels;
+ audio_format_t format;
+ status_t status;
+ mHeap = new MemoryHeapBase(kDefaultHeapSize);
+
+ ALOGV("Start decode");
+ status = decode(mFd, mOffset, mLength, &sampleRate, &numChannels, &format,
+ mHeap, &mSize);
+ ALOGV("close(%d)", mFd);
+ ::close(mFd);
+ mFd = -1;
+ if (status != NO_ERROR) {
+ ALOGE("Unable to load sample");
+ goto error;
+ }
+ ALOGV("pointer = %p, size = %zu, sampleRate = %u, numChannels = %d",
+ mHeap->getBase(), mSize, sampleRate, numChannels);
+
+ if (sampleRate > kMaxSampleRate) {
+ ALOGE("Sample rate (%u) out of range", sampleRate);
+ status = BAD_VALUE;
+ goto error;
+ }
+
+ if ((numChannels < 1) || (numChannels > 2)) {
+ ALOGE("Sample channel count (%d) out of range", numChannels);
+ status = BAD_VALUE;
+ goto error;
+ }
+
+ mData = new MemoryBase(mHeap, 0, mSize);
+ mSampleRate = sampleRate;
+ mNumChannels = numChannels;
+ mFormat = format;
+ mState = READY;
+ return NO_ERROR;
+
+error:
+ mHeap.clear();
+ return status;
+}
+
+
+void SoundChannel::init(SoundPool* soundPool)
+{
+ mSoundPool = soundPool;
+}
+
+// call with sound pool lock held
+void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftVolume,
+ float rightVolume, int priority, int loop, float rate)
+{
+ sp<AudioTrack> oldTrack;
+ sp<AudioTrack> newTrack;
+ status_t status;
+
+ { // scope for the lock
+ Mutex::Autolock lock(&mLock);
+
+ ALOGV("SoundChannel::play %p: sampleID=%d, channelID=%d, leftVolume=%f, rightVolume=%f,"
+ " priority=%d, loop=%d, rate=%f",
+ this, sample->sampleID(), nextChannelID, leftVolume, rightVolume,
+ priority, loop, rate);
+
+ // if not idle, this voice is being stolen
+ if (mState != IDLE) {
+ ALOGV("channel %d stolen - event queued for channel %d", channelID(), nextChannelID);
+ mNextEvent.set(sample, nextChannelID, leftVolume, rightVolume, priority, loop, rate);
+ stop_l();
+ return;
+ }
+
+ // initialize track
+ size_t afFrameCount;
+ uint32_t afSampleRate;
+ audio_stream_type_t streamType = audio_attributes_to_stream_type(mSoundPool->attributes());
+ if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) {
+ afFrameCount = kDefaultFrameCount;
+ }
+ if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
+ afSampleRate = kDefaultSampleRate;
+ }
+ int numChannels = sample->numChannels();
+ uint32_t sampleRate = uint32_t(float(sample->sampleRate()) * rate + 0.5);
+ size_t frameCount = 0;
+
+ if (loop) {
+ frameCount = sample->size()/numChannels/
+ ((sample->format() == AUDIO_FORMAT_PCM_16_BIT) ? sizeof(int16_t) : sizeof(uint8_t));
+ }
+
+#ifndef USE_SHARED_MEM_BUFFER
+ uint32_t totalFrames = (kDefaultBufferCount * afFrameCount * sampleRate) / afSampleRate;
+ // Ensure minimum audio buffer size in case of short looped sample
+ if(frameCount < totalFrames) {
+ frameCount = totalFrames;
+ }
+#endif
+
+ // mToggle toggles each time a track is started on a given channel.
+ // The toggle is concatenated with the SoundChannel address and passed to AudioTrack
+ // as callback user data. This enables the detection of callbacks received from the old
+ // audio track while the new one is being started and avoids processing them with
+ // wrong audio audio buffer size (mAudioBufferSize)
+ unsigned long toggle = mToggle ^ 1;
+ void *userData = (void *)((unsigned long)this | toggle);
+ audio_channel_mask_t channelMask = audio_channel_out_mask_from_count(numChannels);
+
+ // do not create a new audio track if current track is compatible with sample parameters
+#ifdef USE_SHARED_MEM_BUFFER
+ newTrack = new AudioTrack(streamType, sampleRate, sample->format(),
+ channelMask, sample->getIMemory(), AUDIO_OUTPUT_FLAG_FAST, callback, userData);
+#else
+ uint32_t bufferFrames = (totalFrames + (kDefaultBufferCount - 1)) / kDefaultBufferCount;
+ newTrack = new AudioTrack(streamType, sampleRate, sample->format(),
+ channelMask, frameCount, AUDIO_OUTPUT_FLAG_FAST, callback, userData,
+ bufferFrames);
+#endif
+ oldTrack = mAudioTrack;
+ status = newTrack->initCheck();
+ if (status != NO_ERROR) {
+ ALOGE("Error creating AudioTrack");
+ goto exit;
+ }
+ ALOGV("setVolume %p", newTrack.get());
+ newTrack->setVolume(leftVolume, rightVolume);
+ newTrack->setLoop(0, frameCount, loop);
+
+ // From now on, AudioTrack callbacks received with previous toggle value will be ignored.
+ mToggle = toggle;
+ mAudioTrack = newTrack;
+ mPos = 0;
+ mSample = sample;
+ mChannelID = nextChannelID;
+ mPriority = priority;
+ mLoop = loop;
+ mLeftVolume = leftVolume;
+ mRightVolume = rightVolume;
+ mNumChannels = numChannels;
+ mRate = rate;
+ clearNextEvent();
+ mState = PLAYING;
+ mAudioTrack->start();
+ mAudioBufferSize = newTrack->frameCount()*newTrack->frameSize();
+ }
+
+exit:
+ ALOGV("delete oldTrack %p", oldTrack.get());
+ if (status != NO_ERROR) {
+ mAudioTrack.clear();
+ }
+}
+
+void SoundChannel::nextEvent()
+{
+ sp<Sample> sample;
+ int nextChannelID;
+ float leftVolume;
+ float rightVolume;
+ int priority;
+ int loop;
+ float rate;
+
+ // check for valid event
+ {
+ Mutex::Autolock lock(&mLock);
+ nextChannelID = mNextEvent.channelID();
+ if (nextChannelID == 0) {
+ ALOGV("stolen channel has no event");
+ return;
+ }
+
+ sample = mNextEvent.sample();
+ leftVolume = mNextEvent.leftVolume();
+ rightVolume = mNextEvent.rightVolume();
+ priority = mNextEvent.priority();
+ loop = mNextEvent.loop();
+ rate = mNextEvent.rate();
+ }
+
+ ALOGV("Starting stolen channel %d -> %d", channelID(), nextChannelID);
+ play(sample, nextChannelID, leftVolume, rightVolume, priority, loop, rate);
+}
+
+void SoundChannel::callback(int event, void* user, void *info)
+{
+ SoundChannel* channel = static_cast<SoundChannel*>((void *)((unsigned long)user & ~1));
+
+ channel->process(event, info, (unsigned long)user & 1);
+}
+
+void SoundChannel::process(int event, void *info, unsigned long toggle)
+{
+ //ALOGV("process(%d)", mChannelID);
+
+ Mutex::Autolock lock(&mLock);
+
+ AudioTrack::Buffer* b = NULL;
+ if (event == AudioTrack::EVENT_MORE_DATA) {
+ b = static_cast<AudioTrack::Buffer *>(info);
+ }
+
+ if (mToggle != toggle) {
+ ALOGV("process wrong toggle %p channel %d", this, mChannelID);
+ if (b != NULL) {
+ b->size = 0;
+ }
+ return;
+ }
+
+ sp<Sample> sample = mSample;
+
+// ALOGV("SoundChannel::process event %d", event);
+
+ if (event == AudioTrack::EVENT_MORE_DATA) {
+
+ // check for stop state
+ if (b->size == 0) return;
+
+ if (mState == IDLE) {
+ b->size = 0;
+ return;
+ }
+
+ if (sample != 0) {
+ // fill buffer
+ uint8_t* q = (uint8_t*) b->i8;
+ size_t count = 0;
+
+ if (mPos < (int)sample->size()) {
+ uint8_t* p = sample->data() + mPos;
+ count = sample->size() - mPos;
+ if (count > b->size) {
+ count = b->size;
+ }
+ memcpy(q, p, count);
+// ALOGV("fill: q=%p, p=%p, mPos=%u, b->size=%u, count=%d", q, p, mPos, b->size,
+// count);
+ } else if (mPos < mAudioBufferSize) {
+ count = mAudioBufferSize - mPos;
+ if (count > b->size) {
+ count = b->size;
+ }
+ memset(q, 0, count);
+// ALOGV("fill extra: q=%p, mPos=%u, b->size=%u, count=%d", q, mPos, b->size, count);
+ }
+
+ mPos += count;
+ b->size = count;
+ //ALOGV("buffer=%p, [0]=%d", b->i16, b->i16[0]);
+ }
+ } else if (event == AudioTrack::EVENT_UNDERRUN || event == AudioTrack::EVENT_BUFFER_END) {
+ ALOGV("process %p channel %d event %s",
+ this, mChannelID, (event == AudioTrack::EVENT_UNDERRUN) ? "UNDERRUN" :
+ "BUFFER_END");
+ mSoundPool->addToStopList(this);
+ } else if (event == AudioTrack::EVENT_LOOP_END) {
+ ALOGV("End loop %p channel %d", this, mChannelID);
+ } else if (event == AudioTrack::EVENT_NEW_IAUDIOTRACK) {
+ ALOGV("process %p channel %d NEW_IAUDIOTRACK", this, mChannelID);
+ } else {
+ ALOGW("SoundChannel::process unexpected event %d", event);
+ }
+}
+
+
+// call with lock held
+bool SoundChannel::doStop_l()
+{
+ if (mState != IDLE) {
+ setVolume_l(0, 0);
+ ALOGV("stop");
+ mAudioTrack->stop();
+ mSample.clear();
+ mState = IDLE;
+ mPriority = IDLE_PRIORITY;
+ return true;
+ }
+ return false;
+}
+
+// call with lock held and sound pool lock held
+void SoundChannel::stop_l()
+{
+ if (doStop_l()) {
+ mSoundPool->done_l(this);
+ }
+}
+
+// call with sound pool lock held
+void SoundChannel::stop()
+{
+ bool stopped;
+ {
+ Mutex::Autolock lock(&mLock);
+ stopped = doStop_l();
+ }
+
+ if (stopped) {
+ mSoundPool->done_l(this);
+ }
+}
+
+//FIXME: Pause is a little broken right now
+void SoundChannel::pause()
+{
+ Mutex::Autolock lock(&mLock);
+ if (mState == PLAYING) {
+ ALOGV("pause track");
+ mState = PAUSED;
+ mAudioTrack->pause();
+ }
+}
+
+void SoundChannel::autoPause()
+{
+ Mutex::Autolock lock(&mLock);
+ if (mState == PLAYING) {
+ ALOGV("pause track");
+ mState = PAUSED;
+ mAutoPaused = true;
+ mAudioTrack->pause();
+ }
+}
+
+void SoundChannel::resume()
+{
+ Mutex::Autolock lock(&mLock);
+ if (mState == PAUSED) {
+ ALOGV("resume track");
+ mState = PLAYING;
+ mAutoPaused = false;
+ mAudioTrack->start();
+ }
+}
+
+void SoundChannel::autoResume()
+{
+ Mutex::Autolock lock(&mLock);
+ if (mAutoPaused && (mState == PAUSED)) {
+ ALOGV("resume track");
+ mState = PLAYING;
+ mAutoPaused = false;
+ mAudioTrack->start();
+ }
+}
+
+void SoundChannel::setRate(float rate)
+{
+ Mutex::Autolock lock(&mLock);
+ if (mAudioTrack != NULL && mSample != 0) {
+ uint32_t sampleRate = uint32_t(float(mSample->sampleRate()) * rate + 0.5);
+ mAudioTrack->setSampleRate(sampleRate);
+ mRate = rate;
+ }
+}
+
+// call with lock held
+void SoundChannel::setVolume_l(float leftVolume, float rightVolume)
+{
+ mLeftVolume = leftVolume;
+ mRightVolume = rightVolume;
+ if (mAudioTrack != NULL)
+ mAudioTrack->setVolume(leftVolume, rightVolume);
+}
+
+void SoundChannel::setVolume(float leftVolume, float rightVolume)
+{
+ Mutex::Autolock lock(&mLock);
+ setVolume_l(leftVolume, rightVolume);
+}
+
+void SoundChannel::setLoop(int loop)
+{
+ Mutex::Autolock lock(&mLock);
+ if (mAudioTrack != NULL && mSample != 0) {
+ uint32_t loopEnd = mSample->size()/mNumChannels/
+ ((mSample->format() == AUDIO_FORMAT_PCM_16_BIT) ? sizeof(int16_t) : sizeof(uint8_t));
+ mAudioTrack->setLoop(0, loopEnd, loop);
+ mLoop = loop;
+ }
+}
+
+SoundChannel::~SoundChannel()
+{
+ ALOGV("SoundChannel destructor %p", this);
+ {
+ Mutex::Autolock lock(&mLock);
+ clearNextEvent();
+ doStop_l();
+ }
+ // do not call AudioTrack destructor with mLock held as it will wait for the AudioTrack
+ // callback thread to exit which may need to execute process() and acquire the mLock.
+ mAudioTrack.clear();
+}
+
+void SoundChannel::dump()
+{
+ ALOGV("mState = %d mChannelID=%d, mNumChannels=%d, mPos = %d, mPriority=%d, mLoop=%d",
+ mState, mChannelID, mNumChannels, mPos, mPriority, mLoop);
+}
+
+void SoundEvent::set(const sp<Sample>& sample, int channelID, float leftVolume,
+ float rightVolume, int priority, int loop, float rate)
+{
+ mSample = sample;
+ mChannelID = channelID;
+ mLeftVolume = leftVolume;
+ mRightVolume = rightVolume;
+ mPriority = priority;
+ mLoop = loop;
+ mRate =rate;
+}
+
+} // end namespace android
diff --git a/media/jni/soundpool/SoundPool.h b/media/jni/soundpool/SoundPool.h
new file mode 100644
index 0000000..9d9cbdf
--- /dev/null
+++ b/media/jni/soundpool/SoundPool.h
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2007 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 SOUNDPOOL_H_
+#define SOUNDPOOL_H_
+
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+#include <media/AudioTrack.h>
+#include <binder/MemoryHeapBase.h>
+#include <binder/MemoryBase.h>
+
+namespace android {
+
+static const int IDLE_PRIORITY = -1;
+
+// forward declarations
+class SoundEvent;
+class SoundPoolThread;
+class SoundPool;
+
+// for queued events
+class SoundPoolEvent {
+public:
+ SoundPoolEvent(int msg, int arg1=0, int arg2=0) :
+ mMsg(msg), mArg1(arg1), mArg2(arg2) {}
+ int mMsg;
+ int mArg1;
+ int mArg2;
+ enum MessageType { INVALID, SAMPLE_LOADED };
+};
+
+// callback function prototype
+typedef void SoundPoolCallback(SoundPoolEvent event, SoundPool* soundPool, void* user);
+
+// tracks samples used by application
+class Sample : public RefBase {
+public:
+ enum sample_state { UNLOADED, LOADING, READY, UNLOADING };
+ Sample(int sampleID, int fd, int64_t offset, int64_t length);
+ ~Sample();
+ int sampleID() { return mSampleID; }
+ int numChannels() { return mNumChannels; }
+ int sampleRate() { return mSampleRate; }
+ audio_format_t format() { return mFormat; }
+ size_t size() { return mSize; }
+ int state() { return mState; }
+ uint8_t* data() { return static_cast<uint8_t*>(mData->pointer()); }
+ status_t doLoad();
+ void startLoad() { mState = LOADING; }
+ sp<IMemory> getIMemory() { return mData; }
+
+private:
+ void init();
+
+ size_t mSize;
+ volatile int32_t mRefCount;
+ uint16_t mSampleID;
+ uint16_t mSampleRate;
+ uint8_t mState : 3;
+ uint8_t mNumChannels : 2;
+ audio_format_t mFormat;
+ int mFd;
+ int64_t mOffset;
+ int64_t mLength;
+ sp<IMemory> mData;
+ sp<MemoryHeapBase> mHeap;
+};
+
+// stores pending events for stolen channels
+class SoundEvent
+{
+public:
+ SoundEvent() : mChannelID(0), mLeftVolume(0), mRightVolume(0),
+ mPriority(IDLE_PRIORITY), mLoop(0), mRate(0) {}
+ void set(const sp<Sample>& sample, int channelID, float leftVolume,
+ float rightVolume, int priority, int loop, float rate);
+ sp<Sample> sample() { return mSample; }
+ int channelID() { return mChannelID; }
+ float leftVolume() { return mLeftVolume; }
+ float rightVolume() { return mRightVolume; }
+ int priority() { return mPriority; }
+ int loop() { return mLoop; }
+ float rate() { return mRate; }
+ void clear() { mChannelID = 0; mSample.clear(); }
+
+protected:
+ sp<Sample> mSample;
+ int mChannelID;
+ float mLeftVolume;
+ float mRightVolume;
+ int mPriority;
+ int mLoop;
+ float mRate;
+};
+
+// for channels aka AudioTracks
+class SoundChannel : public SoundEvent {
+public:
+ enum state { IDLE, RESUMING, STOPPING, PAUSED, PLAYING };
+ SoundChannel() : mState(IDLE), mNumChannels(1),
+ mPos(0), mToggle(0), mAutoPaused(false) {}
+ ~SoundChannel();
+ void init(SoundPool* soundPool);
+ void play(const sp<Sample>& sample, int channelID, float leftVolume, float rightVolume,
+ int priority, int loop, float rate);
+ void setVolume_l(float leftVolume, float rightVolume);
+ void setVolume(float leftVolume, float rightVolume);
+ void stop_l();
+ void stop();
+ void pause();
+ void autoPause();
+ void resume();
+ void autoResume();
+ void setRate(float rate);
+ int state() { return mState; }
+ void setPriority(int priority) { mPriority = priority; }
+ void setLoop(int loop);
+ int numChannels() { return mNumChannels; }
+ void clearNextEvent() { mNextEvent.clear(); }
+ void nextEvent();
+ int nextChannelID() { return mNextEvent.channelID(); }
+ void dump();
+
+private:
+ static void callback(int event, void* user, void *info);
+ void process(int event, void *info, unsigned long toggle);
+ bool doStop_l();
+
+ SoundPool* mSoundPool;
+ sp<AudioTrack> mAudioTrack;
+ SoundEvent mNextEvent;
+ Mutex mLock;
+ int mState;
+ int mNumChannels;
+ int mPos;
+ int mAudioBufferSize;
+ unsigned long mToggle;
+ bool mAutoPaused;
+};
+
+// application object for managing a pool of sounds
+class SoundPool {
+ friend class SoundPoolThread;
+ friend class SoundChannel;
+public:
+ SoundPool(int maxChannels, const audio_attributes_t* pAttributes);
+ ~SoundPool();
+ int load(int fd, int64_t offset, int64_t length, int priority);
+ bool unload(int sampleID);
+ int play(int sampleID, float leftVolume, float rightVolume, int priority,
+ int loop, float rate);
+ void pause(int channelID);
+ void autoPause();
+ void resume(int channelID);
+ void autoResume();
+ void stop(int channelID);
+ void setVolume(int channelID, float leftVolume, float rightVolume);
+ void setPriority(int channelID, int priority);
+ void setLoop(int channelID, int loop);
+ void setRate(int channelID, float rate);
+ const audio_attributes_t* attributes() { return &mAttributes; }
+
+ // called from SoundPoolThread
+ void sampleLoaded(int sampleID);
+
+ // called from AudioTrack thread
+ void done_l(SoundChannel* channel);
+
+ // callback function
+ void setCallback(SoundPoolCallback* callback, void* user);
+ void* getUserData() { return mUserData; }
+
+private:
+ SoundPool() {} // no default constructor
+ bool startThreads();
+ void doLoad(sp<Sample>& sample);
+ sp<Sample> findSample(int sampleID) { return mSamples.valueFor(sampleID); }
+ SoundChannel* findChannel (int channelID);
+ SoundChannel* findNextChannel (int channelID);
+ SoundChannel* allocateChannel_l(int priority);
+ void moveToFront_l(SoundChannel* channel);
+ void notify(SoundPoolEvent event);
+ void dump();
+
+ // restart thread
+ void addToRestartList(SoundChannel* channel);
+ void addToStopList(SoundChannel* channel);
+ static int beginThread(void* arg);
+ int run();
+ void quit();
+
+ Mutex mLock;
+ Mutex mRestartLock;
+ Condition mCondition;
+ SoundPoolThread* mDecodeThread;
+ SoundChannel* mChannelPool;
+ List<SoundChannel*> mChannels;
+ List<SoundChannel*> mRestart;
+ List<SoundChannel*> mStop;
+ DefaultKeyedVector< int, sp<Sample> > mSamples;
+ int mMaxChannels;
+ audio_attributes_t mAttributes;
+ int mAllocated;
+ int mNextSampleID;
+ int mNextChannelID;
+ bool mQuit;
+
+ // callback
+ Mutex mCallbackLock;
+ SoundPoolCallback* mCallback;
+ void* mUserData;
+};
+
+} // end namespace android
+
+#endif /*SOUNDPOOL_H_*/
diff --git a/media/jni/soundpool/SoundPoolThread.cpp b/media/jni/soundpool/SoundPoolThread.cpp
new file mode 100644
index 0000000..ba3b482
--- /dev/null
+++ b/media/jni/soundpool/SoundPoolThread.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SoundPoolThread"
+#include "utils/Log.h"
+
+#include "SoundPoolThread.h"
+
+namespace android {
+
+void SoundPoolThread::write(SoundPoolMsg msg) {
+ Mutex::Autolock lock(&mLock);
+ while (mMsgQueue.size() >= maxMessages) {
+ mCondition.wait(mLock);
+ }
+
+ // if thread is quitting, don't add to queue
+ if (mRunning) {
+ mMsgQueue.push(msg);
+ mCondition.signal();
+ }
+}
+
+const SoundPoolMsg SoundPoolThread::read() {
+ Mutex::Autolock lock(&mLock);
+ while (mMsgQueue.size() == 0) {
+ mCondition.wait(mLock);
+ }
+ SoundPoolMsg msg = mMsgQueue[0];
+ mMsgQueue.removeAt(0);
+ mCondition.signal();
+ return msg;
+}
+
+void SoundPoolThread::quit() {
+ Mutex::Autolock lock(&mLock);
+ if (mRunning) {
+ mRunning = false;
+ mMsgQueue.clear();
+ mMsgQueue.push(SoundPoolMsg(SoundPoolMsg::KILL, 0));
+ mCondition.signal();
+ mCondition.wait(mLock);
+ }
+ ALOGV("return from quit");
+}
+
+SoundPoolThread::SoundPoolThread(SoundPool* soundPool) :
+ mSoundPool(soundPool)
+{
+ mMsgQueue.setCapacity(maxMessages);
+ if (createThreadEtc(beginThread, this, "SoundPoolThread")) {
+ mRunning = true;
+ }
+}
+
+SoundPoolThread::~SoundPoolThread()
+{
+ quit();
+}
+
+int SoundPoolThread::beginThread(void* arg) {
+ ALOGV("beginThread");
+ SoundPoolThread* soundPoolThread = (SoundPoolThread*)arg;
+ return soundPoolThread->run();
+}
+
+int SoundPoolThread::run() {
+ ALOGV("run");
+ for (;;) {
+ SoundPoolMsg msg = read();
+ ALOGV("Got message m=%d, mData=%d", msg.mMessageType, msg.mData);
+ switch (msg.mMessageType) {
+ case SoundPoolMsg::KILL:
+ ALOGV("goodbye");
+ return NO_ERROR;
+ case SoundPoolMsg::LOAD_SAMPLE:
+ doLoadSample(msg.mData);
+ break;
+ default:
+ ALOGW("run: Unrecognized message %d\n",
+ msg.mMessageType);
+ break;
+ }
+ }
+}
+
+void SoundPoolThread::loadSample(int sampleID) {
+ write(SoundPoolMsg(SoundPoolMsg::LOAD_SAMPLE, sampleID));
+}
+
+void SoundPoolThread::doLoadSample(int sampleID) {
+ sp <Sample> sample = mSoundPool->findSample(sampleID);
+ status_t status = -1;
+ if (sample != 0) {
+ status = sample->doLoad();
+ }
+ mSoundPool->notify(SoundPoolEvent(SoundPoolEvent::SAMPLE_LOADED, sampleID, status));
+}
+
+} // end namespace android
diff --git a/media/jni/soundpool/SoundPoolThread.h b/media/jni/soundpool/SoundPoolThread.h
new file mode 100644
index 0000000..d388388
--- /dev/null
+++ b/media/jni/soundpool/SoundPoolThread.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2007 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 SOUNDPOOLTHREAD_H_
+#define SOUNDPOOLTHREAD_H_
+
+#include <utils/threads.h>
+#include <utils/Vector.h>
+#include <media/AudioTrack.h>
+
+#include "SoundPool.h"
+
+namespace android {
+
+class SoundPoolMsg {
+public:
+ enum MessageType { INVALID, KILL, LOAD_SAMPLE };
+ SoundPoolMsg() : mMessageType(INVALID), mData(0) {}
+ SoundPoolMsg(MessageType MessageType, int data) :
+ mMessageType(MessageType), mData(data) {}
+ uint16_t mMessageType;
+ uint16_t mData;
+};
+
+/*
+ * This class handles background requests from the SoundPool
+ */
+class SoundPoolThread {
+public:
+ SoundPoolThread(SoundPool* SoundPool);
+ ~SoundPoolThread();
+ void loadSample(int sampleID);
+ void quit();
+ void write(SoundPoolMsg msg);
+
+private:
+ static const size_t maxMessages = 5;
+
+ static int beginThread(void* arg);
+ int run();
+ void doLoadSample(int sampleID);
+ const SoundPoolMsg read();
+
+ Mutex mLock;
+ Condition mCondition;
+ Vector<SoundPoolMsg> mMsgQueue;
+ SoundPool* mSoundPool;
+ bool mRunning;
+};
+
+} // end namespace android
+
+#endif /*SOUNDPOOLTHREAD_H_*/
diff --git a/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
index baf61d5..b2333f8 100644
--- a/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
+++ b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
@@ -23,7 +23,7 @@
#include <nativehelper/jni.h>
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
-#include <media/SoundPool.h>
+#include "SoundPool.h"
using namespace android;
@@ -45,20 +45,6 @@
static audio_attributes_fields_t javaAudioAttrFields;
// ----------------------------------------------------------------------------
-static jint
-android_media_SoundPool_SoundPoolImpl_load_URL(JNIEnv *env, jobject thiz, jstring path, jint priority)
-{
- ALOGV("android_media_SoundPool_SoundPoolImpl_load_URL");
- SoundPool *ap = MusterSoundPool(env, thiz);
- if (path == NULL) {
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- return 0;
- }
- const char* s = env->GetStringUTFChars(path, NULL);
- int id = ap->load(s, priority);
- env->ReleaseStringUTFChars(path, s);
- return (jint) id;
-}
static jint
android_media_SoundPool_SoundPoolImpl_load_FD(JNIEnv *env, jobject thiz, jobject fileDescriptor,
@@ -249,10 +235,6 @@
// Dalvik VM type signatures
static JNINativeMethod gMethods[] = {
{ "_load",
- "(Ljava/lang/String;I)I",
- (void *)android_media_SoundPool_SoundPoolImpl_load_URL
- },
- { "_load",
"(Ljava/io/FileDescriptor;JJI)I",
(void *)android_media_SoundPool_SoundPoolImpl_load_FD
},
diff --git a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
index c2bb90c..0b7b99f 100644
--- a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
+++ b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
@@ -29,7 +29,8 @@
import android.os.ServiceManager;
import android.os.storage.IMountService;
import android.os.storage.StorageManager;
-import android.util.Log;
+import android.text.Editable;
+import android.text.TextWatcher;
import android.util.Slog;
import android.view.View;
import android.widget.Button;
@@ -180,20 +181,6 @@
mEncPassword = (TextView) findViewById(R.id.enc_password);
TextView curPwDesc = (TextView) findViewById(R.id.password_desc);
- // We vary the password prompt depending on whether one is predefined, and whether
- // the device is encrypted.
- mIsEncrypted = deviceIsEncrypted();
- if (!haveBackupPassword()) {
- curPwDesc.setVisibility(View.GONE);
- mCurPassword.setVisibility(View.GONE);
- if (layoutId == R.layout.confirm_backup) {
- TextView encPwDesc = (TextView) findViewById(R.id.enc_password_desc);
- encPwDesc.setText(mIsEncrypted
- ? R.string.backup_enc_password_required
- : R.string.backup_enc_password_optional);
- }
- }
-
mAllowButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -209,6 +196,7 @@
sendAcknowledgement(mToken, false, mObserver);
mAllowButton.setEnabled(false);
mDenyButton.setEnabled(false);
+ finish();
}
});
@@ -218,6 +206,39 @@
mAllowButton.setEnabled(!mDidAcknowledge);
mDenyButton.setEnabled(!mDidAcknowledge);
}
+
+ // We vary the password prompt depending on whether one is predefined, and whether
+ // the device is encrypted.
+ mIsEncrypted = deviceIsEncrypted();
+ if (!haveBackupPassword()) {
+ curPwDesc.setVisibility(View.GONE);
+ mCurPassword.setVisibility(View.GONE);
+ if (layoutId == R.layout.confirm_backup) {
+ TextView encPwDesc = (TextView) findViewById(R.id.enc_password_desc);
+ if (mIsEncrypted) {
+ encPwDesc.setText(R.string.backup_enc_password_required);
+ monitorEncryptionPassword();
+ } else {
+ encPwDesc.setText(R.string.backup_enc_password_optional);
+ }
+ }
+ }
+ }
+
+ private void monitorEncryptionPassword() {
+ mAllowButton.setEnabled(false);
+ mEncPassword.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) { }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ mAllowButton.setEnabled(mEncPassword.getText().length() > 0);
+ }
+ });
}
// Preserve the restore observer callback binder across activity relaunch
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
index 7cee066..5e9ec10 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
@@ -97,14 +97,18 @@
/** Called on mLooper thread */
public void enable() {
- mEnabled = true;
- updateRequirements();
+ if (!mEnabled) {
+ mEnabled = true;
+ updateRequirements();
+ }
}
/** Called on mLooper thread */
public void disable() {
- mEnabled = false;
- updateRequirements();
+ if (mEnabled) {
+ mEnabled = false;
+ updateRequirements();
+ }
}
/** Called on mLooper thread */
@@ -131,16 +135,14 @@
private void enableProvider(String name, long minTime) {
ProviderStats stats = mStats.get(name);
- if (stats.available) {
- if (!stats.requested) {
- stats.requestTime = SystemClock.elapsedRealtime();
- stats.requested = true;
- stats.minTime = minTime;
- mLocationManager.requestLocationUpdates(name, minTime, 0, this, mLooper);
- } else if (stats.minTime != minTime) {
- stats.minTime = minTime;
- mLocationManager.requestLocationUpdates(name, minTime, 0, this, mLooper);
- }
+ if (!stats.requested) {
+ stats.requestTime = SystemClock.elapsedRealtime();
+ stats.requested = true;
+ stats.minTime = minTime;
+ mLocationManager.requestLocationUpdates(name, minTime, 0, this, mLooper);
+ } else if (stats.minTime != minTime) {
+ stats.minTime = minTime;
+ mLocationManager.requestLocationUpdates(name, minTime, 0, this, mLooper);
}
}
diff --git a/packages/Keyguard/res/values-zh-rCN/strings.xml b/packages/Keyguard/res/values-zh-rCN/strings.xml
index c5d61ac..9f9f970 100644
--- a/packages/Keyguard/res/values-zh-rCN/strings.xml
+++ b/packages/Keyguard/res/values-zh-rCN/strings.xml
@@ -34,7 +34,7 @@
<string name="keyguard_low_battery" msgid="8143808018719173859">"请连接充电器。"</string>
<string name="keyguard_instructions_when_pattern_disabled" msgid="1332288268600329841">"按“菜单”键解锁。"</string>
<string name="keyguard_network_locked_message" msgid="9169717779058037168">"网络已锁定"</string>
- <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"无SIM卡"</string>
+ <string name="keyguard_missing_sim_message_short" msgid="494980561304211931">"无 SIM 卡"</string>
<string name="keyguard_missing_sim_message" product="tablet" msgid="1445849005909260039">"平板电脑中没有SIM卡。"</string>
<string name="keyguard_missing_sim_message" product="default" msgid="3481110395508637643">"手机中没有SIM卡。"</string>
<string name="keyguard_missing_sim_instructions" msgid="5210891509995942250">"请插入SIM卡。"</string>
diff --git a/packages/PrintSpooler/res/values-ca/arrays.xml b/packages/PrintSpooler/res/values-ca/arrays.xml
new file mode 100644
index 0000000..c1b149c
--- /dev/null
+++ b/packages/PrintSpooler/res/values-ca/arrays.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.
+-->
+<resources>
+
+ <string-array name="pdf_printer_media_sizes" translatable="false">
+ <item>NA_LETTER</item>
+ <item>NA_GOVT_LETTER</item>
+ <item>NA_LEGAL</item>
+ <item>NA_JUNIOR_LEGAL</item>
+ <item>NA_LEDGER</item>
+ <item>NA_TABLOID</item>
+ <item>NA_INDEX_3X5</item>
+ <item>NA_INDEX_4X6</item>
+ <item>NA_INDEX_5X8</item>
+ <item>NA_MONARCH</item>
+ <item>NA_QUARTO</item>
+ <item>NA_FOOLSCAP</item>
+ </string-array>
+
+</resources>
diff --git a/packages/SystemUI/res/layout/notification_public_default.xml b/packages/SystemUI/res/layout/notification_public_default.xml
index efabc06..044ba09 100644
--- a/packages/SystemUI/res/layout/notification_public_default.xml
+++ b/packages/SystemUI/res/layout/notification_public_default.xml
@@ -16,12 +16,9 @@
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:internal="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/status_bar_latest_event_content"
android:layout_width="match_parent"
android:layout_height="64dp"
- internal:layout_minHeight="64dp"
- internal:layout_maxHeight="64dp"
>
<ImageView android:id="@+id/icon"
android:layout_width="40dp"
diff --git a/packages/SystemUI/res/layout/recents.xml b/packages/SystemUI/res/layout/recents.xml
index 8f367a6..26523f9 100644
--- a/packages/SystemUI/res/layout/recents.xml
+++ b/packages/SystemUI/res/layout/recents.xml
@@ -15,7 +15,7 @@
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
+ android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Status Bar Scrim View -->
<ImageView
@@ -29,9 +29,16 @@
<!-- Recents View -->
<com.android.systemui.recents.views.RecentsView
android:id="@+id/recents_view"
- android:layout_width="match_parent"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
- android:focusable="true" />
+ android:focusable="true">
+ <!-- MultiStack Debug View -->
+ <ViewStub android:id="@+id/multistack_debug_view_stub"
+ android:layout="@layout/recents_multistack_debug"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left|bottom" />
+ </com.android.systemui.recents.views.RecentsView>
<!-- Empty View -->
<ViewStub android:id="@+id/empty_view_stub"
diff --git a/packages/SystemUI/res/layout/recents_multistack_debug.xml b/packages/SystemUI/res/layout/recents_multistack_debug.xml
new file mode 100644
index 0000000..6524a54
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_multistack_debug.xml
@@ -0,0 +1,46 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left|bottom"
+ android:orientation="vertical">
+ <Button
+ android:id="@+id/add_stack"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:padding="8dp"
+ android:textSize="20sp"
+ android:textColor="#ffffffff"
+ android:text="@string/recents_multistack_add_stack"
+ android:fontFamily="sans-serif"
+ android:background="#000000"
+ android:alpha="0.5" />
+ <Button
+ android:id="@+id/resize_stack"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:padding="8dp"
+ android:textSize="20sp"
+ android:textColor="#ffffffff"
+ android:text="@string/recents_multistack_resize_stack"
+ android:fontFamily="sans-serif"
+ android:background="#000000"
+ android:alpha="0.5" />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_multistack_stack_size_dialog.xml b/packages/SystemUI/res/layout/recents_multistack_stack_size_dialog.xml
new file mode 100644
index 0000000..36e54a0
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_multistack_stack_size_dialog.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="16dp"
+ android:orientation="vertical"
+ android:descendantFocusability="beforeDescendants"
+ android:focusableInTouchMode="true">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <EditText
+ android:id="@+id/inset_left"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:hint="Left"
+ android:singleLine="true"
+ android:imeOptions="actionNext"
+ android:inputType="number"
+ android:selectAllOnFocus="true" />
+ <EditText
+ android:id="@+id/inset_top"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:hint="Top"
+ android:singleLine="true"
+ android:imeOptions="actionNext"
+ android:inputType="number"
+ android:selectAllOnFocus="true" />
+ <EditText
+ android:id="@+id/inset_right"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:hint="Right"
+ android:singleLine="true"
+ android:imeOptions="actionNext"
+ android:inputType="number"
+ android:selectAllOnFocus="true" />
+ <EditText
+ android:id="@+id/inset_bottom"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:hint="Bottom"
+ android:singleLine="true"
+ android:imeOptions="actionDone"
+ android:inputType="number"
+ android:selectAllOnFocus="true" />
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_task_view_header.xml b/packages/SystemUI/res/layout/recents_task_view_header.xml
index f1d8ad0..53047a3 100644
--- a/packages/SystemUI/res/layout/recents_task_view_header.xml
+++ b/packages/SystemUI/res/layout/recents_task_view_header.xml
@@ -43,6 +43,16 @@
android:ellipsize="marquee"
android:fadingEdge="horizontal" />
<com.android.systemui.recents.views.FixedSizeImageView
+ android:id="@+id/move_task"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_marginEnd="52dp"
+ android:layout_gravity="center_vertical|end"
+ android:padding="12dp"
+ android:background="@drawable/recents_button_bg"
+ android:src="@drawable/star"
+ android:visibility="gone" />
+ <com.android.systemui.recents.views.FixedSizeImageView
android:id="@+id/dismiss_task"
android:layout_width="48dp"
android:layout_height="48dp"
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index c18757d..e57c5f4 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> አስወግድ።"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ተሰናብቷል::"</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"ሁሉም የቅርብ ጊዜ ማመልከቻዎች ተሰናብተዋል።"</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> በመጀመር ላይ።"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"ማሳወቂያ ተወግዷል።"</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"የማሳወቂያ ጥላ።"</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ማያ ገጽ መሰካት"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"ፈልግ"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>ን መጀመር አልተቻለም።"</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"ሁሉንም ማመልከቻዎች አሰናብት"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"ባትሪ ሞልቷል"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"ኃይል በመሙላት ላይ"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> እስኪሞላ ድረስ"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index a011911..7fc379c 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Отхвърляне на <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Приложението <xliff:g id="APP">%s</xliff:g> е отхвърлено."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Всички скорошни приложения са отхвърлени."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> се стартира."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Известието е отхвърлено."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Падащ панел с известия."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"фиксиране на екрана"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"търсене"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> не можа да стартира."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Отхвърляне на всички приложения"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Заредена"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Зарежда се"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> до пълно зареждане"</string>
diff --git a/packages/SystemUI/res/values-bn-rBD/strings.xml b/packages/SystemUI/res/values-bn-rBD/strings.xml
index 8d33282..1110310 100644
--- a/packages/SystemUI/res/values-bn-rBD/strings.xml
+++ b/packages/SystemUI/res/values-bn-rBD/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> খারিজ করুন।"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> খারিজ করা হয়েছে৷"</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"সমস্ত সাম্প্রতিক অ্যাপ্লিকেশন খারিজ করা হয়েছে।"</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> তারাঙ্কিত করা হচ্ছে।"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"বিজ্ঞপ্তি খারিজ করা হয়েছে৷"</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"বিজ্ঞপ্তি শেড৷"</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"স্ক্রীন পিন করা"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"অনুসন্ধান"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> শুরু করা যায়নি৷"</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"সমস্ত অ্যাপ্লিকেশন খারিজ করুন"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"চার্জ হয়েছে"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"চার্জ হচ্ছে"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"পূর্ণ হতে <xliff:g id="CHARGING_TIME">%s</xliff:g> সময় লাগবে"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index e33c49d..ef5986b 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -161,8 +161,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Descarta <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"S\'ha omès <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"S\'han descartat totes les aplicacions recents."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"S\'està iniciant <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificació omesa."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Àrea de notificacions"</string>
@@ -284,8 +283,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fixació de pantalla"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"cerca"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"No s\'ha pogut iniciar <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Descarta totes les aplicacions"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carregada"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"S\'està carregant"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> per completar la càrrega"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index c8907c7..6235eb1 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -161,8 +161,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Zavřít aplikaci <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Aplikace <xliff:g id="APP">%s</xliff:g> byla odebrána."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Všechny naposledy použité aplikace byly odstraněny."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Spouštění aplikace <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Oznámení je zavřeno."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Panel oznámení."</string>
@@ -284,8 +283,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"připnutí obrazovky"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"vyhledat"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Aplikaci <xliff:g id="APP">%s</xliff:g> nelze spustit."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Odstranit všechny aplikace"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Nabito"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Nabíjení"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> do plného nabití"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index f442472..3153d3f 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Afvis <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> er annulleret."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Alle de seneste applikationer er lukket."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> startes."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Underretningen er annulleret."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Underretningspanel."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"bliv i app"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"søg"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> kunne ikke startes."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Luk alle applikationer"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Opladet"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Oplader"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> indtil fuld opladet"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index f3349d2..3165a3c 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -161,8 +161,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> beenden"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> entfernt"</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Alle kürzlich verwendeten Apps wurden entfernt."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> wird gestartet."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Benachrichtigung geschlossen"</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Benachrichtigungsleiste"</string>
@@ -284,8 +283,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"Bildschirmfixierung"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"Suche"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> konnte nicht gestartet werden."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Alle Apps entfernen"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Aufgeladen"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Wird aufgeladen"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Voll in <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 04c4fe8..e6ea177 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -161,8 +161,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Παράβλεψη <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Απορρίφθηκαν <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Έγινε παράβλεψη όλων των πρόσφατων εφαρμογών."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Έναρξη <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Η ειδοποίηση έχει απορριφθεί."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Πλαίσιο σκίασης ειδοποιήσεων."</string>
@@ -284,8 +283,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"καρφίτσωμα οθόνης"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"αναζήτηση"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Δεν ήταν δυνατή η εκκίνηση της εφαρμογής <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Παράβλεψη όλων των εφαρμογών"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Φορτίστηκε"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Φόρτιση"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> για πλήρη φόρτιση"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 7cf20ba..c788677 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -161,8 +161,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Rechazar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> descartada."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Se descartaron todas las aplicaciones recientes."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Iniciando <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificación ignorada"</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Pantalla de notificaciones"</string>
@@ -284,8 +283,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"Fijar pantalla"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"buscar"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"No se pudo iniciar <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Descartar todas las aplicaciones"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Cargada"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Cargando"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> para completarse"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 82fe2bd..3f2fa15 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Ignorar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Se ha eliminado <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Se han ignorado todas las aplicaciones recientes."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Iniciando <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificación ignorada"</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Pantalla de notificaciones"</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fijación de pantalla"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"buscar"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"No se ha podido iniciar <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Ignorar todas las aplicaciones"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Cargada"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Cargando"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> para completarse"</string>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index f8ce386..9c05158 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Rakendusest <xliff:g id="APP">%s</xliff:g> loobumine."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Loobusite rakendusest <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Kõikidest hiljutistest rakendustest on loobutud"</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Rakenduse <xliff:g id="APP">%s</xliff:g> käivitamine."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Märguandest on loobutud."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Märguande vari."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ekraanikuva kinnitamine"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"otsing"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Rakendust <xliff:g id="APP">%s</xliff:g> ei saanud käivitada."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Loobu kõikidest rakendustest"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Laetud"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Laadimine"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Täislaadimiseks kulub <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml
index b456bee..e2a81e9 100644
--- a/packages/SystemUI/res/values-eu-rES/strings.xml
+++ b/packages/SystemUI/res/values-eu-rES/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Baztertu <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> baztertu da."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Azken aplikazioen atala garbitu da."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> hasten."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Jakinarazpena baztertu da."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Jakinarazpenen panela."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"pantaila-ainguratzea"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"bilatu"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Ezin izan da hasi <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Garbitu aplikazio guztiak"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Kargatuta"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Kargatzen"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> falta zaizkio guztiz kargatzeko"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index b8b2d65..3208b1b 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Hylätään <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> hylättiin."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Kaikki viimeisimmät sovellukset on hylätty."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Käynnistetään <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Ilmoitus hylätty."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Ilmoitusalue."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"näytön kiinnitys"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"haku"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Sovelluksen <xliff:g id="APP">%s</xliff:g> käynnistäminen epäonnistui."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Hylkää kaikki sovellukset"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Ladattu"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Ladataan"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> kunnes täynnä"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 156e4a7..e6bd1f6 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -161,8 +161,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Supprimer <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Application \"<xliff:g id="APP">%s</xliff:g>\" ignorée."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Toutes les applications récentes ont été supprimées."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Lancement de <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notification masquée"</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Volet des notifications"</string>
@@ -284,8 +283,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"épinglage d\'écran"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"rechercher"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Impossible de lancer <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Supprimer toutes les applications"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Chargée"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Charge en cours..."</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Chargée dans <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 31ef4ef..1d91c8a 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -161,8 +161,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Supprimer <xliff:g id="APP">%s</xliff:g>"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Application \"<xliff:g id="APP">%s</xliff:g>\" ignorée."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Toutes les applications récentes ont été supprimées."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Lancement de <xliff:g id="APP">%s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notification masquée"</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Volet des notifications"</string>
@@ -284,8 +283,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"épinglage d\'écran"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"rechercher"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Impossible de lancer <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Supprimer toutes les applications"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Chargé"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"En charge"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Chargé dans <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml
index 4c40a5a..78a5fbb 100644
--- a/packages/SystemUI/res/values-gl-rES/strings.xml
+++ b/packages/SystemUI/res/values-gl-rES/strings.xml
@@ -161,8 +161,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Rexeitar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Rexeitouse <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Rexeitáronse todas as aplicacións recentes."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Iniciando <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificación rexeitada"</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Sombra de notificación"</string>
@@ -284,8 +283,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fixación de pantalla"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"buscar"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Non foi posible iniciar <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Rexeitar todas as aplicacións"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Cargada"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Cargando"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> para completar a carga"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 50ef47a..411e3c3 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> को ख़ारिज करें."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> खा़रिज कर दिया गया."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"हाल ही के सभी ऐप्लिकेशन ख़ारिज कर दिए गए."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> प्रारंभ हो रहा है."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"नोटिफिकेशन खारिज की गई."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"नोटिफिकेशन शेड."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"स्क्रीन पिन करना"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"खोज"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> प्रारंभ नहीं किया जा सका."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"सभी ऐप्लिकेशन ख़ारिज करें"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"चार्ज हो गई है"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"चार्ज हो रही है"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"पूर्ण होने में <xliff:g id="CHARGING_TIME">%s</xliff:g> शेष"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 3fb20a9..1e42832 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Odbacivanje aplikacije <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Aplikacija <xliff:g id="APP">%s</xliff:g> odbačena je."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Odbačene su sve nedavne aplikacije."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Pokretanje aplikacije <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Obavijest je odbačena."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Zaslon obavijesti."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"prikvačivanje zaslona"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"pretraži"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Aplikacija <xliff:g id="APP">%s</xliff:g> nije pokrenuta."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Odbaci sve aplikacije"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Napunjeno"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Punjenje"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> do napunjenosti"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 8787599..ed105d0 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"A(z) <xliff:g id="APP">%s</xliff:g> elvetése."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> eltávolítva."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Az összes alkalmazás eltávolítva a nemrég használtak közül."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"A(z) <xliff:g id="APP">%s</xliff:g> indítása."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Értesítés elvetve."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Értesítési felület."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"képernyő rögzítése"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"keresés"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Nem lehet elindítani a következőt: <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Összes alkalmazás elvetése"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Feltöltve"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Töltés"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> a teljes töltöttségig"</string>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index c0f8776..6432060 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Անտեսել <xliff:g id="APP">%s</xliff:g>-ը:"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g>-ը անտեսված է:"</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Բոլոր վերջին հավելվածները հեռացվել են ցուցակից:"</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Մեկնարկել <xliff:g id="APP">%s</xliff:g>-ը:"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Ծանուցումը անտեսվեց:"</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Ծանուցումների վահանակ:"</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"էկրանի ամրակցում"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"որոնել"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Հնարավոր չէ գործարկել <xliff:g id="APP">%s</xliff:g>-ը:"</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Հեռացնել բոլոր հավելվածները"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Լիցքավորված է"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Լիցքավորվում է"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Լրիվ լիցքավորմանը մնաց <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 025bdbe..bd0a99f 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Menyingkirkan <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> disingkirkan."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Semua aplikasi terbaru ditutup."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Memulai <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Pemberitahuan disingkirkan."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Bayangan pemberitahuan."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"pin ke layar"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"telusuri"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Tidak dapat memulai <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Tutup semua aplikasi"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Terisi"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Mengisi daya"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> sampai penuh"</string>
diff --git a/packages/SystemUI/res/values-is-rIS/strings.xml b/packages/SystemUI/res/values-is-rIS/strings.xml
index 87fb619..adcda3f 100644
--- a/packages/SystemUI/res/values-is-rIS/strings.xml
+++ b/packages/SystemUI/res/values-is-rIS/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Hunsa <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> vísað frá."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Öll nýleg forrit fjarlægð."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Ræsir <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Tilkynningu lokað."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Tilkynningasvæði."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"skjáfesting"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"leita"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Ekki var hægt að ræsa <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Fjarlægja öll forrit"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Fullhlaðin"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Í hleðslu"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> þar til fullri hleðslu er náð"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 8144bc0..21a483f 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -161,8 +161,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Elimina <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> eliminata."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Tutte le applicazioni recenti sono state rimosse."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Avvio di <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notifica eliminata."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Area notifiche."</string>
@@ -284,8 +283,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"blocco su schermo"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"cerca"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Impossibile avviare <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Rimuovi tutte le applicazioni"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carica"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"In carica"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> al termine della carica"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 0183e21..5b68f86 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -161,8 +161,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>を削除します。"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g>は削除されました。"</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"最近のアプリケーションをすべて消去しました。"</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>を開始しています。"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"通知が削除されました。"</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"通知シェード"</string>
@@ -284,8 +283,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"画面固定"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"検索"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>を開始できません。"</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"すべてのアプリケーションを消去"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"充電が完了しました"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"充電しています"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"充電完了まで<xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index 42fdfe9..b6650d0 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>-ის უგულებელყოფა."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ამოშლილია სიიდან."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"ყველა ბოლო აპლიკაცია გაუქმდა."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> იწყება."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"შეტყობინება წაიშალა."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"შეტყობინებების ფარდა"</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ეკრანზე ჩამაგრება"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"ძიება"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>-ის გამოძახება ვერ მოხერხდა."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"ყველა აპლიკაციის გაუქმება"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"დატენილია"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"მიმდინარეობს დატენვა"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> სრულად დატენვამდე"</string>
diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml
index 7190aa2..15c4584 100644
--- a/packages/SystemUI/res/values-kk-rKZ/strings.xml
+++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> қолданбасынан бас тарту."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> алынып тасталған."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Барлық жақындағы қабылданбаған қолданбалар."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> іске қосылуда."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Хабар алынып тасталды."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Хабарландыру тақтасы"</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"экранды бекіту"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"іздеу"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> іске қосу мүмкін болмады."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Барлық қолданбаларды қабылдамау"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Зарядталды"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Зарядталуда"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Толғанға дейін <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index e88deb7..28d586a 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"បោះបង់ <xliff:g id="APP">%s</xliff:g> ។"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> បដិសេធ។"</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"កម្មវិធីថ្មីៗទាំងអស់ត្រូវបានបោះបង់។"</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"ចាប់ផ្ដើម <xliff:g id="APP">%s</xliff:g> ។"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"បានបដិសេធការជូនដំណឹង"</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"ពណ៌ការជូនដំណឹង"</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ការភ្ជាប់អេក្រង់"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"ស្វែងរក"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"មិនអាចចាប់ផ្ដើម <xliff:g id="APP">%s</xliff:g> ទេ។"</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"បោះបង់កម្មវិធីទាំងអស់"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"បានបញ្ចូលថ្ម"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"កំពុងបញ្ចូលថ្ម"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> រហូតដល់ពេញ"</string>
diff --git a/packages/SystemUI/res/values-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml
index 5b7738b..4134c11 100644
--- a/packages/SystemUI/res/values-kn-rIN/strings.xml
+++ b/packages/SystemUI/res/values-kn-rIN/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> ವಜಾಗೊಳಿಸು."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"ಇತ್ತೀಚಿನ ಎಲ್ಲಾ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"ಅಧಿಸೂಚನೆ ವಜಾಗೊಂಡಿದೆ."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"ಅಧಿಸೂಚನೆಯ ಛಾಯೆ."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ಸ್ಕ್ರೀನ್ ಪಿನ್ನಿಂಗ್"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"ಹುಡುಕಾಟ"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> ಪ್ರಾರಂಭಿಸಲು ಸಾದ್ಯವಿಲ್ಲ."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"ಎಲ್ಲಾ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ವಜಾಗೊಳಿಸಿ"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"ಚಾರ್ಜ್ ಆಗಿದೆ"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> ಪೂರ್ಣಗೊಳ್ಳುವವರೆಗೆ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 906ba2b..549da84 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>을(를) 숨깁니다."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g>이(가) 제거되었습니다."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"최근 사용한 애플리케이션을 모두 닫았습니다."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>을(를) 시작하는 중입니다."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"알림이 제거되었습니다."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"알림 세부정보"</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"화면 고정"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"검색"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>을(를) 시작할 수 없습니다."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"모든 애플리케이션 닫기"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"충전됨"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"충전 중"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"완충까지 <xliff:g id="CHARGING_TIME">%s</xliff:g> 남음"</string>
diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml
index 30e5c97..960541a 100644
--- a/packages/SystemUI/res/values-ky-rKG/strings.xml
+++ b/packages/SystemUI/res/values-ky-rKG/strings.xml
@@ -184,8 +184,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> этибарга албоо."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> жок болду."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Акыркы колдонмолордун баары көз жаздымда калтырылды."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> иштеп баштоодо."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Эскертме жок кылынды."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Эскертмелер көшөгөсү."</string>
@@ -307,8 +306,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"экран кадоо"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"издөө"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> баштай алган жок."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Бардык колдонмолорду көз жаздымда калтыруу"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Кубатталды"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Кубатталууда"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> толгонго чейин"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 49f38b9..f7a8c00 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Atsisakyti <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Atsisakyta programos „<xliff:g id="APP">%s</xliff:g>“."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Atsisakyta visų naujausių programų."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Paleidžiama <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Pranešimo atsisakyta."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Pranešimų gaubtas."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ekrano prisegimas"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"paieška"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Nepavyko paleisti <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Atsisakyti visų programų"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Įkrautas"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Kraunamas"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> iki visiško įkrovimo"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index a0f7b20..a9f7110 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Nerādīt lietotni <xliff:g id="APP">%s</xliff:g>"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Lietotne <xliff:g id="APP">%s</xliff:g> vairs netiek rādīta."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Visas nesen izmantotās lietojumprogrammas tika noņemtas."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Notiek lietotnes <xliff:g id="APP">%s</xliff:g> palaišana."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Paziņojums netiek rādīts."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Paziņojumu panelis"</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"Piespraust ekrānu"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"Meklēt"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Nevarēja palaist lietotni <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Noņemt visas lietojumprogrammas"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Akumulators uzlādēts"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Notiek uzlāde"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> līdz pilnam akumulatoram"</string>
diff --git a/packages/SystemUI/res/values-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml
index 67314c0..4686ba5 100644
--- a/packages/SystemUI/res/values-mk-rMK/strings.xml
+++ b/packages/SystemUI/res/values-mk-rMK/strings.xml
@@ -161,8 +161,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Отфрли <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> е отфрлена."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Сите неодамнешни апликации се отфрлени."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Се стартува <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Известувањето е отфрлено."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Панел за известување"</string>
@@ -284,8 +283,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"прикачување екран"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"пребарај"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> не може да се вклучи."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Отфрли ги сите апликации"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Наполнета"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Се полни"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> додека не се наполни"</string>
diff --git a/packages/SystemUI/res/values-ml-rIN/strings.xml b/packages/SystemUI/res/values-ml-rIN/strings.xml
index 41e5ef8..41f5923 100644
--- a/packages/SystemUI/res/values-ml-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ml-rIN/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> നിരസിക്കുക."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> നിരസിച്ചു."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"അടുത്തിടെയുള്ള എല്ലാ അപ്ലിക്കേഷനും നിരസിച്ചു."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> ആരംഭിക്കുന്നു."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"അറിയിപ്പ് നിരസിച്ചു."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"അറിയിപ്പ് ഷെയ്ഡ്."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"സ്ക്രീൻ പിൻ ചെയ്യൽ"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"തിരയുക"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> ആരംഭിക്കാനായില്ല."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"എല്ലാ അപ്ലിക്കേഷനുകളും നിരസിക്കുക"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"ചാർജ്ജുചെയ്തു"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"ചാർജ്ജുചെയ്യുന്നു"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"പൂർണ്ണമായും ചാർജ്ജാകുന്നതിന്, <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index ea80976..8f81367 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>-г хаах."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> байхгүй."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Хамгийн сүүлийн бүх програмыг арилгасан байна."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>-г эхлүүлж байна."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Мэдэгдэл хаагдсан."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Мэдэгдлийн хураангуй самбар"</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"дэлгэц тогтоох"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"хайх"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>-г эхлүүлж чадсангүй."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Бүх програмыг арилгах"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Цэнэглэгдсэн"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Цэнэглэж байна"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"дүүргэхэд <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-mr-rIN/strings.xml b/packages/SystemUI/res/values-mr-rIN/strings.xml
index e8f3a98..bf7c5a6 100644
--- a/packages/SystemUI/res/values-mr-rIN/strings.xml
+++ b/packages/SystemUI/res/values-mr-rIN/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> डिसमिस करा."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> डिसमिस केला."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"अलीकडील सर्व अनुप्रयोग डिसमिस झाले."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> प्रारंभ करीत आहे."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"सूचना डिसमिस केल्या."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"सूचना शेड."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"स्क्रीन पिन करणे"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"शोधा"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> प्रारंभ करणे शक्य झाले नाही."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"सर्व अनुप्रयोग डिसमिस करा"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"चार्ज झाली"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"चार्ज होत आहे"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> पूर्ण होईपर्यंत"</string>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 7b89c1c..5845d59 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Ketepikan <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ditolak."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Semua aplikasi terbaharu diketepikan."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Memulakan <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Pemberitahuan diketepikan."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Bidai pemberitahuan."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"penyematan skrin"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"cari"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Tidak dapat memulakan <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Ketepikan semua aplikasi"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Sudah dicas"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Mengecas"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Lagi <xliff:g id="CHARGING_TIME">%s</xliff:g> untuk penuh"</string>
diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml
index 86e4729..c8f4c55 100644
--- a/packages/SystemUI/res/values-my-rMM/strings.xml
+++ b/packages/SystemUI/res/values-my-rMM/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>ကို ပယ်လိုက်ရန်"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ထုတ်ထားသည်။"</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"မကြာသေးမီက အပလီကေးရှင်းများအားလုံး ဖယ်ထုတ်ပြီးပါပြီ။"</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>ကို စတင်နေသည်။"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"အကြောင်းကြားချက်ကိုဖယ်ရှားပြီး"</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"အကြောင်းကြားစာအကွက်"</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"မျက်နှာပြင် ပင်ထိုးမှု"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"ရှာဖွေရန်"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> ကို မစနိုင်ပါ။"</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"အပလီကေးရှင်းများအားလုံး ဖယ်ထုတ်မည်"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"အားသွင်းပြီး"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"အားသွင်းနေ"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> ပြည်သည့် အထိ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 7ac8a69..60e27ee 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Avvis <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> avvist."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Alle nylig brukte apper er avvist."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Starter <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Varselet ble skjult."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Varselskygge."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"én-appsmodus"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"Søk"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Kunne ikke starte <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Avvis alle apper"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Oppladet"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Lader"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Fulladet om <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml
index d1d19af..87cbb6f 100644
--- a/packages/SystemUI/res/values-ne-rNP/strings.xml
+++ b/packages/SystemUI/res/values-ne-rNP/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> खारेज गर्नुहोस्।"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> खारेज गरिएको छ।"</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"सबै हालका अनुप्रयोगहरू खारेज गरियो।"</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>सुरु गर्दै।"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"सूचना खारेज।"</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"सूचना कक्ष।"</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"स्क्रिन पिन गर्दै"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"खोजी गर्नुहोस्"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"सुरु गर्न सकिएन <xliff:g id="APP">%s</xliff:g>।"</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"सबै अनुप्रयोगहरू खारेज गर्नुहोस्"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"चार्ज भयो"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"चार्ज हुँदै"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> पूर्ण नभएसम्म"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 451ff9a..6786a74 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> sluiten."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> verwijderd."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Alle recente apps gesloten."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> starten."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Melding verwijderd."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Meldingenpaneel."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"scherm vastzetten"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"zoeken"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Kan <xliff:g id="APP">%s</xliff:g> niet starten."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Alle apps sluiten"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Opgeladen"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Opladen"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> tot volledig opgeladen"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 81e22ca..3f2c7fd 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Usuń stąd <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g>: zamknięto."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Wszystkie ostatnie aplikacje zostały zamknięte."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Uruchamiam <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Zamknięto powiadomienie."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Obszar powiadomień."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"przypinanie ekranu"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"szukaj"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Nie udało się uruchomić aplikacji <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Zamknij wszystkie aplikacje"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Naładowana"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Ładowanie"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> do pełnego naładowania"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index ae7b551..b5b2650 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Ignorar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ignorado."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Todas as aplicações recentes foram ignoradas."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"A iniciar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificação ignorada."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Painel de notificações."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fixação no ecrã"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"pesquisar"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Não foi possível iniciar o <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Ignorar todas as aplicações"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carregada"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"A carregar"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> até ficar completa"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 7887e06..c914a50 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -161,8 +161,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Descartar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> descartado."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Todos os apps recentes foram dispensados."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Iniciando <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificação dispensada."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Aba de notificações."</string>
@@ -284,8 +283,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fixação de tela"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"pesquisar"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Não foi possível iniciar <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Dispensar todos os apps"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Carregada"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Carregando"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> até concluir"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 0c1b4ec..c84b96f 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Închideți <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> a fost eliminată."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Toate aplicațiile recente au fost închise."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Se inițiază <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Notificarea a fost închisă."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Fereastră pentru notificări."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fixare pe ecran"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"căutare"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> nu a putut porni."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Închideți toate aplicațiile"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"S-a încărcat"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Se încarcă"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> până la încărcare completă"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 8425cb4..cd00556 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -19,7 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_label" msgid="7164937344850004466">"Графический интерфейс системы"</string>
+ <string name="app_label" msgid="7164937344850004466">"Интерфейс системы"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Очистить"</string>
<string name="status_bar_recent_remove_item_title" msgid="6026395868129852968">"Удалить из списка"</string>
<string name="status_bar_recent_inspect_item_title" msgid="7793624864528818569">"О приложении"</string>
@@ -161,8 +161,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Удаление приложения <xliff:g id="APP">%s</xliff:g> из списка."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" удалено из списка."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Все недавние приложения закрыты."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Запуск приложения <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Уведомление закрыто"</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Панель уведомлений"</string>
@@ -284,8 +283,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"Заблокировать в приложении"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"поиск"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Не удалось запустить приложение \"<xliff:g id="APP">%s</xliff:g>\""</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Закрыть все приложения"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Батарея заряжена"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Зарядка батареи"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> до полной зарядки"</string>
diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml
index 760e49d..0a54324 100644
--- a/packages/SystemUI/res/values-si-rLK/strings.xml
+++ b/packages/SystemUI/res/values-si-rLK/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> ඉවතලන්න."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> අස් කර ඇත."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"සියලු මෑත යෙඳුම් අස් කෙරිණි"</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> ආරම්භ කරමින්."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"දැනුම්දීම නිෂ්ප්රභා කරඇත."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"දැනුම්දීම් ආවරණය."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"තිර ඇමිණීම"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"සෙවීම"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> ආරම්භ කළ නොහැක."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"සියලු යෙදුම් අස් කරන්න"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"අරෝපිතයි"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"ආරෝපණය වෙමින්"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> සම්පූර්ණ වන තෙක්"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index b20d8f9..9fbd73e4 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -161,8 +161,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Zrušiť aplikáciu <xliff:g id="APP">%s</xliff:g>"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Aplikácia <xliff:g id="APP">%s</xliff:g> bola zrušená."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Všetky nedávne aplikácie boli odmietnuté."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Spúšťa sa aplikácia <xliff:g id="APP">%s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Upozornenie bolo zrušené."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Panel upozornení."</string>
@@ -284,8 +283,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"pripnutie k obrazovke"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"hľadať"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Aplikáciu <xliff:g id="APP">%s</xliff:g> sa nepodarilo spustiť"</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Odmietnuť všetky aplikácie"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Nabitá"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Nabíja sa"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Úplné nabitie o <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 1981bfd..e4a2016 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Opusti aplikacijo <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Aplikacija <xliff:g id="APP">%s</xliff:g> je bila odstranjena."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Vse nedavne aplikacije so bile opuščene."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Zaganjanje aplikacije <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Obvestilo je bilo odstranjeno."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Zaslon z obvestili."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"pripenjanje zaslona"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"iskanje"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Aplikacije <xliff:g id="APP">%s</xliff:g> ni bilo mogoče zagnati."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Opusti vse aplikacije"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Akumulator napolnjen"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Polnjenje"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> do napolnjenosti"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index fc4dd64..f3d7da0 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Одбаците <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Апликација <xliff:g id="APP">%s</xliff:g> је одбачена."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Све недавно коришћене апликације су одбачене."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Покрећемо <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Обавештење је одбачено."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Прозор са обавештењима."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"качење екрана"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"претражи"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Покретање апликације <xliff:g id="APP">%s</xliff:g> није успело."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Одбаци све апликације"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Напуњена је"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Пуњење"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> док се не напуни"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 6cc5ad0..0e9879a 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Ta bort <xliff:g id="APP">%s</xliff:g> från listan."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> togs bort permanent."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Alla appar har tagits bort från listan Senaste."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Startar <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Meddelandet ignorerades."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Meddelandepanel."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fästa skärmen"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"sök"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Det gick inte att starta appen <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Ta bort alla appar"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Laddat"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Laddar"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> tills batteriet är fulladdat"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 3b539b9..05cd74a 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Ondoa <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> imeondolewa."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Programu za hivi majuzi zimeondolewa."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Inaanzisha <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Arifa imetupwa."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Kivuli cha arifa."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"kudumisha programu moja"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"tafuta"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Haikuweza kuanzisha <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Ondoa programu zote"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Betri imejaa"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Inachaji"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Imebakisha <xliff:g id="CHARGING_TIME">%s</xliff:g> ijae"</string>
diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml
index bf163f6..7556c7b 100644
--- a/packages/SystemUI/res/values-ta-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ta-rIN/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> ஐ நிராகரி."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> விலக்கப்பட்டது."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"எல்லா சமீபத்திய பயன்பாடுகளும் விலக்கப்பட்டன."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> ஐத் தொடங்குகிறது."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"அறிவிப்பு நிராகரிக்கப்பட்டது."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"அறிவிப்பு விவரம்."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"திரையை பின் செய்தல்"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"தேடு"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>ஐத் தொடங்க முடியவில்லை."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"எல்லா பயன்பாடுகளையும் விலக்கு"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"சார்ஜ் செய்யப்பட்டது"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"சார்ஜாகிறது"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"முழுவதும் சார்ஜாக <xliff:g id="CHARGING_TIME">%s</xliff:g> ஆகும்"</string>
diff --git a/packages/SystemUI/res/values-te-rIN/strings.xml b/packages/SystemUI/res/values-te-rIN/strings.xml
index 04616c9..7704e85 100644
--- a/packages/SystemUI/res/values-te-rIN/strings.xml
+++ b/packages/SystemUI/res/values-te-rIN/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>ని తీసివేయండి."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> తీసివేయబడింది."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"అన్ని ఇటీవలి అనువర్తనాలు తీసివేయబడ్డాయి."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g>ని ప్రారంభిస్తోంది."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"నోటిఫికేషన్ తీసివేయబడింది."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"నోటిఫికేషన్ షేడ్."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"స్క్రీన్ పిన్నింగ్"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"శోధించు"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g>ని ప్రారంభించడం సాధ్యపడలేదు."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"అన్ని అనువర్తనాలను తీసివేయి"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"ఛార్జ్ చేయబడింది"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"ఛార్జ్ అవుతోంది"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"పూర్తిగా నిండటానికి <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 5b51a87..d591d0a 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"ยกเลิก <xliff:g id="APP">%s</xliff:g>"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ถูกลบไปแล้ว"</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"ปิดแอปพลิเคชันล่าสุดทั้งหมดแล้ว"</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"กำลังเริ่มต้น <xliff:g id="APP">%s</xliff:g>"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"ปิดการแจ้งเตือนแล้ว"</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"หน้าต่างแจ้งเตือน"</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"การตรึงหน้าจอ"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"ค้นหา"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"ไม่สามารถเริ่มใช้ <xliff:g id="APP">%s</xliff:g>"</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"ปิดแอปพลิเคชันทั้งหมด"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"ชาร์จแล้ว"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"กำลังชาร์จ"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"อีก <xliff:g id="CHARGING_TIME">%s</xliff:g> จึงจะเต็ม"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 2367762..abe7b53 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"I-dismiss ang <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Hindi pinansin ang <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Na-dismiss ang lahat ng kamakailang application."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Sinisimulan ang <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Na-dismiss ang notification."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Notification shade."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"pagpi-pin sa screen"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"maghanap"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Hindi masimulan <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"I-dismiss ang lahat ng application"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Nasingil na"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Nagcha-charge"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> hanggang mapuno"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 2b7f059..9bcdc27 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> uygulamasını kapat."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> kaldırıldı."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Tüm son uygulamalar kapatıldı."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> başlatılıyor."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Bildirim kapatıldı."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Bildirim gölgesi."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ekran sabitleme"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"ara"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> başlatılamadı."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Tüm uygulamaları kapat"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Ödeme alındı"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Şarj oluyor"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"Tam şarj olmasına <xliff:g id="CHARGING_TIME">%s</xliff:g> kaldı"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 1f086ff..68f7daa 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Видалити додаток <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"Програму <xliff:g id="APP">%s</xliff:g> закрито."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Усі останні додатки закрито."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Запуск додатка <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Сповіщення відхилено."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Панель сповіщень."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"закріпити екран"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"пошук"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Не вдалося запустити <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Закрити всі додатки"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Заряджено"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Заряджається"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"До повного зарядження <xliff:g id="CHARGING_TIME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ur-rPK/strings.xml b/packages/SystemUI/res/values-ur-rPK/strings.xml
index cb6f3f0..bc3dbc2 100644
--- a/packages/SystemUI/res/values-ur-rPK/strings.xml
+++ b/packages/SystemUI/res/values-ur-rPK/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> کو مسترد کریں۔"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> کو ہٹا دیا گیا۔"</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"سبھی حالیہ ایپلیکیشنز کو برخاست کر دیا گیا۔"</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> شروع ہو رہی ہے۔"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"اطلاع مسترد ہوگئی۔"</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"اطلاعاتی شیڈ۔"</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"اسکرین کو پن کرنا"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"تلاش کریں"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> کو شروع نہیں کیا جا سکا۔"</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"سبھی ایپلیکیشنز کو برخاست کریں"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"چارج ہوگئی"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"چارج ہو رہی ہے"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> مکمل ہونے تک"</string>
diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml
index 58bf477..86ad17e 100644
--- a/packages/SystemUI/res/values-uz-rUZ/strings.xml
+++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Olib tashlash: <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> olib tashlangan."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Yaqinda ishlatilgan barcha ilovalar olib tashlandi."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> ishga tushirilmoqda."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Xabarnoma e‘tiborsiz qoldirildi."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Xabarnoma soyasi."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ekranni qadab qo‘yish"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"qidirish"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"“<xliff:g id="APP">%s</xliff:g>” ilovasini ishga tushirib bo‘lmadi."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Barcha ilovalarni olib tashlash"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Batareya quvvati to‘ldi"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Quvvat olmoqda"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g>da to‘ladi"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index c34d0ed..506c60a 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -159,8 +159,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"Xóa bỏ <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> đã bị loại bỏ."</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"Đã loại bỏ tất cả các ứng dụng gần đây."</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"Bắt đầu <xliff:g id="APP">%s</xliff:g>."</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"Đã loại bỏ thông báo."</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"Bóng thông báo."</string>
@@ -282,8 +281,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"khóa màn hình"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"tìm kiếm"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"Không thể khởi động <xliff:g id="APP">%s</xliff:g>."</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"Loại bỏ tất cả các ứng dụng"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"Đã sạc"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"Đang sạc"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> cho đến khi đầy"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 49fe785..4b39f65 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -143,7 +143,7 @@
<string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"漫游中"</string>
<string name="accessibility_data_connection_edge" msgid="4477457051631979278">"EDGE"</string>
<string name="accessibility_data_connection_wifi" msgid="2324496756590645221">"WLAN"</string>
- <string name="accessibility_no_sim" msgid="8274017118472455155">"无SIM卡。"</string>
+ <string name="accessibility_no_sim" msgid="8274017118472455155">"无 SIM 卡。"</string>
<string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"蓝牙网络共享。"</string>
<string name="accessibility_airplane_mode" msgid="834748999790763092">"飞行模式。"</string>
<!-- String.format failed for translation -->
@@ -161,8 +161,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"移除<xliff:g id="APP">%s</xliff:g>。"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"已删除<xliff:g id="APP">%s</xliff:g>"</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"已关闭所有最近用过的应用。"</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"正在启动<xliff:g id="APP">%s</xliff:g>。"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"已关闭通知。"</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"通知栏。"</string>
@@ -284,8 +283,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"固定屏幕"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"搜索"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"无法启动<xliff:g id="APP">%s</xliff:g>。"</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"关闭所有应用"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"充电完成"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"正在充电"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"还需<xliff:g id="CHARGING_TIME">%s</xliff:g>充满"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 22e0ed1..50d8489 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -161,8 +161,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"關閉「<xliff:g id="APP">%s</xliff:g>」。"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"「<xliff:g id="APP">%s</xliff:g>」已關閉。"</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"所有最近使用的應用程式已關閉。"</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"正在啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"通知已關閉。"</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"通知欄。"</string>
@@ -284,8 +283,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"螢幕固定"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"搜尋"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"無法啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"關閉所有應用程式"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"已完成充電"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"充電中"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g>後完成充電"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 674b974..8a50cf9 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -161,8 +161,7 @@
<skip />
<string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"關閉「<xliff:g id="APP">%s</xliff:g>」。"</string>
<string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"「<xliff:g id="APP">%s</xliff:g>」已關閉。"</string>
- <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
- <skip />
+ <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"最近使用的使用程式已全部關閉。"</string>
<string name="accessibility_recents_item_launched" msgid="7616039892382525203">"正在啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
<string name="accessibility_notification_dismissed" msgid="854211387186306927">"已關閉通知。"</string>
<string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"通知欄。"</string>
@@ -284,8 +283,7 @@
<string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"螢幕固定"</string>
<string name="recents_search_bar_label" msgid="8074997400187836677">"搜尋"</string>
<string name="recents_launch_error_message" msgid="2969287838120550506">"無法啟動「<xliff:g id="APP">%s</xliff:g>」。"</string>
- <!-- no translation found for recents_dismiss_all_message (8495275386693095768) -->
- <skip />
+ <string name="recents_dismiss_all_message" msgid="8495275386693095768">"關閉所有應用程式"</string>
<string name="expanded_header_battery_charged" msgid="5945855970267657951">"已充飽"</string>
<string name="expanded_header_battery_charging" msgid="205623198487189724">"充電中"</string>
<string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g>後充飽"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 071b9a3..2659009 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -250,7 +250,7 @@
<integer name="doze_pickup_vibration_threshold">2000</integer>
<!-- Doze: can we assume the pickup sensor includes a proximity check? -->
- <bool name="doze_pickup_performs_proximity_check">true</bool>
+ <bool name="doze_pickup_performs_proximity_check">false</bool>
<!-- Doze: pulse parameter - how long does it take to fade in? -->
<integer name="doze_pulse_duration_in">900</integer>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c977db9..40bf13f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -672,6 +672,19 @@
<!-- Recents: Dismiss all button. [CHAR LIMIT=NONE] -->
<string name="recents_dismiss_all_message">Dismiss all applications</string>
+ <!-- Recents: MultiStack add stack button. [CHAR LIMIT=NONE] -->
+ <string name="recents_multistack_add_stack">+</string>
+ <!-- Recents: MultiStack remove stack button. [CHAR LIMIT=NONE] -->
+ <string name="recents_multistack_remove_stack">-</string>
+ <!-- Recents: MultiStack resize stack button. [CHAR LIMIT=NONE] -->
+ <string name="recents_multistack_resize_stack">[]</string>
+ <!-- Recents: MultiStack add stack split horizontal radio button. [CHAR LIMIT=NONE] -->
+ <string name="recents_multistack_add_stack_dialog_split_horizontal">Split Horizontal</string>
+ <!-- Recents: MultiStack add stack split vertical radio button. [CHAR LIMIT=NONE] -->
+ <string name="recents_multistack_add_stack_dialog_split_vertical">Split Vertical</string>
+ <!-- Recents: MultiStack add stack split custom radio button. [CHAR LIMIT=NONE] -->
+ <string name="recents_multistack_add_stack_dialog_split_custom">Split Custom</string>
+
<!-- Expanded Status Bar Header: Battery Charged [CHAR LIMIT=40] -->
<string name="expanded_header_battery_charged">Charged</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index f2b4a69..bf19b8d 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -20,7 +20,7 @@
<item name="android:windowAnimationStyle">@style/Animation.RecentsActivity</item>
</style>
- <style name="RecentsTheme" parent="@android:style/Theme">
+ <style name="RecentsTheme" parent="@android:style/Theme.Material.Light">
<!-- NoTitle -->
<item name="android:windowNoTitle">true</item>
<!-- Misc -->
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 974235e..4dacacf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -423,7 +423,9 @@
}
for (TileRecord record : mRecords) {
- record.tileView.setDual(record.tile.supportsDualTargets());
+ if (record.tileView.setDual(record.tile.supportsDualTargets())) {
+ record.tileView.handleStateChanged(record.tile.getState());
+ }
if (record.tileView.getVisibility() == GONE) continue;
final int cw = record.row == 0 ? mLargeCellWidth : mCellWidth;
final int ch = record.row == 0 ? mLargeCellHeight : mCellHeight;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
index bb353d5..16ae6b4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
@@ -172,7 +172,7 @@
}
}
- public void setDual(boolean dual) {
+ public boolean setDual(boolean dual) {
final boolean changed = dual != mDual;
mDual = dual;
if (changed) {
@@ -199,6 +199,7 @@
setFocusable(!dual);
mDivider.setVisibility(dual ? VISIBLE : GONE);
postInvalidate();
+ return changed;
}
private void setRipple(RippleDrawable tileBackground) {
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 ddb96a2..30f92b9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -97,9 +97,7 @@
state.icon = ResourceIcon.get(iconId);
state.isOverlayIconWide = cb.isDataTypeIconWide;
state.autoMirrorDrawable = !cb.noSim;
- state.overlayIconId = cb.enabled && (cb.dataTypeIconId > 0) && !cb.wifiConnected
- ? cb.dataTypeIconId
- : 0;
+ state.overlayIconId = cb.enabled && (cb.dataTypeIconId > 0) ? cb.dataTypeIconId : 0;
state.filter = iconId != R.drawable.ic_qs_no_sim;
state.activityIn = cb.enabled && cb.activityIn;
state.activityOut = cb.enabled && cb.activityOut;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index cfd6b40..192acc6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -39,6 +39,8 @@
public static final boolean EnableSearchLayout = true;
// Enables the thumbnail alpha on the front-most task
public static final boolean EnableThumbnailAlphaOnFrontmost = false;
+ // Enables all system stacks to show up in the same recents stack
+ public static final boolean EnableMultiStackToSingleStack = true;
// This disables the bitmap and icon caches
public static final boolean DisableBackgroundCache = false;
// Enables the simulated task affiliations
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 3c75aac..9dd82fc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -24,7 +24,6 @@
import android.appwidget.AppWidgetProviderInfo;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -56,7 +55,6 @@
import com.android.systemui.recents.views.TaskViewTransform;
import java.util.ArrayList;
-import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -112,6 +110,9 @@
/** Preloads the next task */
public void run() {
+ // Temporarily skip this if multi stack is enabled
+ if (mConfig.multiStackEnabled) return;
+
RecentsConfiguration config = RecentsConfiguration.getInstance();
if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
@@ -362,13 +363,21 @@
}
void showRelativeAffiliatedTask(boolean showNextTask) {
+ // Return early if there is no focused stack
+ int focusedStackId = mSystemServicesProxy.getFocusedStack();
+ TaskStack focusedStack = null;
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
loader.preloadTasks(plan, true /* isTopTaskHome */);
- TaskStack stack = plan.getTaskStack();
+ if (mConfig.multiStackEnabled) {
+ if (focusedStackId < 0) return;
+ focusedStack = plan.getTaskStack(focusedStackId);
+ } else {
+ focusedStack = plan.getAllTaskStacks().get(0);
+ }
- // Return early if there are no tasks
- if (stack.getTaskCount() == 0) return;
+ // Return early if there are no tasks in the focused stack
+ if (focusedStack.getTaskCount() == 0) return;
ActivityManager.RunningTaskInfo runningTask = mSystemServicesProxy.getTopMostTask();
// Return early if there is no running task (can't determine affiliated tasks in this case)
@@ -377,7 +386,7 @@
if (mSystemServicesProxy.isInHomeStack(runningTask.id)) return;
// Find the task in the recents list
- ArrayList<Task> tasks = stack.getTasks();
+ ArrayList<Task> tasks = focusedStack.getTasks();
Task toTask = null;
ActivityOptions launchOpts = null;
int taskCount = tasks.size();
@@ -399,7 +408,7 @@
R.anim.recents_launch_prev_affiliated_task_source);
}
if (toTaskKey != null) {
- toTask = stack.findTaskWithId(toTaskKey.id);
+ toTask = focusedStack.findTaskWithId(toTaskKey.id);
}
numAffiliatedTasks = group.getTaskCount();
break;
@@ -473,8 +482,9 @@
// Reload the widget id before we get the task stack bounds
reloadSearchBarAppWidget(mContext, mSystemServicesProxy);
}
- mConfig.getTaskStackBounds(mWindowRect.width(), mWindowRect.height(), mStatusBarHeight,
- (mConfig.hasTransposedNavBar ? mNavBarWidth : 0), mTaskStackBounds);
+ mConfig.getAvailableTaskStackBounds(mWindowRect.width(), mWindowRect.height(),
+ mStatusBarHeight, (mConfig.hasTransposedNavBar ? mNavBarWidth : 0),
+ mTaskStackBounds);
if (mConfig.isLandscape && mConfig.hasTransposedNavBar) {
mSystemInsets.set(0, mStatusBarHeight, mNavBarWidth, 0);
} else {
@@ -653,8 +663,25 @@
// Create a new load plan if onPreloadRecents() was never triggered
sInstanceLoadPlan = loader.createLoadPlan(mContext);
}
+
+ // Temporarily skip the transition (use a dummy fade) if multi stack is enabled.
+ // For multi-stack we need to figure out where each of the tasks are going.
+ if (mConfig.multiStackEnabled) {
+ loader.preloadTasks(sInstanceLoadPlan, true);
+ ArrayList<TaskStack> stacks = sInstanceLoadPlan.getAllTaskStacks();
+ TaskStack stack = stacks.get(0);
+ mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, true);
+ TaskStackViewLayoutAlgorithm.VisibilityReport stackVr =
+ mDummyStackView.computeStackVisibilityReport();
+ ActivityOptions opts = getUnknownTransitionActivityOptions();
+ startAlternateRecentsActivity(topTask, opts, true /* fromHome */,
+ false /* fromSearchHome */, false /* fromThumbnail */, stackVr);
+ return;
+ }
+
loader.preloadTasks(sInstanceLoadPlan, isTopTaskHome);
- TaskStack stack = sInstanceLoadPlan.getTaskStack();
+ ArrayList<TaskStack> stacks = sInstanceLoadPlan.getAllTaskStacks();
+ TaskStack stack = stacks.get(0);
// Prepare the dummy stack for the transition
mDummyStackView.updateMinMaxScrollForStack(stack, mTriggeredFromAltTab, isTopTaskHome);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 1833e09..b1ac733 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -18,6 +18,7 @@
import android.app.Activity;
import android.app.ActivityOptions;
+import android.app.Dialog;
import android.app.SearchManager;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
@@ -43,7 +44,6 @@
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.recents.model.RecentsTaskLoader;
-import com.android.systemui.recents.model.SpaceNode;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.views.DebugOverlayView;
@@ -75,6 +75,9 @@
View mEmptyView;
DebugOverlayView mDebugOverlay;
+ // MultiStack debug
+ RecentsMultiStackDialog mMultiStackDebugDialog;
+
// Search AppWidget
RecentsAppWidgetHost mAppWidgetHost;
AppWidgetProviderInfo mSearchAppWidgetInfo;
@@ -190,7 +193,8 @@
}
// Start loading tasks according to the load plan
- if (plan.getTaskStack() == null) {
+ ArrayList<TaskStack> stacks = plan.getAllTaskStacks();
+ if (stacks.size() == 0) {
loader.preloadTasks(plan, mConfig.launchedFromHome);
}
RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
@@ -199,9 +203,7 @@
loadOpts.numVisibleTaskThumbnails = mConfig.launchedNumVisibleThumbnails;
loader.loadTasks(this, plan, loadOpts);
- SpaceNode root = plan.getSpaceNode();
- ArrayList<TaskStack> stacks = root.getStacks();
- boolean hasTasks = root.hasTasks();
+ boolean hasTasks = plan.hasTasks();
if (hasTasks) {
mRecentsView.setTaskStacks(stacks);
}
@@ -591,6 +593,40 @@
}
}
+
+ /**** RecentsMultiStackDialog ****/
+
+ private RecentsMultiStackDialog getMultiStackDebugDialog() {
+ if (mMultiStackDebugDialog == null) {
+ mMultiStackDebugDialog = new RecentsMultiStackDialog(getFragmentManager());
+ }
+ return mMultiStackDebugDialog;
+ }
+
+ @Override
+ public void onMultiStackAddStack() {
+ RecentsMultiStackDialog dialog = getMultiStackDebugDialog();
+ dialog.showAddStackDialog();
+ }
+
+ @Override
+ public void onMultiStackResizeStack() {
+ RecentsMultiStackDialog dialog = getMultiStackDebugDialog();
+ dialog.showResizeStackDialog();
+ }
+
+ @Override
+ public void onMultiStackRemoveStack() {
+ RecentsMultiStackDialog dialog = getMultiStackDebugDialog();
+ dialog.showRemoveStackDialog();
+ }
+
+ @Override
+ public void onMultiStackMoveTask(Task t) {
+ RecentsMultiStackDialog dialog = getMultiStackDebugDialog();
+ dialog.showMoveTaskDialog(t);
+ }
+
/**** RecentsView.RecentsViewCallbacks Implementation ****/
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index bc10a48..1736c77 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -134,6 +134,7 @@
public boolean fakeShadows;
/** Dev options and global settings */
+ public boolean multiStackEnabled;
public boolean lockToAppEnabled;
public boolean developerOptionsEnabled;
public boolean debugModeEnabled;
@@ -294,6 +295,7 @@
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED) != 0;
lockToAppEnabled = ssp.getSystemSetting(context,
Settings.System.LOCK_TO_APP_ENABLED) != 0;
+ multiStackEnabled = "1".equals(ssp.getSystemProperty("overview.enableMultiStack"));
}
/** Called when the configuration has changed, and we want to reset any configuration specific
@@ -335,8 +337,8 @@
* Returns the task stack bounds in the current orientation. These bounds do not account for
* the system insets.
*/
- public void getTaskStackBounds(int windowWidth, int windowHeight, int topInset, int rightInset,
- Rect taskStackBounds) {
+ public void getAvailableTaskStackBounds(int windowWidth, int windowHeight, int topInset,
+ int rightInset, Rect taskStackBounds) {
Rect searchBarBounds = new Rect();
getSearchBarBounds(windowWidth, windowHeight, topInset, searchBarBounds);
if (isLandscape && hasTransposedSearchBar) {
@@ -353,7 +355,7 @@
* the system insets.
*/
public void getSearchBarBounds(int windowWidth, int windowHeight, int topInset,
- Rect searchBarSpaceBounds) {
+ Rect searchBarSpaceBounds) {
// Return empty rects if search is not enabled
int searchBarSize = searchBarSpaceHeightPx;
if (!Constants.DebugFlags.App.EnableSearchLayout || !hasSearchBarAppWidget()) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsMultiStackDialog.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsMultiStackDialog.java
new file mode 100644
index 0000000..fdf9d39
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsMultiStackDialog.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents;
+
+import android.app.ActivityManager;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ResolveInfo;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.util.MutableInt;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.Toast;
+import com.android.systemui.R;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.RecentsTaskLoader;
+import com.android.systemui.recents.model.Task;
+
+import java.util.List;
+
+/**
+ * A helper for the dialogs that show when multistack debugging is on.
+ */
+public class RecentsMultiStackDialog extends DialogFragment {
+
+ static final String TAG = "RecentsMultiStackDialog";
+
+ public static final int ADD_STACK_DIALOG = 0;
+ public static final int ADD_STACK_PICK_APP_DIALOG = 1;
+ public static final int REMOVE_STACK_DIALOG = 2;
+ public static final int RESIZE_STACK_DIALOG = 3;
+ public static final int RESIZE_STACK_PICK_STACK_DIALOG = 4;
+ public static final int MOVE_TASK_DIALOG = 5;
+
+ FragmentManager mFragmentManager;
+ int mCurrentDialogType;
+ MutableInt mTargetStackIndex = new MutableInt(0);
+ Task mTaskToMove;
+ SparseArray<ActivityManager.StackInfo> mStacks;
+ List<ResolveInfo> mLauncherActivities;
+ Rect mAddStackRect;
+ Intent mAddStackIntent;
+
+ View mAddStackDialogContent;
+
+ public RecentsMultiStackDialog() {}
+
+ public RecentsMultiStackDialog(FragmentManager mgr) {
+ mFragmentManager = mgr;
+ }
+
+ /** Shows the add-stack dialog. */
+ void showAddStackDialog() {
+ mCurrentDialogType = ADD_STACK_DIALOG;
+ show(mFragmentManager, TAG);
+ }
+
+ /** Creates a new add-stack dialog. */
+ private void createAddStackDialog(final Context context, LayoutInflater inflater,
+ AlertDialog.Builder builder, final SystemServicesProxy ssp) {
+ builder.setTitle("Add Stack - Enter new dimensions");
+ mAddStackDialogContent =
+ inflater.inflate(R.layout.recents_multistack_stack_size_dialog, null, false);
+ Rect windowRect = ssp.getWindowRect();
+ setDimensionInEditText(mAddStackDialogContent, R.id.inset_left, windowRect.left);
+ setDimensionInEditText(mAddStackDialogContent, R.id.inset_top, windowRect.top);
+ setDimensionInEditText(mAddStackDialogContent, R.id.inset_right, windowRect.right);
+ setDimensionInEditText(mAddStackDialogContent, R.id.inset_bottom, windowRect.bottom);
+ builder.setView(mAddStackDialogContent);
+ builder.setPositiveButton("Add Stack", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ int left = getDimensionFromEditText(mAddStackDialogContent, R.id.inset_left);
+ int top = getDimensionFromEditText(mAddStackDialogContent, R.id.inset_top);
+ int right = getDimensionFromEditText(mAddStackDialogContent, R.id.inset_right);
+ int bottom = getDimensionFromEditText(mAddStackDialogContent, R.id.inset_bottom);
+ if (bottom <= top || right <= left) {
+ Toast.makeText(context, "Invalid dimensions", Toast.LENGTH_SHORT).show();
+ dismiss();
+ return;
+ }
+
+ // Prompt the user for the app to start
+ dismiss();
+ mCurrentDialogType = ADD_STACK_PICK_APP_DIALOG;
+ mAddStackRect = new Rect(left, top, right, bottom);
+ show(mFragmentManager, TAG);
+ }
+ });
+ builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismiss();
+ }
+ });
+ }
+
+ /** Creates a new add-stack pick-app dialog. */
+ private void createAddStackPickAppDialog(final Context context, LayoutInflater inflater,
+ AlertDialog.Builder builder, final SystemServicesProxy ssp) {
+ mLauncherActivities = ssp.getLauncherApps();
+ mAddStackIntent = null;
+ int activityCount = mLauncherActivities.size();
+ CharSequence[] activityNames = new CharSequence[activityCount];
+ for (int i = 0; i < activityCount; i++) {
+ activityNames[i] = ssp.getActivityLabel(mLauncherActivities.get(i).activityInfo);
+ }
+ builder.setTitle("Add Stack - Pick starting app");
+ builder.setSingleChoiceItems(activityNames, -1,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ ActivityInfo ai = mLauncherActivities.get(which).activityInfo;
+ mAddStackIntent = new Intent(Intent.ACTION_MAIN);
+ mAddStackIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ mAddStackIntent.setComponent(new ComponentName(ai.packageName, ai.name));
+ }
+ });
+ builder.setPositiveButton("Add Stack", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // Display 0 = default display
+ ssp.createNewStack(0, mAddStackRect, mAddStackIntent);
+ }
+ });
+ builder.setNegativeButton("Skip", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // Display 0 = default display
+ ssp.createNewStack(0, mAddStackRect, null);
+ }
+ });
+ }
+
+ /** Shows the resize-stack dialog. */
+ void showResizeStackDialog() {
+ mCurrentDialogType = RESIZE_STACK_PICK_STACK_DIALOG;
+ show(mFragmentManager, TAG);
+ }
+
+ /** Creates a new resize-stack pick-stack dialog. */
+ private void createResizeStackPickStackDialog(final Context context, LayoutInflater inflater,
+ AlertDialog.Builder builder, final SystemServicesProxy ssp) {
+ mStacks = ssp.getAllStackInfos();
+ mTargetStackIndex.value = -1;
+ CharSequence[] stackNames = getAllStacksDescriptions(mStacks, -1, null);
+ builder.setTitle("Resize Stack - Pick stack");
+ builder.setSingleChoiceItems(stackNames, mTargetStackIndex.value,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mTargetStackIndex.value = which;
+ }
+ });
+ builder.setPositiveButton("Resize Stack", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (mTargetStackIndex.value != -1) {
+ // Prompt the user for the new dimensions
+ dismiss();
+ mCurrentDialogType = RESIZE_STACK_DIALOG;
+ show(mFragmentManager, TAG);
+ }
+ }
+ });
+ builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismiss();
+ }
+ });
+ }
+
+ /** Creates a new resize-stack dialog. */
+ private void createResizeStackDialog(final Context context, LayoutInflater inflater,
+ AlertDialog.Builder builder, final SystemServicesProxy ssp) {
+ builder.setTitle("Resize Stack - Enter new dimensions");
+ final ActivityManager.StackInfo stack = mStacks.valueAt(mTargetStackIndex.value);
+ mAddStackDialogContent =
+ inflater.inflate(R.layout.recents_multistack_stack_size_dialog, null, false);
+ setDimensionInEditText(mAddStackDialogContent, R.id.inset_left, stack.bounds.left);
+ setDimensionInEditText(mAddStackDialogContent, R.id.inset_top, stack.bounds.top);
+ setDimensionInEditText(mAddStackDialogContent, R.id.inset_right, stack.bounds.right);
+ setDimensionInEditText(mAddStackDialogContent, R.id.inset_bottom, stack.bounds.bottom);
+ builder.setView(mAddStackDialogContent);
+ builder.setPositiveButton("Resize Stack", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ int left = getDimensionFromEditText(mAddStackDialogContent, R.id.inset_left);
+ int top = getDimensionFromEditText(mAddStackDialogContent, R.id.inset_top);
+ int right = getDimensionFromEditText(mAddStackDialogContent, R.id.inset_right);
+ int bottom = getDimensionFromEditText(mAddStackDialogContent, R.id.inset_bottom);
+ if (bottom <= top || right <= left) {
+ Toast.makeText(context, "Invalid dimensions", Toast.LENGTH_SHORT).show();
+ dismiss();
+ return;
+ }
+ ssp.resizeStack(stack.stackId, new Rect(left, top, right, bottom));
+ }
+ });
+ builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismiss();
+ }
+ });
+ }
+
+ /** Shows the remove-stack dialog. */
+ void showRemoveStackDialog() {
+ mCurrentDialogType = REMOVE_STACK_DIALOG;
+ show(mFragmentManager, TAG);
+ }
+
+ /** Shows the move-task dialog. */
+ void showMoveTaskDialog(Task task) {
+ mCurrentDialogType = MOVE_TASK_DIALOG;
+ mTaskToMove = task;
+ show(mFragmentManager, TAG);
+ }
+
+ /** Creates a new move-stack dialog. */
+ private void createMoveTaskDialog(final Context context, LayoutInflater inflater,
+ AlertDialog.Builder builder, final SystemServicesProxy ssp) {
+ mStacks = ssp.getAllStackInfos();
+ mTargetStackIndex.value = -1;
+ CharSequence[] stackNames = getAllStacksDescriptions(mStacks, mTaskToMove.key.stackId,
+ mTargetStackIndex);
+ builder.setTitle("Move Task to Stack");
+ builder.setSingleChoiceItems(stackNames, mTargetStackIndex.value,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mTargetStackIndex.value = which;
+ }
+ });
+ builder.setPositiveButton("Move Task", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (mTargetStackIndex.value != -1) {
+ ActivityManager.StackInfo toStack = mStacks.valueAt(mTargetStackIndex.value);
+ if (toStack.stackId != mTaskToMove.key.stackId) {
+ ssp.moveTaskToStack(mTaskToMove.key.id, toStack.stackId, true);
+ mTaskToMove.setStackId(toStack.stackId);
+ }
+ }
+ }
+ });
+ builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismiss();
+ }
+ });
+ }
+
+ /** Helper to get an integer value from an edit text. */
+ private int getDimensionFromEditText(View container, int id) {
+ String text = ((EditText) container.findViewById(id)).getText().toString();
+ if (text.trim().length() != 0) {
+ return Integer.parseInt(text.trim());
+ }
+ return 0;
+ }
+
+ /** Helper to set an integer value to an edit text. */
+ private void setDimensionInEditText(View container, int id, int value) {
+ ((EditText) container.findViewById(id)).setText("" + value);
+ }
+
+ /** Gets a list of all the stacks. */
+ private CharSequence[] getAllStacksDescriptions(SparseArray<ActivityManager.StackInfo> stacks,
+ int targetStackId, MutableInt indexOfTargetStackId) {
+ int stackCount = stacks.size();
+ CharSequence[] stackNames = new CharSequence[stackCount];
+ for (int i = 0; i < stackCount; i++) {
+ ActivityManager.StackInfo stack = stacks.valueAt(i);
+ Rect b = stack.bounds;
+ String desc = "Stack " + stack.stackId + " / " +
+ "" + (stack.taskIds.length > 0 ? stack.taskIds.length : "No") + " tasks\n" +
+ "(" + b.left + ", " + b.top + ")-(" + b.right + ", " + b.bottom + ")\n";
+ stackNames[i] = desc;
+ if (targetStackId != -1 && stack.stackId == targetStackId) {
+ indexOfTargetStackId.value = i;
+ }
+ }
+ return stackNames;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle args) {
+ final Context context = this.getActivity();
+ final SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
+ LayoutInflater inflater = getActivity().getLayoutInflater();
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ switch(mCurrentDialogType) {
+ case ADD_STACK_DIALOG:
+ createAddStackDialog(context, inflater, builder, ssp);
+ break;
+ case ADD_STACK_PICK_APP_DIALOG:
+ createAddStackPickAppDialog(context, inflater, builder, ssp);
+ break;
+ case MOVE_TASK_DIALOG:
+ createMoveTaskDialog(context, inflater, builder, ssp);
+ break;
+ case RESIZE_STACK_PICK_STACK_DIALOG:
+ createResizeStackPickStackDialog(context, inflater, builder, ssp);
+ break;
+ case RESIZE_STACK_DIALOG:
+ createResizeStackDialog(context, inflater, builder, ssp);
+ break;
+ }
+ return builder.create();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 237d4f0..72040fe 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -20,6 +20,7 @@
import android.app.ActivityManagerNative;
import android.app.ActivityOptions;
import android.app.AppGlobals;
+import android.app.IActivityContainer;
import android.app.IActivityManager;
import android.app.ITaskStackListener;
import android.app.SearchManager;
@@ -49,10 +50,12 @@
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
import android.util.Pair;
+import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.SurfaceControl;
@@ -64,6 +67,8 @@
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
@@ -228,6 +233,23 @@
return null;
}
+ /** Returns a list of all the launcher apps sorted by name. */
+ public List<ResolveInfo> getLauncherApps() {
+ if (mPm == null) return new ArrayList<ResolveInfo>();
+
+ final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+ mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ List<ResolveInfo> activities = mPm.queryIntentActivities(mainIntent, 0 /* flags */);
+ Collections.sort(activities, new Comparator<ResolveInfo>() {
+ @Override
+ public int compare(ResolveInfo o1, ResolveInfo o2) {
+ return getActivityLabel(o1.activityInfo).compareTo(
+ getActivityLabel(o2.activityInfo));
+ }
+ });
+ return activities;
+ }
+
/** Returns whether the recents is currently running */
public boolean isRecentsTopMost(ActivityManager.RunningTaskInfo topTask,
AtomicBoolean isHomeTopMost) {
@@ -250,6 +272,64 @@
return false;
}
+ /** Create a new stack. */
+ public void createNewStack(int displayId, Rect bounds, Intent activity) {
+ try {
+ IActivityContainer container = mIam.createStackOnDisplay(displayId);
+ if (container != null) {
+ // Resize the stack
+ resizeStack(container.getStackId(), bounds);
+ // Start the new activity on that stack
+ container.startActivity(activity);
+ }
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /** Resizes a stack. */
+ public void resizeStack(int stackId, Rect bounds) {
+ if (mIam == null) return;
+
+ try {
+ mIam.resizeStack(stackId, bounds);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /** Returns the stack info for all stacks. */
+ public SparseArray<ActivityManager.StackInfo> getAllStackInfos() {
+ if (mIam == null) return new SparseArray<ActivityManager.StackInfo>();
+
+ try {
+ SparseArray<ActivityManager.StackInfo> stacks =
+ new SparseArray<ActivityManager.StackInfo>();
+ List<ActivityManager.StackInfo> infos = mIam.getAllStackInfos();
+ int stackCount = infos.size();
+ for (int i = 0; i < stackCount; i++) {
+ ActivityManager.StackInfo info = infos.get(i);
+ stacks.put(info.stackId, info);
+ }
+ return stacks;
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ return new SparseArray<ActivityManager.StackInfo>();
+ }
+ }
+
+ /** Returns the focused stack id. */
+ public int getFocusedStack() {
+ if (mIam == null) return -1;
+
+ try {
+ return mIam.getFocusedStackId();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ return -1;
+ }
+ }
+
/** Returns whether the specified task is in the home stack */
public boolean isInHomeStack(int taskId) {
if (mAm == null) return false;
@@ -313,7 +393,7 @@
return thumbnail;
}
- /** Moves a task to the front with the specified activity options */
+ /** Moves a task to the front with the specified activity options. */
public void moveTaskToFront(int taskId, ActivityOptions opts) {
if (mAm == null) return;
if (Constants.DebugFlags.App.EnableSystemServicesProxy) return;
@@ -326,6 +406,18 @@
}
}
+ /** Moves a task to another stack. */
+ public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
+ if (mIam == null) return;
+ if (Constants.DebugFlags.App.EnableSystemServicesProxy) return;
+
+ try {
+ mIam.moveTaskToStack(taskId, stackId, toTop);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
/** Removes the task */
public void removeTask(int taskId) {
if (mAm == null) return;
@@ -524,6 +616,13 @@
}
/**
+ * Returns a system property.
+ */
+ public String getSystemProperty(String key) {
+ return SystemProperties.get(key);
+ }
+
+ /**
* Returns the window rect.
*/
public Rect getWindowRect() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 3d25c80..788e473 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -20,9 +20,12 @@
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.util.Log;
+import android.util.SparseArray;
+import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -60,7 +63,7 @@
SystemServicesProxy mSystemServicesProxy;
List<ActivityManager.RecentTaskInfo> mRawTasks;
- TaskStack mStack;
+ SparseArray<TaskStack> mStacks = new SparseArray<>();
HashMap<Task.ComponentNameKey, ActivityInfoHandle> mActivityInfoCache =
new HashMap<Task.ComponentNameKey, ActivityInfoHandle>();
@@ -90,21 +93,28 @@
synchronized void preloadPlan(RecentsTaskLoader loader, boolean isTopTaskHome) {
if (DEBUG) Log.d(TAG, "preloadPlan");
+ // This activity info cache will be used for both preloadPlan() and executePlan()
mActivityInfoCache.clear();
- mStack = new TaskStack();
+
+ // TODO (multi-display): Currently assume the primary display
+ Rect displayBounds = mSystemServicesProxy.getWindowRect();
Resources res = mContext.getResources();
- ArrayList<Task> loadedTasks = new ArrayList<Task>();
+ SparseArray<ArrayList<Task>> stacksTasks = new SparseArray<>();
if (mRawTasks == null) {
preloadRawTasks(isTopTaskHome);
}
+ int firstStackId = -1;
int taskCount = mRawTasks.size();
for (int i = 0; i < taskCount; i++) {
ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
+ if (firstStackId < 0) {
+ firstStackId = t.stackId;
+ }
// Compose the task key
- Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.baseIntent, t.userId,
- t.firstActiveTime, t.lastActiveTime);
+ Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.stackId, t.baseIntent,
+ t.userId, t.firstActiveTime, t.lastActiveTime);
// Get an existing activity info handle if possible
Task.ComponentNameKey cnKey = taskKey.getComponentNameKey();
@@ -143,14 +153,42 @@
iconFilename);
task.thumbnail = loader.getAndUpdateThumbnail(taskKey, mSystemServicesProxy, false);
if (DEBUG) Log.d(TAG, "\tthumbnail: " + taskKey + ", " + task.thumbnail);
- loadedTasks.add(task);
- }
- mStack.setTasks(loadedTasks);
- mStack.createAffiliatedGroupings(mConfig);
- // Assertion
- if (mStack.getTaskCount() != mRawTasks.size()) {
- throw new RuntimeException("Loading failed");
+ if (!mConfig.multiStackEnabled ||
+ Constants.DebugFlags.App.EnableMultiStackToSingleStack) {
+ ArrayList<Task> stackTasks = stacksTasks.get(firstStackId);
+ if (stackTasks == null) {
+ stackTasks = new ArrayList<Task>();
+ stacksTasks.put(firstStackId, stackTasks);
+ }
+ stackTasks.add(task);
+ } else {
+ ArrayList<Task> stackTasks = stacksTasks.get(t.stackId);
+ if (stackTasks == null) {
+ stackTasks = new ArrayList<Task>();
+ stacksTasks.put(t.stackId, stackTasks);
+ }
+ stackTasks.add(task);
+ }
+ }
+
+ // Initialize the stacks
+ SparseArray<ActivityManager.StackInfo> stackInfos = mSystemServicesProxy.getAllStackInfos();
+ mStacks.clear();
+ int stackCount = stacksTasks.size();
+ for (int i = 0; i < stackCount; i++) {
+ int stackId = stacksTasks.keyAt(i);
+ ActivityManager.StackInfo info = stackInfos.get(stackId);
+ ArrayList<Task> stackTasks = stacksTasks.valueAt(i);
+ TaskStack stack = new TaskStack(stackId);
+ if (Constants.DebugFlags.App.EnableMultiStackToSingleStack) {
+ stack.setBounds(displayBounds, displayBounds);
+ } else {
+ stack.setBounds(info.bounds, displayBounds);
+ }
+ stack.setTasks(stackTasks);
+ stack.createAffiliatedGroupings(mConfig);
+ mStacks.put(stackId, stack);
}
}
@@ -166,72 +204,93 @@
Resources res = mContext.getResources();
// Iterate through each of the tasks and load them according to the load conditions.
- ArrayList<Task> tasks = mStack.getTasks();
- int taskCount = tasks.size();
- for (int i = 0; i < taskCount; i++) {
- ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
- Task task = tasks.get(i);
- Task.TaskKey taskKey = task.key;
+ int stackCount = mStacks.size();
+ for (int j = 0; j < stackCount; j++) {
+ ArrayList<Task> tasks = mStacks.valueAt(j).getTasks();
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
+ Task task = tasks.get(i);
+ Task.TaskKey taskKey = task.key;
- // Get an existing activity info handle if possible
- Task.ComponentNameKey cnKey = taskKey.getComponentNameKey();
- ActivityInfoHandle infoHandle;
- boolean hadCachedActivityInfo = false;
- if (mActivityInfoCache.containsKey(cnKey)) {
- infoHandle = mActivityInfoCache.get(cnKey);
- hadCachedActivityInfo = true;
- } else {
- infoHandle = new ActivityInfoHandle();
- }
-
- boolean isRunningTask = (task.key.id == opts.runningTaskId);
- boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks);
- boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails);
-
- // If requested, skip the running task
- if (opts.onlyLoadPausedActivities && isRunningTask) {
- continue;
- }
-
- if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
- if (task.activityIcon == null) {
- if (DEBUG) Log.d(TAG, "\tLoading icon: " + taskKey);
- task.activityIcon = loader.getAndUpdateActivityIcon(taskKey, t.taskDescription,
- mSystemServicesProxy, res, infoHandle, true);
+ // Get an existing activity info handle if possible
+ Task.ComponentNameKey cnKey = taskKey.getComponentNameKey();
+ ActivityInfoHandle infoHandle;
+ boolean hadCachedActivityInfo = false;
+ if (mActivityInfoCache.containsKey(cnKey)) {
+ infoHandle = mActivityInfoCache.get(cnKey);
+ hadCachedActivityInfo = true;
+ } else {
+ infoHandle = new ActivityInfoHandle();
}
- }
- if (opts.loadThumbnails && (isRunningTask || isVisibleThumbnail)) {
- if (task.thumbnail == null || isRunningTask) {
- if (DEBUG) Log.d(TAG, "\tLoading thumbnail: " + taskKey);
- if (mConfig.svelteLevel <= RecentsConfiguration.SVELTE_LIMIT_CACHE) {
- task.thumbnail = loader.getAndUpdateThumbnail(taskKey, mSystemServicesProxy,
- true);
- } else if (mConfig.svelteLevel == RecentsConfiguration.SVELTE_DISABLE_CACHE) {
- loadQueue.addTask(task);
+
+ boolean isRunningTask = (task.key.id == opts.runningTaskId);
+ boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks);
+ boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails);
+
+ // If requested, skip the running task
+ if (opts.onlyLoadPausedActivities && isRunningTask) {
+ continue;
+ }
+
+ if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
+ if (task.activityIcon == null) {
+ if (DEBUG) Log.d(TAG, "\tLoading icon: " + taskKey);
+ task.activityIcon = loader.getAndUpdateActivityIcon(taskKey,
+ t.taskDescription, mSystemServicesProxy, res, infoHandle, true);
}
}
- }
+ if (opts.loadThumbnails && (isRunningTask || isVisibleThumbnail)) {
+ if (task.thumbnail == null || isRunningTask) {
+ if (DEBUG) Log.d(TAG, "\tLoading thumbnail: " + taskKey);
+ if (mConfig.svelteLevel <= RecentsConfiguration.SVELTE_LIMIT_CACHE) {
+ task.thumbnail = loader.getAndUpdateThumbnail(taskKey,
+ mSystemServicesProxy, true);
+ } else if (mConfig.svelteLevel == RecentsConfiguration.SVELTE_DISABLE_CACHE) {
+ loadQueue.addTask(task);
+ }
+ }
+ }
- // Update the activity info cache
- if (!hadCachedActivityInfo && infoHandle.info != null) {
- mActivityInfoCache.put(cnKey, infoHandle);
+ // Update the activity info cache
+ if (!hadCachedActivityInfo && infoHandle.info != null) {
+ mActivityInfoCache.put(cnKey, infoHandle);
+ }
}
}
}
/**
- * Composes and returns a TaskStack from the preloaded list of recent tasks.
+ * Returns all TaskStacks from the preloaded list of recent tasks.
*/
- public TaskStack getTaskStack() {
- return mStack;
+ public ArrayList<TaskStack> getAllTaskStacks() {
+ ArrayList<TaskStack> stacks = new ArrayList<TaskStack>();
+ int stackCount = mStacks.size();
+ for (int i = 0; i < stackCount; i++) {
+ stacks.add(mStacks.valueAt(i));
+ }
+ // Ensure that we have at least one stack
+ if (stacks.isEmpty()) {
+ stacks.add(new TaskStack());
+ }
+ return stacks;
}
/**
- * Composes and returns a SpaceNode from the preloaded list of recent tasks.
+ * Returns a specific TaskStack from the preloaded list of recent tasks.
*/
- public SpaceNode getSpaceNode() {
- SpaceNode node = new SpaceNode();
- node.setStack(mStack);
- return node;
+ public TaskStack getTaskStack(int stackId) {
+ return mStacks.get(stackId);
+ }
+
+ /** Returns whether there are any tasks in any stacks. */
+ public boolean hasTasks() {
+ int stackCount = mStacks.size();
+ for (int i = 0; i < stackCount; i++) {
+ if (mStacks.valueAt(i).getTaskCount() > 0) {
+ return true;
+ }
+ }
+ return false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java b/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java
deleted file mode 100644
index 831698a..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/model/SpaceNode.java
+++ /dev/null
@@ -1,82 +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.recents.model;
-
-import android.graphics.Rect;
-
-import java.util.ArrayList;
-
-
-/**
- * The full recents space is partitioned using a BSP into various nodes that define where task
- * stacks should be placed.
- */
-public class SpaceNode {
- /* BSP node callbacks */
- public interface SpaceNodeCallbacks {
- /** Notifies when a node is added */
- public void onSpaceNodeAdded(SpaceNode node);
- /** Notifies when a node is measured */
- public void onSpaceNodeMeasured(SpaceNode node, Rect rect);
- }
-
- SpaceNode mStartNode;
- SpaceNode mEndNode;
-
- TaskStack mStack;
-
- public SpaceNode() {
- // Do nothing
- }
-
- /** Sets the current stack for this space node */
- public void setStack(TaskStack stack) {
- mStack = stack;
- }
-
- /** Returns the task stack (not null if this is a leaf) */
- TaskStack getStack() {
- return mStack;
- }
-
- /** Returns whether there are any tasks in any stacks below this node. */
- public boolean hasTasks() {
- return (mStack.getTaskCount() > 0) ||
- (mStartNode != null && mStartNode.hasTasks()) ||
- (mEndNode != null && mEndNode.hasTasks());
- }
-
- /** Returns whether this is a leaf node */
- boolean isLeafNode() {
- return (mStartNode == null) && (mEndNode == null);
- }
-
- /** Returns all the descendent task stacks */
- private void getStacksRec(ArrayList<TaskStack> stacks) {
- if (isLeafNode()) {
- stacks.add(mStack);
- } else {
- mStartNode.getStacksRec(stacks);
- mEndNode.getStacksRec(stacks);
- }
- }
- public ArrayList<TaskStack> getStacks() {
- ArrayList<TaskStack> stacks = new ArrayList<TaskStack>();
- getStacksRec(stacks);
- return stacks;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 55dfe45..0cd55d7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -36,6 +36,9 @@
public void onTaskDataLoaded();
/* Notifies when a task has been unbound */
public void onTaskDataUnloaded();
+
+ /* Notifies when a task's stack id has changed. */
+ public void onMultiStackDebugTaskStackIdChanged();
}
/** The ComponentNameKey represents the unique primary key for a component
@@ -68,14 +71,17 @@
public static class TaskKey {
final ComponentNameKey mComponentNameKey;
public final int id;
+ public int stackId;
public final Intent baseIntent;
public final int userId;
public long firstActiveTime;
public long lastActiveTime;
- public TaskKey(int id, Intent intent, int userId, long firstActiveTime, long lastActiveTime) {
+ public TaskKey(int id, int stackId, Intent intent, int userId, long firstActiveTime,
+ long lastActiveTime) {
mComponentNameKey = new ComponentNameKey(intent.getComponent(), userId);
this.id = id;
+ this.stackId = stackId;
this.baseIntent = intent;
this.userId = userId;
this.firstActiveTime = firstActiveTime;
@@ -92,18 +98,19 @@
if (!(o instanceof TaskKey)) {
return false;
}
- return id == ((TaskKey) o).id
- && userId == ((TaskKey) o).userId;
+ TaskKey otherKey = (TaskKey) o;
+ return id == otherKey.id && stackId == otherKey.stackId && userId == otherKey.userId;
}
@Override
public int hashCode() {
- return (id << 5) + userId;
+ return Objects.hash(id, stackId, userId);
}
@Override
public String toString() {
return "Task.Key: " + id + ", "
+ + "s: " + stackId + ", "
+ "u: " + userId + ", "
+ "lat: " + lastActiveTime + ", "
+ baseIntent.getComponent().getPackageName();
@@ -180,6 +187,14 @@
this.group = group;
}
+ /** Updates the stack id of this task. */
+ public void setStackId(int stackId) {
+ key.stackId = stackId;
+ if (mCb != null) {
+ mCb.onMultiStackDebugTaskStackIdChanged();
+ }
+ }
+
/** Notifies the callback listeners that this task has been loaded */
public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable applicationIcon) {
this.applicationIcon = applicationIcon;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 7f7eee4..5aaea15 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -17,6 +17,7 @@
package com.android.systemui.recents.model;
import android.graphics.Color;
+import android.graphics.Rect;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.NamedCounter;
@@ -173,33 +174,38 @@
public void onStackUnfiltered(TaskStack newStack, ArrayList<Task> curTasks);
}
- /** A pair of indices representing the group and task positions in the stack and group. */
- public static class GroupTaskIndex {
- public int groupIndex; // Index in the stack
- public int taskIndex; // Index in the group
-
- public GroupTaskIndex() {}
-
- public GroupTaskIndex(int gi, int ti) {
- groupIndex = gi;
- taskIndex = ti;
- }
- }
-
// The task offset to apply to a task id as a group affiliation
static final int IndividualTaskIdOffset = 1 << 16;
+ public final int id;
+ public final Rect stackBounds = new Rect();
+ public final Rect displayBounds = new Rect();
+
FilteredTaskList mTaskList = new FilteredTaskList();
TaskStackCallbacks mCb;
ArrayList<TaskGrouping> mGroups = new ArrayList<TaskGrouping>();
HashMap<Integer, TaskGrouping> mAffinitiesGroups = new HashMap<Integer, TaskGrouping>();
- /** Sets the callbacks for this task stack */
+ public TaskStack() {
+ this(0);
+ }
+
+ public TaskStack(int stackId) {
+ id = stackId;
+ }
+
+ /** Sets the callbacks for this task stack. */
public void setCallbacks(TaskStackCallbacks cb) {
mCb = cb;
}
+ /** Sets the bounds of this stack. */
+ public void setBounds(Rect stackBounds, Rect displayBounds) {
+ this.stackBounds.set(stackBounds);
+ this.displayBounds.set(displayBounds);
+ }
+
/** Resets this TaskStack. */
public void reset() {
mCb = null;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 5152150..1bed553 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -29,8 +29,10 @@
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewStub;
import android.view.WindowInsets;
import android.widget.FrameLayout;
+import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -40,6 +42,7 @@
import com.android.systemui.recents.model.TaskStack;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -56,13 +59,22 @@
public void onAllTaskViewsDismissed();
public void onExitToHomeAnimationTriggered();
public void onScreenPinningRequest();
+
+ public void onMultiStackAddStack();
+ public void onMultiStackResizeStack();
+ public void onMultiStackRemoveStack();
+ public void onMultiStackMoveTask(Task t);
}
RecentsConfiguration mConfig;
LayoutInflater mInflater;
DebugOverlayView mDebugOverlay;
+ ViewStub mMultiStackDebugStub;
+ View mMultiStackDebugView;
+ RecentsViewLayoutAlgorithm mLayoutAlgorithm;
ArrayList<TaskStack> mStacks;
+ List<TaskStackView> mImmutableTaskStackViews = new ArrayList<TaskStackView>();
View mSearchBar;
RecentsViewCallbacks mCb;
@@ -82,6 +94,29 @@
super(context, attrs, defStyleAttr, defStyleRes);
mConfig = RecentsConfiguration.getInstance();
mInflater = LayoutInflater.from(context);
+ mLayoutAlgorithm = new RecentsViewLayoutAlgorithm(mConfig);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ if (!mConfig.multiStackEnabled) return;
+
+ mMultiStackDebugStub = (ViewStub) findViewById(R.id.multistack_debug_view_stub);
+ if (mMultiStackDebugView == null) {
+ mMultiStackDebugView = mMultiStackDebugStub.inflate();
+ mMultiStackDebugView.findViewById(R.id.add_stack).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCb.onMultiStackAddStack();
+ }
+ });
+ mMultiStackDebugView.findViewById(R.id.resize_stack).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCb.onMultiStackResizeStack();
+ }
+ });
+ }
}
/** Sets the callbacks */
@@ -99,24 +134,19 @@
int numStacks = stacks.size();
// Make a list of the stack view children only
- ArrayList<TaskStackView> stackViews = new ArrayList<TaskStackView>();
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child != mSearchBar) {
- stackViews.add((TaskStackView) child);
- }
- }
+ ArrayList<TaskStackView> stackViewsList = new ArrayList<TaskStackView>();
+ List<TaskStackView> stackViews = getTaskStackViews();
// Remove all/extra stack views
int numTaskStacksToKeep = 0; // Keep no tasks if we are recreating the layout
if (mConfig.launchedReuseTaskStackViews) {
- numTaskStacksToKeep = Math.min(childCount, numStacks);
+ numTaskStacksToKeep = Math.min(stackViews.size(), numStacks);
}
for (int i = stackViews.size() - 1; i >= numTaskStacksToKeep; i--) {
removeView(stackViews.get(i));
stackViews.remove(i);
}
+ stackViewsList.addAll(stackViews);
// Update the stack views that we are keeping
for (int i = 0; i < numTaskStacksToKeep; i++) {
@@ -133,42 +163,51 @@
TaskStackView stackView = new TaskStackView(getContext(), stack);
stackView.setCallbacks(this);
addView(stackView);
+ stackViewsList.add(stackView);
}
+ // Set the immutable stack views list
+ mImmutableTaskStackViews = Collections.unmodifiableList(stackViewsList);
+
// Enable debug mode drawing on all the stacks if necessary
if (mConfig.debugModeEnabled) {
- for (int i = childCount - 1; i >= 0; i--) {
- View v = getChildAt(i);
- if (v != mSearchBar) {
- TaskStackView stackView = (TaskStackView) v;
- stackView.setDebugOverlay(mDebugOverlay);
- }
+ for (int i = mImmutableTaskStackViews.size() - 1; i >= 0; i--) {
+ TaskStackView stackView = mImmutableTaskStackViews.get(i);
+ stackView.setDebugOverlay(mDebugOverlay);
}
}
+ // Bring the debug view to the front
+ if (mMultiStackDebugView != null) {
+ mMultiStackDebugView.bringToFront();
+ }
+
// Trigger a new layout
requestLayout();
}
+ /** Gets the list of task views */
+ List<TaskStackView> getTaskStackViews() {
+ return mImmutableTaskStackViews;
+ }
+
/** Launches the focused task from the first stack if possible */
public boolean launchFocusedTask() {
// Get the first stack view
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child != mSearchBar) {
- TaskStackView stackView = (TaskStackView) child;
- TaskStack stack = stackView.mStack;
- // Iterate the stack views and try and find the focused task
- List<TaskView> taskViews = stackView.getTaskViews();
- int taskViewCount = taskViews.size();
- for (int j = 0; j < taskViewCount; j++) {
- TaskView tv = taskViews.get(j);
- Task task = tv.getTask();
- if (tv.isFocusedTask()) {
- onTaskViewClicked(stackView, tv, stack, task, false);
- return true;
- }
+ List<TaskStackView> stackViews = getTaskStackViews();
+ int stackCount = stackViews.size();
+ for (int i = 0; i < stackCount; i++) {
+ TaskStackView stackView = stackViews.get(i);
+ TaskStack stack = stackView.getStack();
+ // Iterate the stack views and try and find the focused task
+ List<TaskView> taskViews = stackView.getTaskViews();
+ int taskViewCount = taskViews.size();
+ for (int j = 0; j < taskViewCount; j++) {
+ TaskView tv = taskViews.get(j);
+ Task task = tv.getTask();
+ if (tv.isFocusedTask()) {
+ onTaskViewClicked(stackView, tv, stack, task, false);
+ return true;
}
}
}
@@ -178,24 +217,22 @@
/** Launches the task that Recents was launched from, if possible */
public boolean launchPreviousTask() {
// Get the first stack view
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child != mSearchBar) {
- TaskStackView stackView = (TaskStackView) child;
- TaskStack stack = stackView.mStack;
- ArrayList<Task> tasks = stack.getTasks();
+ List<TaskStackView> stackViews = getTaskStackViews();
+ int stackCount = stackViews.size();
+ for (int i = 0; i < stackCount; i++) {
+ TaskStackView stackView = stackViews.get(i);
+ TaskStack stack = stackView.getStack();
+ ArrayList<Task> tasks = stack.getTasks();
- // Find the launch task in the stack
- if (!tasks.isEmpty()) {
- int taskCount = tasks.size();
- for (int j = 0; j < taskCount; j++) {
- if (tasks.get(j).isLaunchTarget) {
- Task task = tasks.get(j);
- TaskView tv = stackView.getChildViewForTask(task);
- onTaskViewClicked(stackView, tv, stack, task, false);
- return true;
- }
+ // Find the launch task in the stack
+ if (!tasks.isEmpty()) {
+ int taskCount = tasks.size();
+ for (int j = 0; j < taskCount; j++) {
+ if (tasks.get(j).isLaunchTarget) {
+ Task task = tasks.get(j);
+ TaskView tv = stackView.getChildViewForTask(task);
+ onTaskViewClicked(stackView, tv, stack, task, false);
+ return true;
}
}
}
@@ -209,13 +246,11 @@
// to ensure that it runs
ctx.postAnimationTrigger.increment();
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child != mSearchBar) {
- TaskStackView stackView = (TaskStackView) child;
- stackView.startEnterRecentsAnimation(ctx);
- }
+ List<TaskStackView> stackViews = getTaskStackViews();
+ int stackCount = stackViews.size();
+ for (int i = 0; i < stackCount; i++) {
+ TaskStackView stackView = stackViews.get(i);
+ stackView.startEnterRecentsAnimation(ctx);
}
ctx.postAnimationTrigger.decrement();
}
@@ -225,13 +260,11 @@
// We have to increment/decrement the post animation trigger in case there are no children
// to ensure that it runs
ctx.postAnimationTrigger.increment();
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child != mSearchBar) {
- TaskStackView stackView = (TaskStackView) child;
- stackView.startExitToHomeAnimation(ctx);
- }
+ List<TaskStackView> stackViews = getTaskStackViews();
+ int stackCount = stackViews.size();
+ for (int i = 0; i < stackCount; i++) {
+ TaskStackView stackView = stackViews.get(i);
+ stackView.startExitToHomeAnimation(ctx);
}
ctx.postAnimationTrigger.decrement();
@@ -287,22 +320,31 @@
}
Rect taskStackBounds = new Rect();
- mConfig.getTaskStackBounds(width, height, mConfig.systemInsets.top,
+ mConfig.getAvailableTaskStackBounds(width, height, mConfig.systemInsets.top,
mConfig.systemInsets.right, taskStackBounds);
- // Measure each TaskStackView with the full width and height of the window since the
+ // Measure each TaskStackView with the full width and height of the window since the
// transition view is a child of that stack view
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child != mSearchBar && child.getVisibility() != GONE) {
- TaskStackView tsv = (TaskStackView) child;
- // Set the insets to be the top/left inset + search bounds
- tsv.setStackInsetRect(taskStackBounds);
- tsv.measure(widthMeasureSpec, heightMeasureSpec);
+ List<TaskStackView> stackViews = getTaskStackViews();
+ List<Rect> stackViewsBounds = mLayoutAlgorithm.computeStackRects(stackViews,
+ taskStackBounds);
+ int stackCount = stackViews.size();
+ for (int i = 0; i < stackCount; i++) {
+ TaskStackView stackView = stackViews.get(i);
+ if (stackView.getVisibility() != GONE) {
+ // We are going to measure the TaskStackView with the whole RecentsView dimensions,
+ // but the actual stack is going to be inset to the bounds calculated by the layout
+ // algorithm
+ stackView.setStackInsetRect(stackViewsBounds.get(i));
+ stackView.measure(widthMeasureSpec, heightMeasureSpec);
}
}
+ // Measure the multistack debug view
+ if (mMultiStackDebugView != null) {
+ mMultiStackDebugView.measure(width, height);
+ }
+
setMeasuredDimension(width, height);
}
@@ -322,14 +364,27 @@
// Layout each TaskStackView with the full width and height of the window since the
// transition view is a child of that stack view
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child != mSearchBar && child.getVisibility() != GONE) {
- child.layout(left, top, left + child.getMeasuredWidth(),
- top + child.getMeasuredHeight());
+ List<TaskStackView> stackViews = getTaskStackViews();
+ int stackCount = stackViews.size();
+ for (int i = 0; i < stackCount; i++) {
+ TaskStackView stackView = stackViews.get(i);
+ if (stackView.getVisibility() != GONE) {
+ stackView.layout(left, top, left + stackView.getMeasuredWidth(),
+ top + stackView.getMeasuredHeight());
}
}
+
+ // Layout the multistack debug view
+ if (mMultiStackDebugView != null) {
+ Rect taskStackBounds = new Rect();
+ mConfig.getAvailableTaskStackBounds(getMeasuredWidth(), getMeasuredHeight(),
+ mConfig.systemInsets.top, mConfig.systemInsets.right, taskStackBounds);
+ mMultiStackDebugView.layout(left,
+ taskStackBounds.bottom - mConfig.systemInsets.bottom -
+ mMultiStackDebugView.getMeasuredHeight(),
+ left + mMultiStackDebugView.getMeasuredWidth(),
+ taskStackBounds.bottom - mConfig.systemInsets.bottom);
+ }
}
@Override
@@ -343,41 +398,29 @@
/** Notifies each task view of the user interaction. */
public void onUserInteraction() {
// Get the first stack view
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child != mSearchBar) {
- TaskStackView stackView = (TaskStackView) child;
- stackView.onUserInteraction();
- }
+ List<TaskStackView> stackViews = getTaskStackViews();
+ int stackCount = stackViews.size();
+ for (int i = 0; i < stackCount; i++) {
+ TaskStackView stackView = stackViews.get(i);
+ stackView.onUserInteraction();
}
}
/** Focuses the next task in the first stack view */
public void focusNextTask(boolean forward) {
// Get the first stack view
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child != mSearchBar) {
- TaskStackView stackView = (TaskStackView) child;
- stackView.focusNextTask(forward, true);
- break;
- }
+ List<TaskStackView> stackViews = getTaskStackViews();
+ if (!stackViews.isEmpty()) {
+ stackViews.get(0).focusNextTask(forward, true);
}
}
/** Dismisses the focused task. */
public void dismissFocusedTask() {
// Get the first stack view
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child != mSearchBar) {
- TaskStackView stackView = (TaskStackView) child;
- stackView.dismissFocusedTask();
- break;
- }
+ List<TaskStackView> stackViews = getTaskStackViews();
+ if (!stackViews.isEmpty()) {
+ stackViews.get(0).dismissFocusedTask();
}
}
@@ -476,9 +519,16 @@
}
};
}
- opts = ActivityOptions.makeThumbnailAspectScaleUpAnimation(sourceView,
- b, offsetX, offsetY, transform.rect.width(), transform.rect.height(),
- sourceView.getHandler(), animStartedListener);
+ if (mConfig.multiStackEnabled) {
+ opts = ActivityOptions.makeCustomAnimation(sourceView.getContext(),
+ R.anim.recents_from_unknown_enter,
+ R.anim.recents_from_unknown_exit,
+ sourceView.getHandler(), animStartedListener);
+ } else {
+ opts = ActivityOptions.makeThumbnailAspectScaleUpAnimation(sourceView,
+ b, offsetX, offsetY, transform.rect.width(), transform.rect.height(),
+ sourceView.getHandler(), animStartedListener);
+ }
}
final ActivityOptions launchOpts = opts;
@@ -561,13 +611,11 @@
/** Final callback after Recents is finally hidden. */
public void onRecentsHidden() {
// Notify each task stack view
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child != mSearchBar) {
- TaskStackView stackView = (TaskStackView) child;
- stackView.onRecentsHidden();
- }
+ List<TaskStackView> stackViews = getTaskStackViews();
+ int stackCount = stackViews.size();
+ for (int i = 0; i < stackCount; i++) {
+ TaskStackView stackView = stackViews.get(i);
+ stackView.onRecentsHidden();
}
}
@@ -599,18 +647,23 @@
}
}
+ @Override
+ public void onMultiStackMoveTask(Task t) {
+ if (mCb != null) {
+ mCb.onMultiStackMoveTask(t);
+ }
+ }
+
/**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
@Override
public void onPackagesChanged(RecentsPackageMonitor monitor, String packageName, int userId) {
// Propagate this event down to each task stack view
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = getChildAt(i);
- if (child != mSearchBar) {
- TaskStackView stackView = (TaskStackView) child;
- stackView.onPackagesChanged(monitor, packageName, userId);
- }
+ List<TaskStackView> stackViews = getTaskStackViews();
+ int stackCount = stackViews.size();
+ for (int i = 0; i < stackCount; i++) {
+ TaskStackView stackView = stackViews.get(i);
+ stackView.onPackagesChanged(monitor, packageName, userId);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewLayoutAlgorithm.java
new file mode 100644
index 0000000..eea273c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewLayoutAlgorithm.java
@@ -0,0 +1,59 @@
+/*
+ * 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.recents.views;
+
+import android.graphics.Rect;
+import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.model.TaskStack;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/* The layout logic for the RecentsView. */
+public class RecentsViewLayoutAlgorithm {
+
+ RecentsConfiguration mConfig;
+
+ public RecentsViewLayoutAlgorithm(RecentsConfiguration config) {
+ mConfig = config;
+ }
+
+ /** Return the relative coordinate given coordinates in another size. */
+ private int getRelativeCoordinate(int availableOffset, int availableSize, int otherCoord, int otherSize) {
+ float relPos = (float) otherCoord / otherSize;
+ return availableOffset + (int) (relPos * availableSize);
+ }
+
+ /**
+ * Computes and returns the bounds that each of the stack views should take up.
+ */
+ List<Rect> computeStackRects(List<TaskStackView> stackViews, Rect availableBounds) {
+ ArrayList<Rect> bounds = new ArrayList<Rect>(stackViews.size());
+ int stackViewsCount = stackViews.size();
+ for (int i = 0; i < stackViewsCount; i++) {
+ TaskStack stack = stackViews.get(i).getStack();
+ Rect sb = stack.stackBounds;
+ Rect db = stack.displayBounds;
+ Rect ab = availableBounds;
+ bounds.add(new Rect(getRelativeCoordinate(ab.left, ab.width(), sb.left, db.width()),
+ getRelativeCoordinate(ab.top, ab.height(), sb.top, db.height()),
+ getRelativeCoordinate(ab.left, ab.width(), sb.right, db.width()),
+ getRelativeCoordinate(ab.top, ab.height(), sb.bottom, db.height())));
+ }
+ return bounds;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 290792a..2318319 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -60,6 +60,8 @@
public void onAllTaskViewsDismissed(ArrayList<Task> removedTasks);
public void onTaskStackFilterTriggered();
public void onTaskStackUnfilterTriggered();
+
+ public void onMultiStackMoveTask(Task t);
}
RecentsConfiguration mConfig;
@@ -149,6 +151,11 @@
requestLayout();
}
+ /** Returns the task stack. */
+ TaskStack getStack() {
+ return mStack;
+ }
+
/** Sets the debug overlay */
public void setDebugOverlay(DebugOverlayView overlay) {
mDebugOverlay = overlay;
@@ -625,6 +632,11 @@
return mTouchHandler.onGenericMotionEvent(ev);
}
+ /** Returns the region that touch gestures can be started in. */
+ Rect getTouchableRegion() {
+ return mTaskStackBounds;
+ }
+
@Override
public void computeScroll() {
mStackScroller.computeScroll();
@@ -1326,6 +1338,13 @@
}
}
+ @Override
+ public void onMultiStackMoveTask(TaskView tv) {
+ if (mCb != null) {
+ mCb.onMultiStackMoveTask(tv.getTask());
+ }
+ }
+
/**** TaskStackViewScroller.TaskStackViewScrollerCallbacks ****/
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index ccad2f1..fabc86d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -96,16 +96,6 @@
}
return false;
}
- /** Bounds the current scroll if necessary, but does not synchronize the stack view with the model. */
- public boolean boundScrollRaw() {
- float curScroll = getStackScroll();
- float newScroll = getBoundedStackScroll(curScroll);
- if (Float.compare(newScroll, curScroll) != 0) {
- setStackScrollRaw(newScroll);
- return true;
- }
- return false;
- }
/** Returns the bounded stack scroll */
float getBoundedStackScroll(float scroll) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 4a6112c..6cdddc5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -123,6 +123,16 @@
return false;
}
+ int action = ev.getAction();
+ if (mConfig.multiStackEnabled) {
+ // Check if we are within the bounds of the stack view contents
+ if ((action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
+ if (!mSv.getTouchableRegion().contains((int) ev.getX(), (int) ev.getY())) {
+ return false;
+ }
+ }
+ }
+
// Pass through to swipe helper if we are swiping
mInterceptedBySwipeHelper = mSwipeHelper.onInterceptTouchEvent(ev);
if (mInterceptedBySwipeHelper) {
@@ -131,7 +141,6 @@
boolean wasScrolling = mScroller.isScrolling() ||
(mScroller.mScrollAnimator != null && mScroller.mScrollAnimator.isRunning());
- int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
// Save the touch down info
@@ -198,6 +207,16 @@
return false;
}
+ int action = ev.getAction();
+ if (mConfig.multiStackEnabled) {
+ // Check if we are within the bounds of the stack view contents
+ if ((action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
+ if (!mSv.getTouchableRegion().contains((int) ev.getX(), (int) ev.getY())) {
+ return false;
+ }
+ }
+ }
+
// Pass through to swipe helper if we are swiping
if (mInterceptedBySwipeHelper && mSwipeHelper.onTouchEvent(ev)) {
return true;
@@ -206,7 +225,6 @@
// Update the velocity tracker
initVelocityTrackerIfNotExists();
- int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
// Save the touch down info
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 82120bf..098f2f9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -45,6 +45,8 @@
public void onTaskViewDismissed(TaskView tv);
public void onTaskViewClipStateChanged(TaskView tv);
public void onTaskViewFocusChanged(TaskView tv, boolean focused);
+
+ public void onMultiStackMoveTask(TaskView tv);
}
RecentsConfiguration mConfig;
@@ -457,6 +459,11 @@
.start();
}
+ /** Enables/disables handling touch on this task view. */
+ void setTouchEnabled(boolean enabled) {
+ setOnClickListener(enabled ? this : null);
+ }
+
/** Animates this task view if the user does not interact with the stack after a certain time. */
void startNoUserInteractionAnimation() {
mHeaderView.startNoUserInteractionAnimation();
@@ -667,6 +674,9 @@
// Rebind any listeners
mHeaderView.mApplicationIcon.setOnClickListener(this);
mHeaderView.mDismissButton.setOnClickListener(this);
+ if (mConfig.multiStackEnabled) {
+ mHeaderView.mMoveTaskButton.setOnClickListener(this);
+ }
mActionButtonView.setOnClickListener(this);
if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
if (mConfig.developerOptionsEnabled) {
@@ -687,6 +697,9 @@
// Unbind any listeners
mHeaderView.mApplicationIcon.setOnClickListener(null);
mHeaderView.mDismissButton.setOnClickListener(null);
+ if (mConfig.multiStackEnabled) {
+ mHeaderView.mMoveTaskButton.setOnClickListener(null);
+ }
mActionButtonView.setOnClickListener(null);
if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
mHeaderView.mApplicationIcon.setOnLongClickListener(null);
@@ -695,9 +708,9 @@
mTaskDataLoaded = false;
}
- /** Enables/disables handling touch on this task view. */
- void setTouchEnabled(boolean enabled) {
- setOnClickListener(enabled ? this : null);
+ @Override
+ public void onMultiStackDebugTaskStackIdChanged() {
+ mHeaderView.rebindToTask(mTask);
}
/**** View.OnClickListener Implementation ****/
@@ -717,6 +730,10 @@
}
} else if (v == mHeaderView.mDismissButton) {
dismissTask();
+ } else if (v == mHeaderView.mMoveTaskButton) {
+ if (mCb != null) {
+ mCb.onMultiStackMoveTask(tv);
+ }
}
}
}, 125);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index e13eed8..b827acc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -56,6 +56,7 @@
RecentsConfiguration mConfig;
// Header views
+ ImageView mMoveTaskButton;
ImageView mDismissButton;
ImageView mApplicationIcon;
TextView mActivityDescription;
@@ -126,6 +127,10 @@
mApplicationIcon = (ImageView) findViewById(R.id.application_icon);
mActivityDescription = (TextView) findViewById(R.id.activity_description);
mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
+ mMoveTaskButton = (ImageView) findViewById(R.id.move_task);
+ if (mConfig.multiStackEnabled) {
+ mMoveTaskButton.setVisibility(View.VISIBLE);
+ }
// Hide the backgrounds if they are ripple drawables
if (!Constants.DebugFlags.App.EnableTaskFiltering) {
@@ -188,7 +193,10 @@
mApplicationIcon.setImageDrawable(t.applicationIcon);
}
mApplicationIcon.setContentDescription(t.activityLabel);
- if (!mActivityDescription.getText().toString().equals(t.activityLabel)) {
+ // Always update when multi stack debugging is enabled as the stack id can change
+ if (mConfig.multiStackEnabled) {
+ mActivityDescription.setText("[" + t.key.stackId + "] " + t.activityLabel);
+ } else if (!mActivityDescription.getText().toString().equals(t.activityLabel)) {
mActivityDescription.setText(t.activityLabel);
}
// Try and apply the system ui tint
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index a2ff64a..747e702 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -148,7 +148,8 @@
private boolean mBlockTouches;
private int mNotificationScrimWaitDistance;
- private boolean mTwoFingerQsExpand;
+ // Used for two finger gesture as well as accessibility shortcut to QS.
+ private boolean mQsExpandImmediate;
private boolean mTwoFingerQsExpandPossible;
/**
@@ -475,6 +476,13 @@
}
}
+ public void expandWithQs() {
+ if (mQsExpansionEnabled) {
+ mQsExpandImmediate = true;
+ }
+ expand();
+ }
+
@Override
public void fling(float vel, boolean expand) {
GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder();
@@ -658,7 +666,7 @@
if (mExpandedHeight != 0) {
handleQsDown(event);
}
- if (!mTwoFingerQsExpand && mQsTracking) {
+ if (!mQsExpandImmediate && mQsTracking) {
onQsTouch(event);
if (!mConflictingQsExpansionGesture) {
return true;
@@ -675,7 +683,7 @@
if (mTwoFingerQsExpandPossible && event.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN
&& event.getPointerCount() == 2
&& event.getY(event.getActionIndex()) < mStatusBarMinHeight) {
- mTwoFingerQsExpand = true;
+ mQsExpandImmediate = true;
requestPanelHeightUpdate();
// Normally, we start listening when the panel is expanded, but here we need to start
@@ -1166,7 +1174,7 @@
private float calculateQsTopPadding() {
if (mKeyguardShowing
- && (mTwoFingerQsExpand || mIsExpanding && mQsExpandedWhenExpandingStarted)) {
+ && (mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted)) {
// Either QS pushes the notifications down when fully expanded, or QS is fully above the
// notifications (mostly on tablets). maxNotifications denotes the normal top padding
@@ -1200,7 +1208,7 @@
mScrollView.getScrollY(),
mAnimateNextTopPaddingChange || animate,
mKeyguardShowing
- && (mTwoFingerQsExpand || mIsExpanding && mQsExpandedWhenExpandingStarted));
+ && (mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted));
mAnimateNextTopPaddingChange = false;
}
@@ -1313,7 +1321,7 @@
min = Math.max(min, minHeight);
}
int maxHeight;
- if (mTwoFingerQsExpand || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted) {
+ if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted) {
maxHeight = calculatePanelHeightQsExpanded();
} else {
maxHeight = calculatePanelHeightShade();
@@ -1328,10 +1336,10 @@
@Override
protected void onHeightUpdated(float expandedHeight) {
- if (!mQsExpanded || mTwoFingerQsExpand || mIsExpanding && mQsExpandedWhenExpandingStarted) {
+ if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) {
positionClockAndNotifications();
}
- if (mTwoFingerQsExpand || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null
+ if (mQsExpandImmediate || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null
&& !mQsExpansionFromOverscroll) {
float t;
if (mKeyguardShowing) {
@@ -1555,7 +1563,7 @@
} else {
setListening(true);
}
- mTwoFingerQsExpand = false;
+ mQsExpandImmediate = false;
mTwoFingerQsExpandPossible = false;
}
@@ -1573,7 +1581,7 @@
@Override
protected void setOverExpansion(float overExpansion, boolean isPixels) {
- if (mConflictingQsExpansionGesture || mTwoFingerQsExpand) {
+ if (mConflictingQsExpansionGesture || mQsExpandImmediate) {
return;
}
if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
@@ -1593,7 +1601,7 @@
protected void onTrackingStarted() {
super.onTrackingStarted();
if (mQsFullyExpanded) {
- mTwoFingerQsExpand = true;
+ mQsExpandImmediate = true;
}
if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
|| mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
@@ -1817,7 +1825,7 @@
@Override
protected boolean fullyExpandedClearAllVisible() {
return mNotificationStackScroller.isDismissViewNotGone()
- && mNotificationStackScroller.isScrolledToBottom() && !mTwoFingerQsExpand;
+ && mNotificationStackScroller.isScrolledToBottom() && !mQsExpandImmediate;
}
@Override
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 25a2f93..d286441 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -2206,8 +2206,7 @@
// Settings are not available in setup
if (!mUserSetup) return;
- mNotificationPanel.expand();
- mNotificationPanel.openQs();
+ mNotificationPanel.expandWithQs();
if (false) postStartTracing();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index ac5602c..e8023bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -57,7 +57,6 @@
// Since some pieces of the phone state are interdependent we store it locally,
// this could potentially become part of MobileState for simplification/complication
// of code.
- private IccCardConstants.State mSimState = IccCardConstants.State.READY;
private int mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
private int mDataState = TelephonyManager.DATA_DISCONNECTED;
private ServiceState mServiceState;
@@ -146,11 +145,6 @@
return getIcons().mDataContentDescription;
}
- @VisibleForTesting
- protected IccCardConstants.State getSimState() {
- return mSimState;
- }
-
public void setAirplaneMode(boolean airplaneMode) {
mCurrentState.airplaneMode = airplaneMode;
notifyListenersIfNecessary();
@@ -239,10 +233,13 @@
String contentDescription = getStringIfExists(getContentDescription());
String dataContentDescription = getStringIfExists(icons.mDataContentDescription);
+
+ boolean showDataIcon = mCurrentState.dataConnected && mCurrentState.inetForNetwork != 0
+ || mCurrentState.iconGroup == TelephonyIcons.ROAMING;
+
// Only send data sim callbacks to QS.
if (mCurrentState.dataSim) {
- int qsTypeIcon = mCurrentState.dataConnected ?
- icons.mQsDataType[mCurrentState.inetForNetwork] : 0;
+ int qsTypeIcon = showDataIcon ? icons.mQsDataType[mCurrentState.inetForNetwork] : 0;
int length = mSignalsChangedCallbacks.size();
for (int i = 0; i < length; i++) {
mSignalsChangedCallbacks.get(i).onMobileDataSignalChanged(mCurrentState.enabled
@@ -257,8 +254,6 @@
icons.mIsWide && qsTypeIcon != 0);
}
}
- boolean showDataIcon = mCurrentState.dataConnected && mCurrentState.inetForNetwork != 0
- || mCurrentState.iconGroup == TelephonyIcons.ROAMING;
int typeIcon = showDataIcon ? icons.mDataType : 0;
int signalClustersLength = mSignalClusters.size();
for (int i = 0; i < signalClustersLength; i++) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 9b95d5c..260dea0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -69,6 +69,8 @@
protected TelephonyManager mMockTm;
protected Config mConfig;
+ protected int mSubId;
+
private NetworkCapabilities mNetCapabilities;
@Override
@@ -100,13 +102,13 @@
protected void setupNetworkController() {
// For now just pretend to be the data sim, so we can test that too.
- final int subId = SubscriptionManager.getDefaultDataSubId();
+ mSubId = SubscriptionManager.getDefaultDataSubId();
SubscriptionInfo subscription = mock(SubscriptionInfo.class);
List<SubscriptionInfo> subs = new ArrayList<SubscriptionInfo>();
- when(subscription.getSubscriptionId()).thenReturn(subId);
+ when(subscription.getSubscriptionId()).thenReturn(mSubId);
subs.add(subscription);
mNetworkController.setCurrentSubscriptions(subs);
- mMobileSignalController = mNetworkController.mMobileSignalControllers.get(subId);
+ mMobileSignalController = mNetworkController.mMobileSignalControllers.get(mSubId);
mPhoneStateListener = mMobileSignalController.mPhoneStateListener;
mSignalCluster = mock(SignalCluster.class);
mNetworkSignalChangedCallback = mock(NetworkSignalChangedCallback.class);
@@ -291,10 +293,6 @@
assertEquals("Visibility in status bar", visible, (boolean) visibleArg.getValue());
}
- protected void assertSimStateEquals(IccCardConstants.State expected) {
- assertEquals("Sim state", expected, mMobileSignalController.getSimState());
- }
-
protected void assertNetworkNameEquals(String expected) {
assertEquals("Network name", expected, mNetworkController.getMobileNetworkName());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index 65b0971..7fa3f68 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -25,6 +25,7 @@
import android.telephony.TelephonyManager;
import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyIntents;
import com.android.systemui.R;
@@ -254,16 +255,6 @@
setCdmaRoaming(false);
}
- public void testOnReceive_updateSimState_noSim() {
- Intent intent = new Intent();
- intent.setAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, IccCardConstants.INTENT_VALUE_ICC_ABSENT);
-
- mNetworkController.onReceive(mContext, intent);
-
- assertSimStateEquals(IccCardConstants.State.ABSENT);
- }
-
public void testOnReceive_stringsUpdatedAction_spn() {
String expectedMNetworkName = "Test";
Intent intent = createStringsUpdatedIntent(true /* showSpn */,
@@ -344,6 +335,7 @@
intent.putExtra(TelephonyIntents.EXTRA_SHOW_PLMN, showPlmn);
intent.putExtra(TelephonyIntents.EXTRA_PLMN, plmn);
+ intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, mSubId);
return intent;
}
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index ed43d8e..1aa7366 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -894,7 +894,10 @@
}
void doInvalidatePanelMenu(int featureId) {
- PanelFeatureState st = getPanelState(featureId, true);
+ PanelFeatureState st = getPanelState(featureId, false);
+ if (st == null) {
+ return;
+ }
Bundle savedActionViewStates = null;
if (st.menu != null) {
savedActionViewStates = new Bundle();
@@ -933,8 +936,8 @@
// The panel key was pushed, so set the chording key
mPanelChordingKey = keyCode;
- PanelFeatureState st = getPanelState(featureId, true);
- if (!st.isOpen) {
+ PanelFeatureState st = getPanelState(featureId, false);
+ if (st != null && !st.isOpen) {
return preparePanel(st, event);
}
}
@@ -952,12 +955,14 @@
if (mPanelChordingKey != 0) {
mPanelChordingKey = 0;
- if (event.isCanceled() || (mDecor != null && mDecor.mActionMode != null)) {
+ final PanelFeatureState st = getPanelState(featureId, false);
+
+ if (event.isCanceled() || (mDecor != null && mDecor.mActionMode != null) ||
+ (st == null)) {
return;
}
boolean playSoundEffect = false;
- final PanelFeatureState st = getPanelState(featureId, true);
if (featureId == FEATURE_OPTIONS_PANEL && mDecorContentParent != null &&
mDecorContentParent.canShowOverflowMenu() &&
!ViewConfiguration.get(getContext()).hasPermanentMenuKey()) {
@@ -1056,7 +1061,7 @@
@Override
public boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, int flags) {
- return performPanelShortcut(getPanelState(featureId, true), keyCode, event, flags);
+ return performPanelShortcut(getPanelState(featureId, false), keyCode, event, flags);
}
private boolean performPanelShortcut(PanelFeatureState st, int keyCode, KeyEvent event,
@@ -1149,11 +1154,11 @@
mInvalidatePanelMenuRunnable.run();
}
- final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
+ final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
// If we don't have a menu or we're waiting for a full content refresh,
// forget it. This is a lingering event that no longer matters.
- if (st.menu != null && !st.refreshMenuContent &&
+ if (st != null && st.menu != null && !st.refreshMenuContent &&
cb.onPreparePanel(FEATURE_OPTIONS_PANEL, st.createdPanelView, st.menu)) {
cb.onMenuOpened(FEATURE_ACTION_BAR, st.menu);
mDecorContentParent.showOverflowMenu();
@@ -1161,15 +1166,19 @@
}
} else {
mDecorContentParent.hideOverflowMenu();
- if (cb != null && !isDestroyed()) {
- final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
+ final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
+ if (st != null && cb != null && !isDestroyed()) {
cb.onPanelClosed(FEATURE_ACTION_BAR, st.menu);
}
}
return;
}
- PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
+ PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
+
+ if (st == null) {
+ return;
+ }
// Save the future expanded mode state since closePanel will reset it
boolean newExpandedMode = toggleMenuMode ? !st.isInExpandedMode : st.isInExpandedMode;
@@ -2302,8 +2311,8 @@
// combination such as Control+C. Temporarily prepare the panel then mark it
// unprepared again when finished to ensure that the panel will again be prepared
// the next time it is shown for real.
- if (mPreparedPanel == null) {
- PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
+ PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
+ if (st != null && mPreparedPanel == null) {
preparePanel(st, ev);
handled = performPanelShortcut(st, ev.getKeyCode(), ev,
Menu.FLAG_PERFORM_NO_CLOSE);
@@ -3156,7 +3165,7 @@
// If the user is chording a menu shortcut, release the chord since
// this window lost focus
- if (!hasWindowFocus && mPanelChordingKey != 0) {
+ if (hasFeature(FEATURE_OPTIONS_PANEL) && !hasWindowFocus && mPanelChordingKey != 0) {
closePanel(FEATURE_OPTIONS_PANEL);
}
@@ -3906,8 +3915,8 @@
@Override
public boolean isShortcutKey(int keyCode, KeyEvent event) {
- PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
- return st.menu != null && st.menu.isShortcutKey(keyCode, event);
+ PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
+ return st != null && st.menu != null && st.menu.isShortcutKey(keyCode, event);
}
private void updateDrawable(int featureId, DrawableFeatureState st, boolean fromResume) {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 7124e5b..d4032cc 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -1723,7 +1723,6 @@
case TYPE_STATUS_BAR_PANEL:
case TYPE_STATUS_BAR_SUB_PANEL:
case TYPE_SYSTEM_DIALOG:
- case TYPE_UNIVERSE_BACKGROUND:
case TYPE_VOLUME_OVERLAY:
case TYPE_PRIVATE_PRESENTATION:
break;
@@ -1822,8 +1821,6 @@
return 2;
}
switch (type) {
- case TYPE_UNIVERSE_BACKGROUND:
- return 1;
case TYPE_PRIVATE_PRESENTATION:
return 2;
case TYPE_WALLPAPER:
@@ -1935,11 +1932,6 @@
}
@Override
- public int getAboveUniverseLayer() {
- return windowTypeToLayerLw(TYPE_SYSTEM_ERROR);
- }
-
- @Override
public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation) {
if (mHasNavigationBar) {
// For a basic navigation bar, when we are in landscape mode we place
@@ -1997,7 +1989,6 @@
case TYPE_NAVIGATION_BAR:
case TYPE_WALLPAPER:
case TYPE_DREAM:
- case TYPE_UNIVERSE_BACKGROUND:
case TYPE_KEYGUARD_SCRIM:
return false;
default:
@@ -3769,8 +3760,7 @@
+ mOverscanScreenWidth;
pf.bottom = df.bottom = of.bottom = cf.bottom = mOverscanScreenTop
+ mOverscanScreenHeight;
- } else if (attrs.type == TYPE_BOOT_PROGRESS
- || attrs.type == TYPE_UNIVERSE_BACKGROUND) {
+ } else if (attrs.type == TYPE_BOOT_PROGRESS) {
// Boot progress screen always covers entire display.
pf.left = df.left = of.left = cf.left = mOverscanScreenLeft;
pf.top = df.top = of.top = cf.top = mOverscanScreenTop;
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index dced99a..06f4def 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -1382,6 +1382,14 @@
sc.zStart = limit_ptr[4];
sc.zEnd = limit_ptr[5];
sc.strategy = RS_FOR_EACH_STRATEGY_DONT_CARE;
+ sc.arrayStart = 0;
+ sc.arrayEnd = 0;
+ sc.array2Start = 0;
+ sc.array2End = 0;
+ sc.array3Start = 0;
+ sc.array3End = 0;
+ sc.array4Start = 0;
+ sc.array4End = 0;
sca = ≻
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 289152b..c1e4994 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -382,6 +382,7 @@
// we're now good to go, so start the backup alarms
if (MORE_DEBUG) Slog.d(TAG, "Now provisioned, so starting backups");
startBackupAlarmsLocked(FIRST_BACKUP_INTERVAL);
+ scheduleNextFullBackupJob();
}
}
}
@@ -3853,6 +3854,16 @@
PackageInfo currentPackage;
try {
+ if (!mEnabled || !mProvisioned) {
+ // Backups are globally disabled, so don't proceed.
+ if (DEBUG) {
+ Slog.i(TAG, "full backup requested but e=" + mEnabled
+ + " p=" + mProvisioned + "; ignoring");
+ }
+ mUpdateSchedule = false;
+ return;
+ }
+
IBackupTransport transport = getTransport(mCurrentTransport);
if (transport == null) {
Slog.w(TAG, "Transport not present; full data backup not performed");
@@ -4150,6 +4161,17 @@
long now = System.currentTimeMillis();
FullBackupEntry entry = null;
+ if (!mEnabled || !mProvisioned) {
+ // Backups are globally disabled, so don't proceed. We also don't reschedule
+ // the job driving automatic backups; that job will be scheduled again when
+ // the user enables backup.
+ if (MORE_DEBUG) {
+ Slog.i(TAG, "beginFullBackup but e=" + mEnabled
+ + " p=" + mProvisioned + "; ignoring");
+ }
+ return false;
+ }
+
if (DEBUG_SCHEDULING) {
Slog.i(TAG, "Beginning scheduled full backup operation");
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 9f8d665..059dde1 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -72,7 +72,7 @@
static final boolean DEBUG_DELAYED_SERVICE = ActivityManagerService.DEBUG_SERVICE;
static final boolean DEBUG_DELAYED_STARTS = DEBUG_DELAYED_SERVICE;
static final boolean DEBUG_MU = ActivityManagerService.DEBUG_MU;
- static final boolean LOG_SERVICE_START_STOP = true;
+ static final boolean LOG_SERVICE_START_STOP = false;
static final String TAG = ActivityManagerService.TAG;
static final String TAG_MU = ActivityManagerService.TAG_MU;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index df4b6d6..287cd6f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -87,6 +87,7 @@
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wm.AppTransition;
import com.android.server.wm.WindowManagerService;
+
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
@@ -1854,9 +1855,14 @@
synchronized (ActivityManagerService.this) {
if (DEBUG_PSS) Slog.d(TAG, "Collected native and kernel memory in "
+ (SystemClock.uptimeMillis()-start) + "ms");
- mProcessStats.addSysMemUsageLocked(memInfo.getCachedSizeKb(),
- memInfo.getFreeSizeKb(), memInfo.getZramTotalSizeKb(),
- memInfo.getKernelUsedSizeKb(), nativeTotalPss);
+ final long cachedKb = memInfo.getCachedSizeKb();
+ final long freeKb = memInfo.getFreeSizeKb();
+ final long zramKb = memInfo.getZramTotalSizeKb();
+ final long kernelKb = memInfo.getKernelUsedSizeKb();
+ EventLogTags.writeAmMeminfo(cachedKb*1024, freeKb*1024, zramKb*1024,
+ kernelKb*1024, nativeTotalPss*1024);
+ mProcessStats.addSysMemUsageLocked(cachedKb, freeKb, zramKb, kernelKb,
+ nativeTotalPss);
}
}
@@ -5617,7 +5623,10 @@
@Override
public void showBootMessage(final CharSequence msg, final boolean always) {
- enforceNotIsolatedCaller("showBootMessage");
+ if (Binder.getCallingUid() != Process.myUid()) {
+ // These days only the core system can call this, so apps can't get in
+ // the way of what we show about running them.
+ }
mWindowManager.showBootMessage(msg, always);
}
@@ -7053,7 +7062,6 @@
return;
}
- final IPackageManager pm = AppGlobals.getPackageManager();
final String authority = uri.getAuthority();
final ProviderInfo pi = getProviderInfoLocked(authority, userId);
if (pi == null) {
@@ -8162,12 +8170,14 @@
}
@Override
- public void resizeStack(int stackBoxId, Rect bounds) {
+ public void resizeStack(int stackId, Rect bounds) {
enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
- "resizeStackBox()");
+ "resizeStack()");
long ident = Binder.clearCallingIdentity();
try {
- mWindowManager.resizeStack(stackBoxId, bounds);
+ synchronized (this) {
+ mStackSupervisor.resizeStackLocked(stackId, bounds);
+ }
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -10034,7 +10044,7 @@
}
final boolean translucentChanged = r.changeWindowTranslucency(true);
if (translucentChanged) {
- r.task.stack.releaseBackgroundResources();
+ r.task.stack.releaseBackgroundResources(r);
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
}
mWindowManager.setAppFullscreen(token, true);
@@ -10061,7 +10071,7 @@
}
final boolean translucentChanged = r.changeWindowTranslucency(false);
if (translucentChanged) {
- r.task.stack.convertToTranslucent(r);
+ r.task.stack.convertActivityToTranslucent(r);
}
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
mWindowManager.setAppFullscreen(token, false);
@@ -10578,9 +10588,68 @@
}
}
+ final class PreBootContinuation extends IIntentReceiver.Stub {
+ final Intent intent;
+ final Runnable onFinishCallback;
+ final ArrayList<ComponentName> doneReceivers;
+ final List<ResolveInfo> ris;
+ final int[] users;
+ int lastRi = -1;
+ int curRi = 0;
+ int curUser = 0;
+
+ PreBootContinuation(Intent _intent, Runnable _onFinishCallback,
+ ArrayList<ComponentName> _doneReceivers, List<ResolveInfo> _ris, int[] _users) {
+ intent = _intent;
+ onFinishCallback = _onFinishCallback;
+ doneReceivers = _doneReceivers;
+ ris = _ris;
+ users = _users;
+ }
+
+ void go() {
+ if (lastRi != curRi) {
+ ActivityInfo ai = ris.get(curRi).activityInfo;
+ ComponentName comp = new ComponentName(ai.packageName, ai.name);
+ intent.setComponent(comp);
+ doneReceivers.add(comp);
+ lastRi = curRi;
+ CharSequence label = ai.loadLabel(mContext.getPackageManager());
+ showBootMessage(mContext.getString(R.string.android_preparing_apk, label), false);
+ }
+ Slog.i(TAG, "Pre-boot of " + intent.getComponent().toShortString()
+ + " for user " + users[curUser]);
+ EventLogTags.writeAmPreBoot(users[curUser], intent.getComponent().getPackageName());
+ broadcastIntentLocked(null, null, intent, null, this,
+ 0, null, null, null, AppOpsManager.OP_NONE,
+ true, false, MY_PID, Process.SYSTEM_UID,
+ users[curUser]);
+ }
+
+ public void performReceive(Intent intent, int resultCode,
+ String data, Bundle extras, boolean ordered,
+ boolean sticky, int sendingUser) {
+ curUser++;
+ if (curUser >= users.length) {
+ curUser = 0;
+ curRi++;
+ if (curRi >= ris.size()) {
+ // All done sending broadcasts!
+ if (onFinishCallback != null) {
+ // The raw IIntentReceiver interface is called
+ // with the AM lock held, so redispatch to
+ // execute our code without the lock.
+ mHandler.post(onFinishCallback);
+ }
+ return;
+ }
+ }
+ go();
+ }
+ }
+
private boolean deliverPreBootCompleted(final Runnable onFinishCallback,
ArrayList<ComponentName> doneReceivers, int userId) {
- boolean waitingUpdate = false;
Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
List<ResolveInfo> ris = null;
try {
@@ -10588,71 +10657,51 @@
intent, null, 0, userId);
} catch (RemoteException e) {
}
- if (ris != null) {
- for (int i=ris.size()-1; i>=0; i--) {
- if ((ris.get(i).activityInfo.applicationInfo.flags
- &ApplicationInfo.FLAG_SYSTEM) == 0) {
- ris.remove(i);
- }
+ if (ris == null) {
+ return false;
+ }
+ for (int i=ris.size()-1; i>=0; i--) {
+ if ((ris.get(i).activityInfo.applicationInfo.flags
+ &ApplicationInfo.FLAG_SYSTEM) == 0) {
+ ris.remove(i);
}
- intent.addFlags(Intent.FLAG_RECEIVER_BOOT_UPGRADE);
+ }
+ intent.addFlags(Intent.FLAG_RECEIVER_BOOT_UPGRADE);
- // For User 0, load the version number. When delivering to a new user, deliver
- // to all receivers.
- if (userId == UserHandle.USER_OWNER) {
- ArrayList<ComponentName> lastDoneReceivers = readLastDonePreBootReceivers();
- for (int i=0; i<ris.size(); i++) {
- ActivityInfo ai = ris.get(i).activityInfo;
- ComponentName comp = new ComponentName(ai.packageName, ai.name);
- if (lastDoneReceivers.contains(comp)) {
- // We already did the pre boot receiver for this app with the current
- // platform version, so don't do it again...
- ris.remove(i);
- i--;
- // ...however, do keep it as one that has been done, so we don't
- // forget about it when rewriting the file of last done receivers.
- doneReceivers.add(comp);
- }
- }
- }
-
- // If primary user, send broadcast to all available users, else just to userId
- final int[] users = userId == UserHandle.USER_OWNER ? getUsersLocked()
- : new int[] { userId };
- for (int i = 0; i < ris.size(); i++) {
+ // For User 0, load the version number. When delivering to a new user, deliver
+ // to all receivers.
+ if (userId == UserHandle.USER_OWNER) {
+ ArrayList<ComponentName> lastDoneReceivers = readLastDonePreBootReceivers();
+ for (int i=0; i<ris.size(); i++) {
ActivityInfo ai = ris.get(i).activityInfo;
ComponentName comp = new ComponentName(ai.packageName, ai.name);
- doneReceivers.add(comp);
- intent.setComponent(comp);
- for (int j=0; j<users.length; j++) {
- IIntentReceiver finisher = null;
- // On last receiver and user, set up a completion callback
- if (i == ris.size() - 1 && j == users.length - 1 && onFinishCallback != null) {
- finisher = new IIntentReceiver.Stub() {
- public void performReceive(Intent intent, int resultCode,
- String data, Bundle extras, boolean ordered,
- boolean sticky, int sendingUser) {
- // The raw IIntentReceiver interface is called
- // with the AM lock held, so redispatch to
- // execute our code without the lock.
- mHandler.post(onFinishCallback);
- }
- };
- }
- Slog.i(TAG, "Sending system update to " + intent.getComponent()
- + " for user " + users[j]);
- broadcastIntentLocked(null, null, intent, null, finisher,
- 0, null, null, null, AppOpsManager.OP_NONE,
- true, false, MY_PID, Process.SYSTEM_UID,
- users[j]);
- if (finisher != null) {
- waitingUpdate = true;
- }
+ if (false && lastDoneReceivers.contains(comp)) {
+ // We already did the pre boot receiver for this app with the current
+ // platform version, so don't do it again...
+ ris.remove(i);
+ i--;
+ // ...however, do keep it as one that has been done, so we don't
+ // forget about it when rewriting the file of last done receivers.
+ doneReceivers.add(comp);
}
}
}
- return waitingUpdate;
+ if (ris.size() <= 0) {
+ return false;
+ }
+
+ // If primary user, send broadcast to all available users, else just to userId
+ final int[] users = userId == UserHandle.USER_OWNER ? getUsersLocked()
+ : new int[] { userId };
+ if (users.length <= 0) {
+ return false;
+ }
+
+ PreBootContinuation cont = new PreBootContinuation(intent, onFinishCallback, doneReceivers,
+ ris, users);
+ cont.go();
+ return true;
}
public void systemReady(final Runnable goingCallback) {
@@ -10687,10 +10736,10 @@
synchronized (ActivityManagerService.this) {
mDidUpdate = true;
}
- writeLastDonePreBootReceivers(doneReceivers);
showBootMessage(mContext.getText(
R.string.android_upgrading_complete),
false);
+ writeLastDonePreBootReceivers(doneReceivers);
systemReady(goingCallback);
}
}, doneReceivers, UserHandle.USER_OWNER);
@@ -13858,9 +13907,14 @@
memInfo.readMemInfo();
if (nativeProcTotalPss > 0) {
synchronized (this) {
- mProcessStats.addSysMemUsageLocked(memInfo.getCachedSizeKb(),
- memInfo.getFreeSizeKb(), memInfo.getZramTotalSizeKb(),
- memInfo.getKernelUsedSizeKb(), nativeProcTotalPss);
+ final long cachedKb = memInfo.getCachedSizeKb();
+ final long freeKb = memInfo.getFreeSizeKb();
+ final long zramKb = memInfo.getZramTotalSizeKb();
+ final long kernelKb = memInfo.getKernelUsedSizeKb();
+ EventLogTags.writeAmMeminfo(cachedKb*1024, freeKb*1024, zramKb*1024,
+ kernelKb*1024, nativeProcTotalPss*1024);
+ mProcessStats.addSysMemUsageLocked(cachedKb, freeKb, zramKb, kernelKb,
+ nativeProcTotalPss);
}
}
if (!brief) {
@@ -15739,14 +15793,14 @@
callerPackage, callingPid, callingUid, resolvedType,
requiredPermission, appOp, receivers, resultTo, resultCode,
resultData, map, ordered, sticky, false, userId);
+
if (DEBUG_BROADCAST) Slog.v(
TAG, "Enqueueing ordered broadcast " + r
+ ": prev had " + queue.mOrderedBroadcasts.size());
- if (DEBUG_BROADCAST) {
- int seq = r.intent.getIntExtra("seq", -1);
- Slog.i(TAG, "Enqueueing broadcast " + r.intent.getAction() + " seq=" + seq);
- }
- boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
+ if (DEBUG_BROADCAST) Slog.i(
+ TAG, "Enqueueing broadcast " + r.intent.getAction());
+
+ boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
if (!replaced) {
queue.enqueueOrderedBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
@@ -16077,6 +16131,15 @@
return mStackSupervisor.getFocusedStack();
}
+ @Override
+ public int getFocusedStackId() throws RemoteException {
+ ActivityStack focusedStack = getFocusedStack();
+ if (focusedStack != null) {
+ return focusedStack.getStackId();
+ }
+ return -1;
+ }
+
public Configuration getConfiguration() {
Configuration ci;
synchronized(this) {
@@ -17054,6 +17117,7 @@
* Record new PSS sample for a process.
*/
void recordPssSample(ProcessRecord proc, int procState, long pss, long uss, long now) {
+ EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss*1024, uss*1024);
proc.lastPssTime = now;
proc.baseProcessTracker.addPss(pss, uss, true, proc.pkgList);
if (DEBUG_PSS) Slog.d(TAG, "PSS of " + proc.toShortString()
@@ -18307,8 +18371,8 @@
}
}
- private Set getProfileIdsLocked(int userId) {
- Set userIds = new HashSet<Integer>();
+ private Set<Integer> getProfileIdsLocked(int userId) {
+ Set<Integer> userIds = new HashSet<Integer>();
final List<UserInfo> profiles = getUserManagerLocked().getProfiles(
userId, false /* enabledOnly */);
for (UserInfo user : profiles) {
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 6573521..a3676f9 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -127,6 +127,10 @@
long pauseTime; // last time we started pausing the activity
long launchTickTime; // base time for launch tick messages
Configuration configuration; // configuration activity was last running in
+ // Overridden configuration by the activity stack
+ // WARNING: Reference points to {@link ActivityStack#mOverrideConfig}, so its internal state
+ // should never be altered directly.
+ Configuration stackConfigOverride;
CompatibilityInfo compat;// last used compatibility mode
ActivityRecord resultTo; // who started this entry, so will get our reply
final String resultWho; // additional identifier for use by resultTo.
@@ -204,6 +208,7 @@
pw.print(" icon=0x"); pw.print(Integer.toHexString(icon));
pw.print(" theme=0x"); pw.println(Integer.toHexString(theme));
pw.print(prefix); pw.print("config="); pw.println(configuration);
+ pw.print(prefix); pw.print("stackConfigOverride="); pw.println(stackConfigOverride);
if (resultTo != null || resultWho != null) {
pw.print(prefix); pw.print("resultTo="); pw.print(resultTo);
pw.print(" resultWho="); pw.print(resultWho);
@@ -394,6 +399,8 @@
resolvedType = _resolvedType;
componentSpecified = _componentSpecified;
configuration = _configuration;
+ stackConfigOverride = (container != null)
+ ? container.mStack.mOverrideConfig : Configuration.EMPTY;
resultTo = _resultTo;
resultWho = _resultWho;
requestCode = _reqCode;
@@ -1066,10 +1073,7 @@
static ActivityRecord isInStackLocked(IBinder token) {
final ActivityRecord r = ActivityRecord.forToken(token);
- if (r != null) {
- return r.task.stack.isInStackLocked(token);
- }
- return null;
+ return (r != null) ? r.task.stack.isInStackLocked(r) : null;
}
static ActivityStack getStackLocked(IBinder token) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 13df444..91013ef 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -220,6 +220,9 @@
*/
boolean mConfigWillChange;
+ // Whether or not this stack covers the entire screen; by default stacks are full screen
+ boolean mFullscreen = true;
+
long mLaunchStartTime = 0;
long mFullyDrawnStartTime = 0;
@@ -235,6 +238,8 @@
/** Run all ActivityStacks through this */
final ActivityStackSupervisor mStackSupervisor;
+ Configuration mOverrideConfig;
+
static final int PAUSE_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 1;
static final int DESTROY_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 2;
static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3;
@@ -348,6 +353,7 @@
mStackId = activityContainer.mStackId;
mCurrentUser = mService.mCurrentUserId;
mRecentTasks = recentTasks;
+ mOverrideConfig = Configuration.EMPTY;
}
/**
@@ -450,13 +456,18 @@
ActivityRecord isInStackLocked(IBinder token) {
final ActivityRecord r = ActivityRecord.forToken(token);
- if (r != null) {
- final TaskRecord task = r.task;
- if (task != null && task.mActivities.contains(r) && mTaskHistory.contains(task)) {
- if (task.stack != this) Slog.w(TAG,
+ return isInStackLocked(r);
+ }
+
+ ActivityRecord isInStackLocked(ActivityRecord r) {
+ if (r == null) {
+ return null;
+ }
+ final TaskRecord task = r.task;
+ if (task != null && task.mActivities.contains(r) && mTaskHistory.contains(task)) {
+ if (task.stack != this) Slog.w(TAG,
"Illegal state! task does not point to stack it is in.");
- return r;
- }
+ return r;
}
return null;
}
@@ -1117,7 +1128,8 @@
final int numStacks = mStacks.size();
while (stackNdx < numStacks) {
- tasks = mStacks.get(stackNdx).mTaskHistory;
+ ActivityStack historyStack = mStacks.get(stackNdx);
+ tasks = historyStack.mTaskHistory;
final int numTasks = tasks.size();
while (taskNdx < numTasks) {
activities = tasks.get(taskNdx).mActivities;
@@ -1125,7 +1137,7 @@
while (activityNdx < numActivities) {
final ActivityRecord activity = activities.get(activityNdx);
if (!activity.finishing) {
- return activity.fullscreen ? null : activity;
+ return historyStack.mFullscreen && activity.fullscreen ? null : activity;
}
++activityNdx;
}
@@ -1141,7 +1153,7 @@
// Checks if any of the stacks above this one has a fullscreen activity behind it.
// If so, this stack is hidden, otherwise it is visible.
- private boolean isStackVisible() {
+ private boolean isStackVisibleLocked() {
if (!isAttached()) {
return false;
}
@@ -1156,11 +1168,18 @@
* wallpaper to be shown behind it.
*/
for (int i = mStacks.indexOf(this) + 1; i < mStacks.size(); i++) {
- final ArrayList<TaskRecord> tasks = mStacks.get(i).getAllTasks();
- for (int taskNdx = 0; taskNdx < tasks.size(); taskNdx++) {
+ ActivityStack stack = mStacks.get(i);
+ // stack above isn't full screen, so, we assume we're still visible. at some point
+ // we should look at the stack bounds to see if we're occluded even if the stack
+ // isn't fullscreen
+ if (!stack.mFullscreen) {
+ continue;
+ }
+ final ArrayList<TaskRecord> tasks = stack.getAllTasks();
+ for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = tasks.get(taskNdx);
final ArrayList<ActivityRecord> activities = task.mActivities;
- for (int activityNdx = 0; activityNdx < activities.size(); activityNdx++) {
+ for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
// Conditions for an activity to obscure the stack we're
@@ -1206,7 +1225,7 @@
// If the top activity is not fullscreen, then we need to
// make sure any activities under it are now visible.
boolean aboveTop = true;
- boolean behindFullscreen = !isStackVisible();
+ boolean behindFullscreen = !isStackVisibleLocked();
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
@@ -1329,7 +1348,7 @@
// This case created for transitioning activities from
// translucent to opaque {@link Activity#convertToOpaque}.
if (getVisibleBehindActivity() == r) {
- releaseBackgroundResources();
+ releaseBackgroundResources(r);
} else {
if (!mStackSupervisor.mStoppingActivities.contains(r)) {
mStackSupervisor.mStoppingActivities.add(r);
@@ -1361,7 +1380,7 @@
}
}
- void convertToTranslucent(ActivityRecord r) {
+ void convertActivityToTranslucent(ActivityRecord r) {
mTranslucentActivityWaiting = r;
mUndrawnActivitiesBelowTopTranslucent.clear();
mHandler.sendEmptyMessageDelayed(TRANSLUCENT_TIMEOUT_MSG, TRANSLUCENT_CONVERSION_TIMEOUT);
@@ -2195,7 +2214,7 @@
}
final int targetTaskId = targetTask.taskId;
- mWindowManager.setAppGroupId(target.appToken, targetTaskId);
+ mWindowManager.setAppTask(target.appToken, targetTaskId);
boolean noOptions = canMoveOptions;
final int start = replyChainEnd < 0 ? i : replyChainEnd;
@@ -2220,7 +2239,7 @@
p.setTask(targetTask, null);
targetTask.addActivityAtBottom(p);
- mWindowManager.setAppGroupId(p.appToken, targetTaskId);
+ mWindowManager.setAppTask(p.appToken, targetTaskId);
}
mWindowManager.moveTaskToBottom(targetTaskId);
@@ -2360,7 +2379,7 @@
new RuntimeException("here").fillInStackTrace());
if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p + " from " + srcPos
+ " in to resetting task " + task);
- mWindowManager.setAppGroupId(p.appToken, taskId);
+ mWindowManager.setAppTask(p.appToken, taskId);
}
mWindowManager.moveTaskToTop(taskId);
if (VALIDATE_TOKENS) {
@@ -3262,7 +3281,7 @@
}
if (DEBUG_CONTAINERS) Slog.d(TAG, "activityDestroyedLocked: r=" + r);
- if (isInStackLocked(token) != null) {
+ if (isInStackLocked(r) != null) {
if (r.state == ActivityState.DESTROYING) {
cleanUpActivityLocked(r, true, false);
removeActivityFromHistoryLocked(r, reason);
@@ -3274,10 +3293,9 @@
}
}
- void releaseBackgroundResources() {
+ void releaseBackgroundResources(ActivityRecord r) {
if (hasVisibleBehindActivity() &&
!mHandler.hasMessages(RELEASE_BACKGROUND_RESOURCES_TIMEOUT_MSG)) {
- final ActivityRecord r = getVisibleBehindActivity();
if (r == topRunningActivityLocked(null)) {
// Don't release the top activity if it has requested to run behind the next
// activity.
@@ -3470,7 +3488,7 @@
}
}
- final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord source, Bundle options,
+ final void moveTaskToFrontLocked(TaskRecord tr, boolean noAnimation, Bundle options,
String reason) {
if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr);
@@ -3478,8 +3496,7 @@
final int index = mTaskHistory.indexOf(tr);
if (numTasks == 0 || index < 0) {
// nothing to do!
- if (source != null &&
- (source.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
+ if (noAnimation) {
ActivityOptions.abort(options);
} else {
updateTransitLocked(AppTransition.TRANSIT_TASK_TO_FRONT, options);
@@ -3493,8 +3510,7 @@
moveToFront(reason);
if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare to front transition: task=" + tr);
- if (source != null &&
- (source.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
+ if (noAnimation) {
mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
ActivityRecord r = topRunningActivityLocked(null);
if (r != null) {
@@ -3633,7 +3649,9 @@
// Short circuit: if the two configurations are the exact same
// object (the common case), then there is nothing to do.
Configuration newConfig = mService.mConfiguration;
- if (r.configuration == newConfig && !r.forceNewConfig) {
+ if (r.configuration == newConfig
+ && r.stackConfigOverride == mOverrideConfig
+ && !r.forceNewConfig) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
"Configuration unchanged in " + r);
return true;
@@ -3649,14 +3667,22 @@
// Okay we now are going to make this activity have the new config.
// But then we need to figure out how it needs to deal with that.
- Configuration oldConfig = r.configuration;
+ final Configuration oldConfig = r.configuration;
+ final Configuration oldStackOverride = r.stackConfigOverride;
r.configuration = newConfig;
+ r.stackConfigOverride = mOverrideConfig;
// Determine what has changed. May be nothing, if this is a config
// that has come back from the app after going idle. In that case
// we just want to leave the official config object now in the
// activity and do nothing else.
- final int changes = oldConfig.diff(newConfig);
+ int stackChanges = oldStackOverride.diff(mOverrideConfig);
+ if (stackChanges == 0 && !oldStackOverride.equals(mOverrideConfig)) {
+ // Assume size change if diff didn't report any changes,
+ // but configurations are not equal.
+ stackChanges = ActivityInfo.CONFIG_SCREEN_SIZE;
+ }
+ final int changes = oldConfig.diff(newConfig) | stackChanges;
if (changes == 0 && !r.forceNewConfig) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
"Configuration no differences in " + r);
@@ -3760,8 +3786,9 @@
(andResume ? "Relaunching to RESUMED " : "Relaunching to PAUSED ")
+ r);
r.forceNewConfig = false;
- r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents,
- changes, !andResume, new Configuration(mService.mConfiguration));
+ r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents, changes,
+ !andResume, new Configuration(mService.mConfiguration),
+ new Configuration(mOverrideConfig));
// Note: don't need to call pauseIfSleepingLocked() here, because
// the caller will only pass in 'andResume' if this activity is
// currently resumed, which implies we aren't sleeping.
@@ -4122,4 +4149,14 @@
return "ActivityStack{" + Integer.toHexString(System.identityHashCode(this))
+ " stackId=" + mStackId + ", " + mTaskHistory.size() + " tasks}";
}
+
+ boolean updateOverrideConfiguration(Configuration newConfig) {
+ Configuration oldConfig = mOverrideConfig;
+ mOverrideConfig = (newConfig == null) ? Configuration.EMPTY : newConfig;
+ // we override the configuration only when the stack's dimensions are different from
+ // the display. in this manner, we know that if the override configuration is empty,
+ // the stack is necessarily full screen
+ mFullscreen = Configuration.EMPTY.equals(mOverrideConfig);
+ return !mOverrideConfig.equals(oldConfig);
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index b7728b3..b4455b6 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -63,6 +63,7 @@
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.graphics.Point;
+import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.hardware.display.DisplayManagerGlobal;
@@ -874,8 +875,7 @@
} else {
stack = container.mStack;
}
- stack.mConfigWillChange = config != null
- && mService.mConfiguration.diff(config) != 0;
+ stack.mConfigWillChange = config != null && mService.mConfiguration.diff(config) != 0;
if (DEBUG_CONFIGURATION) Slog.v(TAG,
"Starting activity when config will change = " + stack.mConfigWillChange);
@@ -1181,9 +1181,9 @@
app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_TOP);
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
- r.compat, r.launchedFromPackage, r.task.voiceInteractor, app.repProcState,
- r.icicle, r.persistentState, results, newIntents, !andResume,
- mService.isNextTransitionForward(), profilerInfo);
+ new Configuration(stack.mOverrideConfig), r.compat, r.launchedFromPackage,
+ r.task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
+ newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);
if ((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
// This may be a heavy-weight process! Note that the package
@@ -1596,7 +1596,7 @@
}
}
- final int startActivityUncheckedLocked(ActivityRecord r, ActivityRecord sourceRecord,
+ final int startActivityUncheckedLocked(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,
boolean doResume, Bundle options, TaskRecord inTask) {
final Intent intent = r.intent;
@@ -1807,6 +1807,7 @@
ActivityStack targetStack;
intent.setFlags(launchFlags);
+ final boolean noAnimation = (launchFlags & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0;
// We may want to try to place the new activity in to an existing task. We always
// do this if the target activity is singleTask or singleInstance; we will also do
@@ -1869,8 +1870,8 @@
intentActivity.setTaskToAffiliateWith(sourceRecord.task);
}
movedHome = true;
- targetStack.moveTaskToFrontLocked(intentActivity.task, r, options,
- "bringingFoundTaskToFront");
+ targetStack.moveTaskToFrontLocked(intentActivity.task, noAnimation,
+ options, "bringingFoundTaskToFront");
if ((launchFlags &
(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
@@ -2100,7 +2101,8 @@
targetStack.moveToFront("sourceStackToFront");
final TaskRecord topTask = targetStack.topTask();
if (topTask != sourceTask) {
- targetStack.moveTaskToFrontLocked(sourceTask, r, options, "sourceTaskToFront");
+ targetStack.moveTaskToFrontLocked(sourceTask, noAnimation, options,
+ "sourceTaskToFront");
}
if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
// In this case, we are adding the activity to an existing
@@ -2154,7 +2156,7 @@
return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
targetStack = inTask.stack;
- targetStack.moveTaskToFrontLocked(inTask, r, options, "inTaskToFront");
+ targetStack.moveTaskToFrontLocked(inTask, noAnimation, options, "inTaskToFront");
// Check whether we should actually launch the new activity in to the task,
// or just reuse the current activity on top.
@@ -2519,7 +2521,7 @@
// we'll just indicate that this task returns to the home task.
task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
}
- task.stack.moveTaskToFrontLocked(task, null, options, reason);
+ task.stack.moveTaskToFrontLocked(task, false /* noAnimation */, options, reason);
if (DEBUG_STACK) Slog.d(TAG, "findTaskToMoveToFront: moved to front of stack="
+ task.stack);
}
@@ -2596,6 +2598,27 @@
}
}
+ void resizeStackLocked(int stackId, Rect bounds) {
+ final ActivityStack stack = getStack(stackId);
+ if (stack == null) {
+ Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
+ return;
+ }
+ final Configuration overrideConfig = mWindowManager.resizeStack(stackId, bounds);
+ if (stack.updateOverrideConfiguration(overrideConfig)) {
+ final ActivityRecord r = stack.topRunningActivityLocked(null);
+ if (r != null) {
+ final boolean updated = stack.ensureActivityConfigurationLocked(r, 0);
+ // And we need to make sure at this point that all other activities
+ // are made visible with the correct configuration.
+ ensureActivitiesVisibleLocked(r, 0);
+ if (!updated) {
+ resumeTopActivitiesLocked(stack, null, null);
+ }
+ }
+ }
+ }
+
ActivityStack createStackOnDisplay(int stackId, int displayId) {
ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
if (activityDisplay == null) {
@@ -3729,6 +3752,13 @@
}
@Override
+ public int getStackId() {
+ synchronized (mService) {
+ return mStackId;
+ }
+ }
+
+ @Override
public boolean injectEvent(InputEvent event) {
final long origId = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 9b7d0b2..7ab3794 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -513,11 +513,7 @@
}
}
try {
- if (DEBUG_BROADCAST_LIGHT) {
- int seq = r.intent.getIntExtra("seq", -1);
- Slog.i(TAG, "Delivering to " + filter
- + " (seq=" + seq + "): " + r);
- }
+ if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG, "Delivering to " + filter + " : " + r);
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);
@@ -662,12 +658,9 @@
// result if requested...
if (r.resultTo != null) {
try {
- if (DEBUG_BROADCAST) {
- int seq = r.intent.getIntExtra("seq", -1);
- Slog.i(TAG, "Finishing broadcast ["
- + mQueueName + "] " + r.intent.getAction()
- + " seq=" + seq + " app=" + r.callerApp);
- }
+ if (DEBUG_BROADCAST) Slog.i(TAG,
+ "Finishing broadcast [" + mQueueName + "] "
+ + r.intent.getAction() + " app=" + r.callerApp);
performReceiveLocked(r.callerApp, r.resultTo,
new Intent(r.intent), r.resultCode,
r.resultData, r.resultExtras, false, false, r.userId);
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index c376744..9a645df 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -95,3 +95,11 @@
# Home Stack brought to front or rear
30044 am_home_stack_moved (User|1|5),(To Front|1|5),(Top Stack Id|1|5),(Focused Stack Id|1|5),(Reason|3)
+
+# Running pre boot receiver
+30045 am_pre_boot (User|1|5),(Package|3)
+
+# Report collection of global memory state
+30046 am_meminfo (CachedKb|2|2),(FreeKb|2|2),(ZramKb|2|2),(KernelKb|2|2),(NativeKb|2|2)
+# Report collection of memory used by a process
+30047 am_pss (Pid|1|5),(UID|1|5),(Process Name|3),(PssKb|2|2),(UssKb|2|2)
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index af5ed83..f900d0d 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -163,6 +163,7 @@
/**
* Force evaluation even if it has succeeded in the past.
* arg1 = UID responsible for requesting this reeval. Will be billed for data.
+ * arg2 = Number of evaluation attempts to make. (If 0, make INITIAL_ATTEMPTS attempts.)
*/
public static final int CMD_FORCE_REEVALUATION = BASE + 8;
@@ -212,11 +213,14 @@
// Negative values disable reevaluation.
private static final String REEVALUATE_DELAY_PROPERTY = "persist.netmon.reeval_delay";
- // Default to 5s reevaluation delay.
+ // When connecting, attempt to validate 3 times, pausing 5s between them.
private static final int DEFAULT_REEVALUATE_DELAY_MS = 5000;
- private static final int MAX_RETRIES = 10;
- // Between groups of MAX_RETRIES evaluation attempts, pause 10 mins in hopes ISP outage passes.
+ private static final int INITIAL_ATTEMPTS = 3;
+ // If a network is not validated, make one attempt every 10 mins to see if it starts working.
private static final int REEVALUATE_PAUSE_MS = 10*60*1000;
+ private static final int PERIODIC_ATTEMPTS = 1;
+ // When an application calls reportBadNetwork, only make one attempt.
+ private static final int REEVALUATE_ATTEMPTS = 1;
private final int mReevaluateDelayMs;
private int mReevaluateToken = 0;
private static final int INVALID_UID = -1;
@@ -236,6 +240,14 @@
// Set if the user explicitly selected "Do not use this network" in captive portal sign-in app.
private boolean mUserDoesNotWant = false;
+ // How many times we should attempt validation. Only checked in EvaluatingState; must be set
+ // before entering EvaluatingState. Note that whatever code causes us to transition to
+ // EvaluatingState last decides how many attempts will be made, so if one codepath were to
+ // enter EvaluatingState with a specific number of attempts, and then another were to enter it
+ // with a different number of attempts, the second number would be used. This is not currently
+ // a problem because EvaluatingState is not reentrant.
+ private int mMaxAttempts;
+
public boolean systemReady = false;
private final State mDefaultState = new DefaultState();
@@ -305,6 +317,7 @@
return HANDLED;
case CMD_NETWORK_CONNECTED:
if (DBG) log("Connected");
+ mMaxAttempts = INITIAL_ATTEMPTS;
transitionTo(mEvaluatingState);
return HANDLED;
case CMD_NETWORK_DISCONNECTED:
@@ -318,6 +331,7 @@
case CMD_FORCE_REEVALUATION:
if (DBG) log("Forcing reevaluation");
mUidResponsibleForReeval = message.arg1;
+ mMaxAttempts = message.arg2 != 0 ? message.arg2 : REEVALUATE_ATTEMPTS;
transitionTo(mEvaluatingState);
return HANDLED;
case CMD_CAPTIVE_PORTAL_APP_FINISHED:
@@ -347,7 +361,10 @@
public void enter() {
mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
NETWORK_TEST_RESULT_INVALID, 0, mNetworkAgentInfo));
- if (!mUserDoesNotWant) sendMessageDelayed(CMD_FORCE_REEVALUATION, REEVALUATE_PAUSE_MS);
+ if (!mUserDoesNotWant) {
+ sendMessageDelayed(CMD_FORCE_REEVALUATION, 0 /* no UID */,
+ PERIODIC_ATTEMPTS, REEVALUATE_PAUSE_MS);
+ }
}
@Override
@@ -413,11 +430,11 @@
// Being in the EvaluatingState State indicates the Network is being evaluated for internet
// connectivity.
private class EvaluatingState extends State {
- private int mRetries;
+ private int mAttempt;
@Override
public void enter() {
- mRetries = 0;
+ mAttempt = 1;
sendMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
if (mUidResponsibleForReeval != INVALID_UID) {
TrafficStats.setThreadStatsUid(mUidResponsibleForReeval);
@@ -454,18 +471,18 @@
transitionTo(mValidatedState);
return HANDLED;
}
- // Note: This call to isCaptivePortal() could take minutes. Resolving the
- // server's IP addresses could hit the DNS timeout and attempting connections
- // to each of the server's several (e.g. 11) IP addresses could each take
- // SOCKET_TIMEOUT_MS. During this time this StateMachine will be unresponsive.
- // isCaptivePortal() could be executed on another Thread if this is found to
- // cause problems.
+ // Note: This call to isCaptivePortal() could take up to a minute. Resolving the
+ // server's IP addresses could hit the DNS timeout, and attempting connections
+ // to each of the server's several IP addresses (currently one IPv4 and one
+ // IPv6) could each take SOCKET_TIMEOUT_MS. During this time this StateMachine
+ // will be unresponsive. isCaptivePortal() could be executed on another Thread
+ // if this is found to cause problems.
int httpResponseCode = isCaptivePortal();
if (httpResponseCode == 204) {
transitionTo(mValidatedState);
} else if (httpResponseCode >= 200 && httpResponseCode <= 399) {
transitionTo(mCaptivePortalState);
- } else if (++mRetries > MAX_RETRIES) {
+ } else if (++mAttempt > mMaxAttempts) {
transitionTo(mOfflineState);
} else if (mReevaluateDelayMs >= 0) {
Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
index 59d5605..7f48768 100644
--- a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
+++ b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
@@ -62,11 +62,6 @@
void process(ActiveSource newActive, int deviceType) {
// Seq #17
HdmiCecLocalDeviceTv tv = mSource;
- ActiveSource activeSource = tv.getActiveSource();
- if (activeSource.equals(newActive)) {
- invokeCallback(HdmiControlManager.RESULT_SUCCESS);
- return;
- }
HdmiDeviceInfo device = mService.getDeviceInfo(newActive.logicalAddress);
if (device == null) {
tv.startNewDeviceAction(newActive, deviceType);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index ce52920..a8f6954 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -55,10 +55,6 @@
mService.sendCecCommand(HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
mAddress, mService.getVendorId()));
startQueuedActions();
-
- // Switch TV input after bootup.
- setActiveSource(true);
- maySendActiveSource(Constants.ADDR_TV);
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 43ef457..8241cdc 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -34,6 +34,7 @@
import android.content.Context;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.HdmiRecordSources;
import android.hardware.hdmi.HdmiTimerRecordSources;
import android.hardware.hdmi.IHdmiControlCallback;
@@ -431,11 +432,15 @@
return;
}
List<SendKeyAction> action = getActions(SendKeyAction.class);
+ int logicalAddress = findKeyReceiverAddress();
+ if (logicalAddress == mAddress) {
+ Slog.w(TAG, "Discard key event to itself :" + keyCode + " pressed:" + isPressed);
+ return;
+ }
if (!action.isEmpty()) {
action.get(0).processKeyEvent(keyCode, isPressed);
} else {
if (isPressed) {
- int logicalAddress = findKeyReceiverAddress();
if (logicalAddress != Constants.ADDR_INVALID) {
addAndStartAction(new SendKeyAction(this, logicalAddress, keyCode));
return;
@@ -580,6 +585,12 @@
if (!isInDeviceList(address, path)) {
handleNewDeviceAtTheTailOfActivePath(path);
}
+
+ // Add the device ahead with default information to handle <Active Source>
+ // promptly, rather than waiting till the new device action is finished.
+ HdmiDeviceInfo deviceInfo = new HdmiDeviceInfo(address, path, getPortId(path), type,
+ Constants.UNKNOWN_VENDOR_ID, HdmiUtils.getDefaultDeviceName(address));
+ addCecDevice(deviceInfo);
startNewDeviceAction(ActiveSource.of(address, path), type);
return true;
}
@@ -878,6 +889,17 @@
return oldStatus;
}
+ @ServiceThreadOnly
+ private void updateArcFeatureStatus(int portId, boolean isConnected) {
+ assertRunOnServiceThread();
+ // HEAC 2.4, HEACT 5-15
+ // Should not activate ARC if +5V status is false.
+ HdmiPortInfo portInfo = mService.getPortInfo(portId);
+ if (portInfo.isArcSupported()) {
+ changeArcFeatureEnabled(isConnected);
+ }
+ }
+
private void notifyArcStatusToAudioService(boolean enabled) {
// Note that we don't set any name to ARC.
mService.getAudioManager().setWiredDeviceConnectionState(
@@ -1456,6 +1478,25 @@
}
/**
+ * Returns the {@link HdmiDeviceInfo} instance whose physical address matches
+ * the given routing path. This is the version accessible safely from threads
+ * other than service thread.
+ *
+ * @param path routing path or physical address
+ * @return {@link HdmiDeviceInfo} if the matched info is found; otherwise null
+ */
+ HdmiDeviceInfo getSafeDeviceInfoByPath(int path) {
+ synchronized (mLock) {
+ for (HdmiDeviceInfo info : mSafeAllDeviceInfos) {
+ if (info.getPhysicalAddress() == path) {
+ return info;
+ }
+ }
+ return null;
+ }
+ }
+
+ /**
* Whether a device of the specified physical address and logical address exists
* in a device info list. However, both are minimal condition and it could
* be different device from the original one.
@@ -1465,7 +1506,7 @@
* @return true if exist; otherwise false
*/
@ServiceThreadOnly
- private boolean isInDeviceList(int logicalAddress, int physicalAddress) {
+ boolean isInDeviceList(int logicalAddress, int physicalAddress) {
assertRunOnServiceThread();
HdmiDeviceInfo device = getCecDeviceInfo(logicalAddress);
if (device == null) {
@@ -1490,6 +1531,7 @@
// It covers seq #40, #43.
hotplugActions.get(0).pollAllDevicesNow();
}
+ updateArcFeatureStatus(portId, connected);
}
private void removeCecSwitches(int portId) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 9f78c61..49a96d8 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1196,7 +1196,7 @@
}
int activePath = tv.getActivePath();
if (activePath != HdmiDeviceInfo.PATH_INVALID) {
- HdmiDeviceInfo info = tv.getDeviceInfoByPath(activePath);
+ HdmiDeviceInfo info = tv.getSafeDeviceInfoByPath(activePath);
return (info != null) ? info : new HdmiDeviceInfo(activePath, tv.getActivePortId());
}
return null;
diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
index 722be71..1bbd038 100644
--- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
+++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
@@ -38,6 +38,7 @@
private static final int POLLING_INTERVAL_MS = 5000;
private static final int TIMEOUT_COUNT = 3;
+ private static final int AVR_COUNT_MAX = 3;
// State in which waits for next polling
private static final int STATE_WAIT_FOR_NEXT_POLLING = 1;
@@ -48,6 +49,12 @@
private int mTimeoutCount = 0;
+ // Counter used to ensure the connection to AVR is stable. Occasional failure to get
+ // polling response from AVR despite its presence leads to unstable status flipping.
+ // This is a workaround to deal with it, by removing the device only if the removal
+ // is detected {@code AVR_COUNT_MAX} times in a row.
+ private int mAvrStatusCount = 0;
+
/**
* Constructor
*
@@ -148,10 +155,22 @@
BitSet removed = complement(currentInfos, polledResult);
int index = -1;
while ((index = removed.nextSetBit(index + 1)) != -1) {
+ if (index == Constants.ADDR_AUDIO_SYSTEM) {
+ ++mAvrStatusCount;
+ Slog.w(TAG, "Ack not returned from AVR. count: " + mAvrStatusCount);
+ if (mAvrStatusCount < AVR_COUNT_MAX) {
+ continue;
+ }
+ }
Slog.v(TAG, "Remove device by hot-plug detection:" + index);
removeDevice(index);
}
+ // Reset the counter if the ack is returned from AVR.
+ if (!removed.get(Constants.ADDR_AUDIO_SYSTEM)) {
+ mAvrStatusCount = 0;
+ }
+
// Next, check added devices.
BitSet added = complement(polledResult, currentInfos);
index = -1;
diff --git a/services/core/java/com/android/server/hdmi/NewDeviceAction.java b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
index 3d64cc5..6753368 100644
--- a/services/core/java/com/android/server/hdmi/NewDeviceAction.java
+++ b/services/core/java/com/android/server/hdmi/NewDeviceAction.java
@@ -163,6 +163,12 @@
}
private void addDeviceInfo() {
+ // The device should be in the device list with default information.
+ if (!tv().isInDeviceList(mDeviceLogicalAddress, mDevicePhysicalAddress)) {
+ Slog.w(TAG, String.format("Device not found (%02x, %04x)",
+ mDeviceLogicalAddress, mDevicePhysicalAddress));
+ return;
+ }
if (mDisplayName == null) {
mDisplayName = HdmiUtils.getDefaultDeviceName(mDeviceLogicalAddress);
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index da63caa..df31158 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -250,77 +250,37 @@
if (isPlaybackActive(false) || hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
flags &= ~AudioManager.FLAG_PLAY_SOUND;
}
- boolean isMute = direction == MediaSessionManager.DIRECTION_MUTE;
- if (direction > 1) {
- direction = 1;
- } else if (direction < -1) {
- direction = -1;
- }
if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
if (mUseMasterVolume) {
// If this device only uses master volume and playback is local
// just adjust the master volume and return.
- boolean isMasterMute = mAudioManager.isMasterMute();
- if (isMute) {
- mAudioManagerInternal.setMasterMuteForUid(!isMasterMute,
- flags, packageName, mService.mICallback, uid);
- } else {
- mAudioManagerInternal.adjustMasterVolumeForUid(direction, flags, packageName,
- uid);
- if (isMasterMute) {
- mAudioManagerInternal.setMasterMuteForUid(false,
- flags, packageName, mService.mICallback, uid);
- }
- }
+ mAudioManagerInternal.adjustMasterVolumeForUid(direction, flags, packageName,
+ uid);
return;
}
int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
- boolean isStreamMute = mAudioManager.isStreamMute(stream);
if (useSuggested) {
if (AudioSystem.isStreamActive(stream, 0)) {
- if (isMute) {
- mAudioManager.setStreamMute(stream, !isStreamMute);
- } else {
- mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream, direction,
- flags, packageName, uid);
- if (isStreamMute && direction != 0) {
- mAudioManager.setStreamMute(stream, false);
- }
- }
+ mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(stream, direction,
+ flags, packageName, uid);
} else {
flags |= previousFlagPlaySound;
- isStreamMute =
- mAudioManager.isStreamMute(AudioManager.USE_DEFAULT_STREAM_TYPE);
- if (isMute) {
- mAudioManager.setStreamMute(AudioManager.USE_DEFAULT_STREAM_TYPE,
- !isStreamMute);
- } else {
- mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(
- AudioManager.USE_DEFAULT_STREAM_TYPE, direction, flags, packageName,
- uid);
- if (isStreamMute && direction != 0) {
- mAudioManager.setStreamMute(AudioManager.USE_DEFAULT_STREAM_TYPE,
- false);
- }
- }
+ mAudioManagerInternal.adjustSuggestedStreamVolumeForUid(
+ AudioManager.USE_DEFAULT_STREAM_TYPE, direction, flags, packageName,
+ uid);
}
} else {
- if (isMute) {
- mAudioManager.setStreamMute(stream, !isStreamMute);
- } else {
- mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags,
- packageName, uid);
- if (isStreamMute && direction != 0) {
- mAudioManager.setStreamMute(stream, false);
- }
- }
+ mAudioManagerInternal.adjustStreamVolumeForUid(stream, direction, flags,
+ packageName, uid);
}
} else {
if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) {
// Nothing to do, the volume cannot be changed
return;
}
- if (isMute) {
+ if (direction == AudioManager.ADJUST_TOGGLE_MUTE
+ || direction == AudioManager.ADJUST_MUTE
+ || direction == AudioManager.ADJUST_UNMUTE) {
Log.w(TAG, "Muting remote playback is not supported");
return;
}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 667d02a..0500f94 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -872,30 +872,10 @@
try {
String packageName = getContext().getOpPackageName();
if (mUseMasterVolume) {
- boolean isMasterMute = mAudioService.isMasterMute();
- if (direction == MediaSessionManager.DIRECTION_MUTE) {
- mAudioService.setMasterMute(!isMasterMute, flags, packageName, mICallback);
- } else {
mAudioService.adjustMasterVolume(direction, flags, packageName);
- // Do not call setMasterMute when direction = 0 which is used just to
- // show the UI.
- if (isMasterMute && direction != 0) {
- mAudioService.setMasterMute(false, flags, packageName, mICallback);
- }
- }
} else {
- boolean isStreamMute = mAudioService.isStreamMute(suggestedStream);
- if (direction == MediaSessionManager.DIRECTION_MUTE) {
- mAudioManager.setStreamMute(suggestedStream, !isStreamMute);
- } else {
- mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
- flags, packageName);
- // Do not call setStreamMute when direction = 0 which is used just to
- // show the UI.
- if (isStreamMute && direction != 0) {
- mAudioManager.setStreamMute(suggestedStream, false);
- }
- }
+ mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
+ flags, packageName);
}
} catch (RemoteException e) {
Log.e(TAG, "Error adjusting default volume.", e);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 13b2b0f..359359e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3281,7 +3281,7 @@
// If the result set is different from when this
// was created, we need to clear it and re-ask the
// user their preference, if we're looking for an "always" type entry.
- if (always && !pa.mPref.sameSet(query, priority)) {
+ if (always && !pa.mPref.sameSet(query)) {
Slog.i(TAG, "Result set changed, dropping preferred activity for "
+ intent + " type " + resolvedType);
if (DEBUG_PREFERRED) {
@@ -3381,7 +3381,7 @@
if (resolveInfo != null) {
List<ResolveInfo> result = new ArrayList<ResolveInfo>(1);
result.add(resolveInfo);
- return result;
+ return filterIfNotPrimaryUser(result, userId);
}
// Check for cross profile results.
resolveInfo = queryCrossProfileIntents(
@@ -3394,17 +3394,38 @@
result.add(resolveInfo);
Collections.sort(result, mResolvePrioritySorter);
}
- return result;
+ return filterIfNotPrimaryUser(result, userId);
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
- return mActivities.queryIntentForPackage(intent, resolvedType, flags,
- pkg.activities, userId);
+ return filterIfNotPrimaryUser(
+ mActivities.queryIntentForPackage(
+ intent, resolvedType, flags, pkg.activities, userId),
+ userId);
}
return new ArrayList<ResolveInfo>();
}
}
+ /**
+ * Filter out activities with primaryUserOnly flag set, when current user is not the owner.
+ *
+ * @return filtered list
+ */
+ private List<ResolveInfo> filterIfNotPrimaryUser(List<ResolveInfo> resolveInfos, int userId) {
+ if (userId == UserHandle.USER_OWNER) {
+ return resolveInfos;
+ }
+ for (int i = resolveInfos.size() - 1; i >= 0; i--) {
+ ResolveInfo info = resolveInfos.get(i);
+ if ((info.activityInfo.flags & ActivityInfo.FLAG_PRIMARY_USER_ONLY) != 0) {
+ resolveInfos.remove(i);
+ }
+ }
+ return resolveInfos;
+ }
+
+
private ResolveInfo querySkipCurrentProfileIntents(
List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
int flags, int sourceUserId) {
diff --git a/services/core/java/com/android/server/pm/PreferredComponent.java b/services/core/java/com/android/server/pm/PreferredComponent.java
index 69c1909..8e2e0cd 100644
--- a/services/core/java/com/android/server/pm/PreferredComponent.java
+++ b/services/core/java/com/android/server/pm/PreferredComponent.java
@@ -192,7 +192,7 @@
}
}
- public boolean sameSet(List<ResolveInfo> query, int priority) {
+ public boolean sameSet(List<ResolveInfo> query) {
if (mSetPackages == null) {
return query == null;
}
@@ -201,10 +201,10 @@
}
final int NQ = query.size();
final int NS = mSetPackages.length;
+
int numMatch = 0;
for (int i=0; i<NQ; i++) {
ResolveInfo ri = query.get(i);
- if (ri.priority != priority) continue;
ActivityInfo ai = ri.activityInfo;
boolean good = false;
for (int j=0; j<NS; j++) {
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 2af56fe..50b2262 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -677,7 +677,9 @@
private AudioDevicePort mAudioSource;
private List<AudioDevicePort> mAudioSink = new ArrayList<>();
private AudioPatch mAudioPatch = null;
- private float mCommittedVolume = 0.0f;
+ // Set to an invalid value for a volume, so that current volume can be applied at the
+ // first call to updateAudioConfigLocked().
+ private float mCommittedVolume = -1f;
private float mSourceVolume = 0.0f;
private TvStreamConfig mActiveConfig = null;
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 08754f9..f8b40d1 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -606,9 +606,8 @@
final int windowCount = windowList.size();
for (int i = 0; i < windowCount; i++) {
WindowState windowState = windowList.get(i);
- if ((windowState.isOnScreen() || windowState.mAttrs.type == WindowManager
- .LayoutParams.TYPE_UNIVERSE_BACKGROUND)
- && !windowState.mWinAnimator.mEnterAnimationPending) {
+ if (windowState.isOnScreen() &&
+ !windowState.mWinAnimator.mEnterAnimationPending) {
outWindows.put(windowState.mLayer, windowState);
}
}
@@ -1237,7 +1236,6 @@
&& windowType != WindowManager.LayoutParams.TYPE_DRAG
&& windowType != WindowManager.LayoutParams.TYPE_HIDDEN_NAV_CONSUMER
&& windowType != WindowManager.LayoutParams.TYPE_POINTER
- && windowType != WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND
&& windowType != WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY
&& windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
&& windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 3b27cd2..a04f6cb 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -52,7 +52,7 @@
final boolean voiceInteraction;
- int groupId = -1;
+ Task mTask;
boolean appFullscreen;
int requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
boolean layoutConfigChanges;
@@ -256,7 +256,8 @@
mIsExiting = false;
removeAllWindows();
- final Task task = service.mTaskIdToTask.get(groupId);
+ // Use local variable because removeAppToken will null out mTask.
+ final Task task = mTask;
if (task != null) {
if (!task.removeAppToken(this)) {
Slog.e(WindowManagerService.TAG, "removeAppFromTaskLocked: token=" + this
@@ -295,8 +296,8 @@
if (allAppWindows.size() > 0) {
pw.print(prefix); pw.print("allAppWindows="); pw.println(allAppWindows);
}
- pw.print(prefix); pw.print("groupId="); pw.print(groupId);
- pw.print(" appFullscreen="); pw.print(appFullscreen);
+ pw.print(prefix); pw.print("task="); pw.println(mTask);
+ pw.print(prefix); pw.print(" appFullscreen="); pw.print(appFullscreen);
pw.print(" requestedOrientation="); pw.println(requestedOrientation);
pw.print(prefix); pw.print("hiddenRequested="); pw.print(hiddenRequested);
pw.print(" clientHidden="); pw.print(clientHidden);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e3b53b0..8bbc5a9 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -379,7 +379,7 @@
ArrayList<Task> tasks = stack.getTasks();
for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
final Task task = tasks.get(taskNdx);
- pw.print(" mTaskId="); pw.println(task.taskId);
+ pw.print(" mTaskId="); pw.println(task.mTaskId);
AppTokenList tokens = task.mAppTokens;
for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx, ++ndx) {
final AppWindowToken wtoken = tokens.get(tokenNdx);
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 27ac32a..55dd911 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -239,9 +239,6 @@
// As an optimization, we could try to prune the list of windows but this turns
// out to be difficult because only the native code knows for sure which window
// currently has touch focus.
- final WindowStateAnimator universeBackground = mService.mAnimator.mUniverseBackground;
- final int aboveUniverseLayer = mService.mAnimator.mAboveUniverseLayer;
- boolean addedUniverse = false;
boolean disableWallpaperTouchEvents = false;
// If there's a drag in flight, provide a pseudowindow to catch drag input
@@ -299,20 +296,8 @@
mService.mDragState.sendDragStartedIfNeededLw(child);
}
- if (universeBackground != null && !addedUniverse
- && child.mBaseLayer < aboveUniverseLayer && onDefaultDisplay) {
- final WindowState u = universeBackground.mWin;
- if (u.mInputChannel != null && u.mInputWindowHandle != null) {
- addInputWindowHandleLw(u.mInputWindowHandle, u, u.mAttrs.flags,
- u.mAttrs.type, true, u == mInputFocus, false);
- }
- addedUniverse = true;
- }
-
- if (child.mWinAnimator != universeBackground) {
- addInputWindowHandleLw(inputWindowHandle, child, flags, type, isVisible,
- hasFocus, hasWallpaper);
- }
+ addInputWindowHandleLw(inputWindowHandle, child, flags, type, isVisible, hasFocus,
+ hasWallpaper);
}
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index a4dfd8a4..d68c056 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -446,20 +446,6 @@
mService.wallpaperCommandComplete(window, result);
}
- public void setUniverseTransform(IBinder window, float alpha, float offx, float offy,
- float dsdx, float dtdx, float dsdy, float dtdy) {
- synchronized(mService.mWindowMap) {
- long ident = Binder.clearCallingIdentity();
- try {
- mService.setUniverseTransformLocked(
- mService.windowForClientLocked(this, window, true),
- alpha, offx, offy, dsdx, dtdx, dsdy, dtdy);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
-
public void onRectangleOnScreenRequested(IBinder token, Rect rectangle) {
synchronized(mService.mWindowMap) {
final long identity = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 80d727d..a9b26e2 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -26,14 +26,13 @@
class Task {
TaskStack mStack;
final AppTokenList mAppTokens = new AppTokenList();
- final int taskId;
+ final int mTaskId;
final int mUserId;
boolean mDeferRemoval = false;
final WindowManagerService mService;
- Task(AppWindowToken wtoken, TaskStack stack, int userId, WindowManagerService service) {
- taskId = wtoken.groupId;
- mAppTokens.add(wtoken);
+ Task(int taskId, TaskStack stack, int userId, WindowManagerService service) {
+ mTaskId = taskId;
mStack = stack;
mUserId = userId;
mService = service;
@@ -45,38 +44,47 @@
void addAppToken(int addPos, AppWindowToken wtoken) {
final int lastPos = mAppTokens.size();
- for (int pos = 0; pos < lastPos && pos < addPos; ++pos) {
- if (mAppTokens.get(pos).removed) {
- // addPos assumes removed tokens are actually gone.
- ++addPos;
+ if (addPos >= lastPos) {
+ addPos = lastPos;
+ } else {
+ for (int pos = 0; pos < lastPos && pos < addPos; ++pos) {
+ if (mAppTokens.get(pos).removed) {
+ // addPos assumes removed tokens are actually gone.
+ ++addPos;
+ }
}
}
mAppTokens.add(addPos, wtoken);
+ wtoken.mTask = this;
mDeferRemoval = false;
}
void removeLocked() {
if (!mAppTokens.isEmpty() && mStack.isAnimating()) {
- if (DEBUG_STACK) Slog.i(TAG, "removeTask: deferring removing taskId=" + taskId);
+ if (DEBUG_STACK) Slog.i(TAG, "removeTask: deferring removing taskId=" + mTaskId);
mDeferRemoval = true;
return;
}
- if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + taskId);
- EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, taskId, "removeTask");
+ if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + mTaskId);
+ EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "removeTask");
mDeferRemoval = false;
mStack.removeTask(this);
- mService.mTaskIdToTask.delete(taskId);
+ mService.mTaskIdToTask.delete(mTaskId);
}
boolean removeAppToken(AppWindowToken wtoken) {
boolean removed = mAppTokens.remove(wtoken);
if (mAppTokens.size() == 0) {
- EventLog.writeEvent(com.android.server.EventLogTags.WM_TASK_REMOVED, taskId,
+ EventLog.writeEvent(com.android.server.EventLogTags.WM_TASK_REMOVED, mTaskId,
"removeAppToken: last token");
if (mDeferRemoval) {
removeLocked();
}
}
+ wtoken.mTask = null;
+ /* Leave mTaskId for now, it might be useful for debug
+ wtoken.mTaskId = -1;
+ */
return removed;
}
@@ -88,6 +96,6 @@
@Override
public String toString() {
- return "{taskId=" + taskId + " appTokens=" + mAppTokens + " mdr=" + mDeferRemoval + "}";
+ return "{taskId=" + mTaskId + " appTokens=" + mAppTokens + " mdr=" + mDeferRemoval + "}";
}
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index d6741b3..6660843 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -19,11 +19,14 @@
import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT;
import static com.android.server.wm.WindowManagerService.TAG;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Debug;
+import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Slog;
import android.util.TypedValue;
+
import com.android.server.EventLogTags;
import java.io.PrintWriter;
@@ -78,9 +81,14 @@
/** Detach this stack from its display when animation completes. */
boolean mDeferDetach;
+ // Contains configurations settings that are different from the global configuration due to
+ // stack specific operations. E.g. {@link #setBounds}.
+ Configuration mOverrideConfig;
+
TaskStack(WindowManagerService service, int stackId) {
mService = service;
mStackId = stackId;
+ mOverrideConfig = Configuration.EMPTY;
// TODO: remove bounds from log, they are always 0.
EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId, mBounds.left, mBounds.top,
mBounds.right, mBounds.bottom);
@@ -119,6 +127,7 @@
boolean oldFullscreen = mFullscreen;
if (mDisplayContent != null) {
mDisplayContent.getLogicalDisplayRect(mTmpRect);
+ bounds.intersect(mTmpRect); // ensure bounds are entirely within the display rect
mFullscreen = mTmpRect.equals(bounds);
}
@@ -129,7 +138,7 @@
mDimLayer.setBounds(bounds);
mAnimationBackgroundSurface.setBounds(bounds);
mBounds.set(bounds);
-
+ updateOverrideConfiguration();
return true;
}
@@ -137,6 +146,27 @@
out.set(mBounds);
}
+ void updateOverrideConfiguration() {
+ final Configuration serviceConfig = mService.mCurConfiguration;
+ if (mFullscreen) {
+ mOverrideConfig = Configuration.EMPTY;
+ return;
+ }
+
+ if (mOverrideConfig == Configuration.EMPTY) {
+ mOverrideConfig = new Configuration();
+ }
+
+ // TODO(multidisplay): Update Dp to that of display stack is on.
+ final float density = serviceConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
+ mOverrideConfig.screenWidthDp =
+ Math.min((int)(mBounds.width() / density), serviceConfig.screenWidthDp);
+ mOverrideConfig.screenHeightDp =
+ Math.min((int)(mBounds.height() / density), serviceConfig.screenHeightDp);
+ mOverrideConfig.smallestScreenWidthDp =
+ Math.min(mOverrideConfig.screenWidthDp, mOverrideConfig.screenHeightDp);
+ }
+
void updateDisplayInfo() {
if (mFullscreen && mDisplayContent != null) {
mDisplayContent.getLogicalDisplayRect(mTmpRect);
@@ -194,7 +224,7 @@
if (toTop) {
mDisplayContent.moveStack(this, true);
}
- EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.taskId, toTop ? 1 : 0, stackNdx);
+ EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, stackNdx);
}
void moveTaskToTop(Task task) {
@@ -224,10 +254,9 @@
}
mDisplayContent.layoutNeeded = true;
}
- final int taskId = task.taskId;
for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) {
final AppWindowToken wtoken = mExitingAppTokens.get(appNdx);
- if (wtoken.groupId == taskId) {
+ if (wtoken.mTask == task) {
wtoken.mIsExiting = false;
mExitingAppTokens.remove(appNdx);
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 7e1f061..3987132 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -79,9 +79,6 @@
* seen. If multiple windows satisfy this, use the lowest window. */
WindowState mWindowDetachedWallpaper = null;
- WindowStateAnimator mUniverseBackground = null;
- int mAboveUniverseLayer = 0;
-
int mBulkUpdateParams = 0;
Object mLastWindowFreezeSource;
@@ -814,10 +811,6 @@
pw.print(prefix); pw.print("mWindowDetachedWallpaper=");
pw.println(mWindowDetachedWallpaper);
}
- if (mUniverseBackground != null) {
- pw.print(prefix); pw.print("mUniverseBackground="); pw.print(mUniverseBackground);
- pw.print(" mAboveUniverseLayer="); pw.println(mAboveUniverseLayer);
- }
}
int getPendingLayoutChanges(final int displayId) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2c05e93..66bef41 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -769,7 +769,7 @@
* Whether the UI is currently running in touch mode (not showing
* navigational focus because the user is directly pressing the screen).
*/
- boolean mInTouchMode = true;
+ boolean mInTouchMode;
private ViewServer mViewServer;
private final ArrayList<WindowChangeListener> mWindowChangeListeners =
@@ -813,9 +813,6 @@
WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());
mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);
- mAnimator.mAboveUniverseLayer = mPolicy.getAboveUniverseLayer()
- * TYPE_LAYER_MULTIPLIER
- + TYPE_LAYER_OFFSET;
}
}, 0);
}
@@ -830,6 +827,8 @@
com.android.internal.R.bool.config_sf_limitedAlpha);
mHasPermanentDpad = context.getResources().getBoolean(
com.android.internal.R.bool.config_hasPermanentDpad);
+ mInTouchMode = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_defaultInTouchMode);
mInputManager = inputManager; // Must be before createDisplayContentLocked.
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mDisplaySettings = new DisplaySettings();
@@ -2945,37 +2944,6 @@
return null;
}
- public void setUniverseTransformLocked(WindowState window, float alpha,
- float offx, float offy, float dsdx, float dtdx, float dsdy, float dtdy) {
- Transformation transform = window.mWinAnimator.mUniverseTransform;
- transform.setAlpha(alpha);
- Matrix matrix = transform.getMatrix();
- matrix.getValues(mTmpFloats);
- mTmpFloats[Matrix.MTRANS_X] = offx;
- mTmpFloats[Matrix.MTRANS_Y] = offy;
- mTmpFloats[Matrix.MSCALE_X] = dsdx;
- mTmpFloats[Matrix.MSKEW_Y] = dtdx;
- mTmpFloats[Matrix.MSKEW_X] = dsdy;
- mTmpFloats[Matrix.MSCALE_Y] = dtdy;
- matrix.setValues(mTmpFloats);
- final DisplayContent displayContent = window.getDisplayContent();
- if (displayContent == null) {
- return;
- }
-
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- final RectF dispRect = new RectF(0, 0,
- displayInfo.logicalWidth, displayInfo.logicalHeight);
- matrix.mapRect(dispRect);
- window.mGivenTouchableRegion.set(0, 0,
- displayInfo.logicalWidth, displayInfo.logicalHeight);
- window.mGivenTouchableRegion.op((int)dispRect.left, (int)dispRect.top,
- (int)dispRect.right, (int)dispRect.bottom, Region.Op.DIFFERENCE);
- window.mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
- displayContent.layoutNeeded = true;
- performLayoutAndPlaceSurfacesLocked();
- }
-
public void onRectangleOnScreenRequested(IBinder token, Rect rectangle) {
synchronized (mWindowMap) {
if (mAccessibilityController != null) {
@@ -3612,15 +3580,15 @@
Binder.restoreCallingIdentity(origId);
}
- private Task createTask(int taskId, int stackId, int userId, AppWindowToken atoken) {
- if (DEBUG_STACK) Slog.i(TAG, "createTask: taskId=" + taskId + " stackId=" + stackId
+ private Task createTaskLocked(int taskId, int stackId, int userId, AppWindowToken atoken) {
+ if (DEBUG_STACK) Slog.i(TAG, "createTaskLocked: taskId=" + taskId + " stackId=" + stackId
+ " atoken=" + atoken);
final TaskStack stack = mStackIdToStack.get(stackId);
if (stack == null) {
throw new IllegalArgumentException("addAppToken: invalid stackId=" + stackId);
}
EventLog.writeEvent(EventLogTags.WM_TASK_CREATED, taskId, stackId);
- Task task = new Task(atoken, stack, userId, this);
+ Task task = new Task(taskId, stack, userId, this);
mTaskIdToTask.put(taskId, task);
stack.addTask(task, !atoken.mLaunchTaskBehind /* toTop */);
return task;
@@ -3657,7 +3625,6 @@
}
atoken = new AppWindowToken(this, token, voiceInteraction);
atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
- atoken.groupId = taskId;
atoken.appFullscreen = fullscreen;
atoken.showWhenLocked = showWhenLocked;
atoken.requestedOrientation = requestedOrientation;
@@ -3669,10 +3636,9 @@
Task task = mTaskIdToTask.get(taskId);
if (task == null) {
- createTask(taskId, stackId, userId, atoken);
- } else {
- task.addAppToken(addPos, atoken);
+ task = createTaskLocked(taskId, stackId, userId, atoken);
}
+ task.addAppToken(addPos, atoken);
mTokenMap.put(token.asBinder(), atoken);
@@ -3685,28 +3651,27 @@
}
@Override
- public void setAppGroupId(IBinder token, int groupId) {
+ public void setAppTask(IBinder token, int taskId) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
- "setAppGroupId()")) {
+ "setAppTask()")) {
throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
}
synchronized(mWindowMap) {
final AppWindowToken atoken = findAppWindowToken(token);
if (atoken == null) {
- Slog.w(TAG, "Attempted to set group id of non-existing app token: " + token);
+ Slog.w(TAG, "Attempted to set task id of non-existing app token: " + token);
return;
}
- final Task oldTask = mTaskIdToTask.get(atoken.groupId);
+ final Task oldTask = atoken.mTask;
oldTask.removeAppToken(atoken);
- atoken.groupId = groupId;
- Task newTask = mTaskIdToTask.get(groupId);
+ Task newTask = mTaskIdToTask.get(taskId);
if (newTask == null) {
- newTask = createTask(groupId, oldTask.mStack.mStackId, oldTask.mUserId, atoken);
- } else {
- newTask.mAppTokens.add(atoken);
+ newTask =
+ createTaskLocked(taskId, oldTask.mStack.mStackId, oldTask.mUserId, atoken);
}
+ newTask.addAppToken(Integer.MAX_VALUE /* at top */, atoken);
}
}
@@ -3990,7 +3955,7 @@
void setFocusedStackFrame() {
final TaskStack stack;
if (mFocusedApp != null) {
- Task task = mTaskIdToTask.get(mFocusedApp.groupId);
+ final Task task = mFocusedApp.mTask;
stack = task.mStack;
final DisplayContent displayContent = task.getDisplayContent();
if (displayContent != null) {
@@ -4812,7 +4777,7 @@
+ " animating=" + wtoken.mAppAnimator.animating);
if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG, "removeAppToken: "
+ wtoken + " delayed=" + delayed + " Callers=" + Debug.getCallers(4));
- final TaskStack stack = mTaskIdToTask.get(wtoken.groupId).mStack;
+ final TaskStack stack = wtoken.mTask.mStack;
if (delayed && !wtoken.allAppWindows.isEmpty()) {
// set the token aside because it has an active animation to be finished
if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
@@ -4877,7 +4842,7 @@
final int numTasks = tasks.size();
for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) {
final Task task = tasks.get(taskNdx);
- Slog.v(TAG, " Task #" + task.taskId + " activities from bottom to top:");
+ Slog.v(TAG, " Task #" + task.mTaskId + " activities from bottom to top:");
AppTokenList tokens = task.mAppTokens;
final int numTokens = tokens.size();
for (int tokenNdx = 0; tokenNdx < numTokens; ++tokenNdx) {
@@ -5128,7 +5093,12 @@
}
}
- public void resizeStack(int stackId, Rect bounds) {
+ /**
+ * Re-sizes the specified stack and its containing windows.
+ * Returns a {@link Configuration} object that contains configurations settings
+ * that should be overridden due to the operation.
+ */
+ public Configuration resizeStack(int stackId, Rect bounds) {
synchronized (mWindowMap) {
final TaskStack stack = mStackIdToStack.get(stackId);
if (stack == null) {
@@ -5140,6 +5110,7 @@
stack.getDisplayContent().layoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
+ return new Configuration(stack.mOverrideConfig);
}
}
@@ -7068,7 +7039,7 @@
return sw;
}
- boolean computeScreenConfigurationLocked(Configuration config) {
+ private boolean computeScreenConfigurationLocked(Configuration config) {
if (!mDisplayReady) {
return false;
}
@@ -7951,16 +7922,16 @@
break;
case TAP_OUTSIDE_STACK: {
- int stackId;
- synchronized (mWindowMap) {
- stackId = ((DisplayContent)msg.obj).stackIdFromPoint(msg.arg1, msg.arg2);
- }
- if (stackId >= 0) {
- try {
- mActivityManager.setFocusedStack(stackId);
- } catch (RemoteException e) {
- }
- }
+// int stackId;
+// synchronized (mWindowMap) {
+// stackId = ((DisplayContent)msg.obj).stackIdFromPoint(msg.arg1, msg.arg2);
+// }
+// if (stackId >= 0) {
+// try {
+// mActivityManager.setFocusedStack(stackId);
+// } catch (RemoteException e) {
+// }
+// }
}
break;
case NOTIFY_ACTIVITY_DRAWN:
@@ -8476,7 +8447,7 @@
numRemoved++;
continue;
} else if (lastBelow == i-1) {
- if (w.mAttrs.type == TYPE_WALLPAPER || w.mAttrs.type == TYPE_UNIVERSE_BACKGROUND) {
+ if (w.mAttrs.type == TYPE_WALLPAPER) {
lastBelow = i;
}
}
@@ -8731,8 +8702,6 @@
+ displayContent.layoutNeeded + " dw=" + dw + " dh=" + dh);
}
- WindowStateAnimator universeBackground = null;
-
mPolicy.beginLayoutLw(isDefaultDisplay, dw, dh, mRotation);
if (isDefaultDisplay) {
// Not needed on non-default displays.
@@ -8790,8 +8759,7 @@
|| ((win.isConfigChanged() || win.setInsetsChanged()) &&
((win.mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 ||
(win.mHasSurface && win.mAppToken != null &&
- win.mAppToken.layoutConfigChanges)))
- || win.mAttrs.type == TYPE_UNIVERSE_BACKGROUND) {
+ win.mAppToken.layoutConfigChanges)))) {
if (!win.mLayoutAttached) {
if (initial) {
//Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
@@ -8815,16 +8783,6 @@
if (topAttached < 0) topAttached = i;
}
}
- if (win.mViewVisibility == View.VISIBLE
- && win.mAttrs.type == TYPE_UNIVERSE_BACKGROUND
- && universeBackground == null) {
- universeBackground = win.mWinAnimator;
- }
- }
-
- if (mAnimator.mUniverseBackground != universeBackground) {
- mFocusMayChange = true;
- mAnimator.mUniverseBackground = universeBackground;
}
boolean attachedBehindDream = false;
@@ -10367,11 +10325,6 @@
}
private WindowState computeFocusedWindowLocked() {
- if (mAnimator.mUniverseBackground != null
- && mAnimator.mUniverseBackground.mWin.canReceiveKeys()) {
- return mAnimator.mUniverseBackground.mWin;
- }
-
final int displayCount = mDisplayContents.size();
for (int i = 0; i < displayCount; i++) {
final DisplayContent displayContent = mDisplayContents.valueAt(i);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 978f5c3..98f00de 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -130,7 +130,8 @@
int mLayoutSeq = -1;
- Configuration mConfiguration = null;
+ private Configuration mConfiguration = Configuration.EMPTY;
+ private Configuration mOverrideConfig = Configuration.EMPTY;
// Sticky answer to isConfigChanged(), remains true until new Configuration is assigned.
// Used only on {@link #TYPE_KEYGUARD}.
private boolean mConfigHasChanged;
@@ -792,14 +793,14 @@
TaskStack getStack() {
AppWindowToken wtoken = mAppToken == null ? mService.mFocusedApp : mAppToken;
if (wtoken != null) {
- Task task = mService.mTaskIdToTask.get(wtoken.groupId);
+ Task task = wtoken.mTask;
if (task != null) {
if (task.mStack != null) {
return task.mStack;
}
Slog.e(TAG, "getStack: mStack null for task=" + task);
} else {
- Slog.e(TAG, "getStack: " + this + " couldn't find taskId=" + wtoken.groupId
+ Slog.e(TAG, "getStack: " + this + " couldn't find task for " + wtoken
+ " Callers=" + Debug.getCallers(4));
}
}
@@ -1076,9 +1077,13 @@
}
boolean isConfigChanged() {
- boolean configChanged = mConfiguration != mService.mCurConfiguration
- && (mConfiguration == null
- || (mConfiguration.diff(mService.mCurConfiguration) != 0));
+ final TaskStack stack = getStack();
+ final Configuration overrideConfig =
+ (stack != null) ? stack.mOverrideConfig : Configuration.EMPTY;
+ final Configuration serviceConfig = mService.mCurConfiguration;
+ boolean configChanged =
+ (mConfiguration != serviceConfig && mConfiguration.diff(serviceConfig) != 0)
+ || (mOverrideConfig != overrideConfig && !mOverrideConfig.equals(overrideConfig));
if ((mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
// Retain configuration changed status until resetConfiguration called.
@@ -1107,8 +1112,10 @@
}
}
- void setConfiguration(final Configuration newConfig) {
+ private void setConfiguration(
+ final Configuration newConfig, final Configuration newOverrideConfig) {
mConfiguration = newConfig;
+ mOverrideConfig = newOverrideConfig;
mConfigHasChanged = false;
}
@@ -1384,12 +1391,15 @@
if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this
+ ": " + mCompatFrame);
boolean configChanged = isConfigChanged();
+ final TaskStack stack = getStack();
+ final Configuration overrideConfig =
+ (stack != null) ? stack.mOverrideConfig : Configuration.EMPTY;
if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION) && configChanged) {
Slog.i(TAG, "Sending new config to window " + this + ": "
- + mWinAnimator.mSurfaceW + "x" + mWinAnimator.mSurfaceH
- + " / " + mService.mCurConfiguration);
+ + mWinAnimator.mSurfaceW + "x" + mWinAnimator.mSurfaceH + " / config="
+ + mService.mCurConfiguration + " overrideConfig=" + overrideConfig);
}
- setConfiguration(mService.mCurConfiguration);
+ setConfiguration(mService.mCurConfiguration, overrideConfig);
if (DEBUG_ORIENTATION && mWinAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING)
Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING");
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index c2d8004..8202880 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -85,10 +85,6 @@
final Context mContext;
final boolean mIsWallpaper;
- // If this is a universe background window, this is the transformation
- // it is applying to the rest of the universe.
- final Transformation mUniverseTransform = new Transformation();
-
// Currently running animation.
boolean mAnimating;
boolean mLocalAnimating;
@@ -1096,9 +1092,6 @@
if (appTransformation != null) {
tmpMatrix.postConcat(appTransformation.getMatrix());
}
- if (mAnimator.mUniverseBackground != null) {
- tmpMatrix.postConcat(mAnimator.mUniverseBackground.mUniverseTransform.getMatrix());
- }
if (screenAnimation) {
tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix());
}
@@ -1164,9 +1157,6 @@
mHasClipRect = true;
}
}
- if (mAnimator.mUniverseBackground != null) {
- mShownAlpha *= mAnimator.mUniverseBackground.mUniverseTransform.getAlpha();
- }
if (screenAnimation) {
mShownAlpha *= screenRotationAnimation.getEnterTransformation().getAlpha();
}
@@ -1192,15 +1182,12 @@
TAG, "computeShownFrameLocked: " + this +
" not attached, mAlpha=" + mAlpha);
- final boolean applyUniverseTransformation = (mAnimator.mUniverseBackground != null
- && mWin.mAttrs.type != WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND
- && mWin.mBaseLayer < mAnimator.mAboveUniverseLayer);
MagnificationSpec spec = null;
//TODO (multidisplay): Magnification is supported only for the default display.
if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) {
spec = mService.mAccessibilityController.getMagnificationSpecForWindowLocked(mWin);
}
- if (applyUniverseTransformation || spec != null) {
+ if (spec != null) {
final Rect frame = mWin.mFrame;
final float tmpFloats[] = mService.mTmpFloats;
final Matrix tmpMatrix = mWin.mTmpMatrix;
@@ -1208,10 +1195,6 @@
tmpMatrix.setScale(mWin.mGlobalScale, mWin.mGlobalScale);
tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset);
- if (applyUniverseTransformation) {
- tmpMatrix.postConcat(mAnimator.mUniverseBackground.mUniverseTransform.getMatrix());
- }
-
if (spec != null && !spec.isNop()) {
tmpMatrix.postScale(spec.scale, spec.scale);
tmpMatrix.postTranslate(spec.offsetX, spec.offsetY);
@@ -1231,9 +1214,6 @@
mWin.mShownFrame.set(x, y, x + w, y + h);
mShownAlpha = mAlpha;
- if (applyUniverseTransformation) {
- mShownAlpha *= mAnimator.mUniverseBackground.mUniverseTransform.getAlpha();
- }
} else {
mWin.mShownFrame.set(mWin.mFrame);
if (mWin.mXOffset != 0 || mWin.mYOffset != 0) {
@@ -1301,17 +1281,9 @@
displayInfo.logicalHeight - w.mCompatFrame.top);
} else if (w.mLayer >= mService.mSystemDecorLayer) {
// Above the decor layer is easy, just use the entire window.
- // Unless we have a universe background... in which case all the
- // windows need to be cropped by the screen, so they don't cover
- // the universe background.
- if (mAnimator.mUniverseBackground == null) {
- w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
- } else {
- applyDecorRect(mService.mScreenRect);
- }
- } else if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND
- || w.mDecorFrame.isEmpty()) {
- // The universe background isn't cropped, nor windows without policy decor.
+ w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
+ } else if (w.mDecorFrame.isEmpty()) {
+ // Windows without policy decor aren't cropped.
w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height());
} else if (w.mAttrs.type == LayoutParams.TYPE_WALLPAPER && mAnimator.mAnimating) {
// If we're animating, the wallpaper crop should only be updated at the end of the
@@ -1932,11 +1904,6 @@
pw.print(prefix); pw.print("mSurfaceResized="); pw.print(mSurfaceResized);
pw.print(" mSurfaceDestroyDeferred="); pw.println(mSurfaceDestroyDeferred);
}
- if (mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND) {
- pw.print(prefix); pw.print("mUniverseTransform=");
- mUniverseTransform.printShortString(pw);
- pw.println();
- }
if (mShownAlpha != 1 || mAlpha != 1 || mLastAlpha != 1) {
pw.print(prefix); pw.print("mShownAlpha="); pw.print(mShownAlpha);
pw.print(" mAlpha="); pw.print(mAlpha);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e4d0b77..bec0f66 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1914,12 +1914,12 @@
}
}
- public void setPasswordQuality(ComponentName who, int quality, int userHandle) {
+ public void setPasswordQuality(ComponentName who, int quality) {
if (!mHasFeature) {
return;
}
+ final int userHandle = UserHandle.getCallingUserId();
validateQualityConstant(quality);
- enforceCrossUserPermission(userHandle);
synchronized (this) {
if (who == null) {
@@ -1963,11 +1963,11 @@
}
}
- public void setPasswordMinimumLength(ComponentName who, int length, int userHandle) {
+ public void setPasswordMinimumLength(ComponentName who, int length) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -2010,11 +2010,11 @@
}
}
- public void setPasswordHistoryLength(ComponentName who, int length, int userHandle) {
+ public void setPasswordHistoryLength(ComponentName who, int length) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -2057,11 +2057,11 @@
}
}
- public void setPasswordExpirationTimeout(ComponentName who, long timeout, int userHandle) {
+ public void setPasswordExpirationTimeout(ComponentName who, long timeout) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -2226,11 +2226,11 @@
}
}
- public void setPasswordMinimumUpperCase(ComponentName who, int length, int userHandle) {
+ public void setPasswordMinimumUpperCase(ComponentName who, int length) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -2273,8 +2273,8 @@
}
}
- public void setPasswordMinimumLowerCase(ComponentName who, int length, int userHandle) {
- enforceCrossUserPermission(userHandle);
+ public void setPasswordMinimumLowerCase(ComponentName who, int length) {
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -2317,11 +2317,11 @@
}
}
- public void setPasswordMinimumLetters(ComponentName who, int length, int userHandle) {
+ public void setPasswordMinimumLetters(ComponentName who, int length) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -2364,11 +2364,11 @@
}
}
- public void setPasswordMinimumNumeric(ComponentName who, int length, int userHandle) {
+ public void setPasswordMinimumNumeric(ComponentName who, int length) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -2411,11 +2411,11 @@
}
}
- public void setPasswordMinimumSymbols(ComponentName who, int length, int userHandle) {
+ public void setPasswordMinimumSymbols(ComponentName who, int length) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -2458,11 +2458,11 @@
}
}
- public void setPasswordMinimumNonLetter(ComponentName who, int length, int userHandle) {
+ public void setPasswordMinimumNonLetter(ComponentName who, int length) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -2522,8 +2522,7 @@
// This API can only be called by an active device admin,
// so try to retrieve it to check that the caller is one.
- getActiveAdminForCallerLocked(null,
- DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
+ getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
if (policy.mActivePasswordQuality < getPasswordQuality(null, userHandle)
|| policy.mActivePasswordLength < getPasswordMinimumLength(null, userHandle)) {
return false;
@@ -2556,11 +2555,11 @@
}
}
- public void setMaximumFailedPasswordsForWipe(ComponentName who, int num, int userHandle) {
+ public void setMaximumFailedPasswordsForWipe(ComponentName who, int num) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -2632,11 +2631,11 @@
return strictestAdmin;
}
- public boolean resetPassword(String passwordOrNull, int flags, int userHandle) {
+ public boolean resetPassword(String passwordOrNull, int flags) {
if (!mHasFeature) {
return false;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
enforceNotManagedProfile(userHandle, "reset the password");
String password = passwordOrNull != null ? passwordOrNull : "";
@@ -2767,11 +2766,11 @@
return true;
}
- public void setMaximumTimeToLock(ComponentName who, long timeMs, int userHandle) {
+ public void setMaximumTimeToLock(ComponentName who, long timeMs) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -3231,11 +3230,10 @@
}
public ComponentName setGlobalProxy(ComponentName who, String proxySpec,
- String exclusionList, int userHandle) {
+ String exclusionList) {
if (!mHasFeature) {
return null;
}
- enforceCrossUserPermission(userHandle);
synchronized(this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -3261,7 +3259,7 @@
// If the user is not the owner, don't set the global proxy. Fail silently.
if (UserHandle.getCallingUserId() != UserHandle.USER_OWNER) {
Slog.w(LOG_TAG, "Only the owner is allowed to set the global proxy. User "
- + userHandle + " is not permitted.");
+ + UserHandle.getCallingUserId() + " is not permitted.");
return null;
}
if (proxySpec == null) {
@@ -3371,11 +3369,11 @@
* Set the storage encryption request for a single admin. Returns the new total request
* status (for all admins).
*/
- public int setStorageEncryption(ComponentName who, boolean encrypt, int userHandle) {
+ public int setStorageEncryption(ComponentName who, boolean encrypt) {
if (!mHasFeature) {
return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
// Check for permissions
if (who == null) {
@@ -3507,11 +3505,11 @@
/**
* Set whether the screen capture is disabled for the user managed by the specified admin.
*/
- public void setScreenCaptureDisabled(ComponentName who, int userHandle, boolean disabled) {
+ public void setScreenCaptureDisabled(ComponentName who, boolean disabled) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -3566,11 +3564,11 @@
/**
* Set whether auto time is required by the specified admin (must be device owner).
*/
- public void setAutoTimeRequired(ComponentName who, int userHandle, boolean required) {
+ public void setAutoTimeRequired(ComponentName who, boolean required) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -3617,11 +3615,11 @@
/**
* Disables all device cameras according to the specified admin.
*/
- public void setCameraDisabled(ComponentName who, boolean disabled, int userHandle) {
+ public void setCameraDisabled(ComponentName who, boolean disabled) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -3666,11 +3664,11 @@
/**
* Selectively disable keyguard features.
*/
- public void setKeyguardDisabledFeatures(ComponentName who, int which, int userHandle) {
+ public void setKeyguardDisabledFeatures(ComponentName who, int which) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
enforceNotManagedProfile(userHandle, "disable keyguard features");
synchronized (this) {
if (who == null) {
@@ -3920,7 +3918,7 @@
Bundle userRestrictions = mUserManager.getUserRestrictions();
mUserManager.setUserRestrictions(new Bundle(), userHandle);
if (userRestrictions.getBoolean(UserManager.DISALLOW_ADJUST_VOLUME)) {
- audioManager.setMasterMute(false);
+ audioManager.adjustMasterVolume(AudioManager.ADJUST_UNMUTE, 0);
}
if (userRestrictions.getBoolean(UserManager.DISALLOW_UNMUTE_MICROPHONE)) {
audioManager.setMicrophoneMute(false);
@@ -4216,11 +4214,11 @@
}
public void setTrustAgentConfiguration(ComponentName admin, ComponentName agent,
- PersistableBundle args, int userHandle) {
+ PersistableBundle args) {
if (!mHasFeature) {
return;
}
- enforceCrossUserPermission(userHandle);
+ final int userHandle = UserHandle.getCallingUserId();
enforceNotManagedProfile(userHandle, "set trust agent configuration");
synchronized (this) {
if (admin == null) {
@@ -4841,7 +4839,8 @@
if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
iAudioService.setMicrophoneMute(true, who.getPackageName());
} else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
- iAudioService.setMasterMute(true, 0, who.getPackageName(), null);
+ iAudioService.adjustMasterVolume(AudioManager.ADJUST_MUTE, 0,
+ who.getPackageName());
}
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Failed to talk to AudioService.", re);
@@ -4906,7 +4905,8 @@
if (UserManager.DISALLOW_UNMUTE_MICROPHONE.equals(key)) {
iAudioService.setMicrophoneMute(false, who.getPackageName());
} else if (UserManager.DISALLOW_ADJUST_VOLUME.equals(key)) {
- iAudioService.setMasterMute(false, 0, who.getPackageName(), null);
+ iAudioService.adjustMasterVolume(AudioManager.ADJUST_UNMUTE, 0,
+ who.getPackageName());
}
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Failed to talk to AudioService.", re);
@@ -5361,8 +5361,6 @@
@Override
public void setMasterVolumeMuted(ComponentName who, boolean on) {
- final ContentResolver contentResolver = mContext.getContentResolver();
-
synchronized (this) {
if (who == null) {
throw new NullPointerException("ComponentName is null");
@@ -5372,7 +5370,9 @@
IAudioService iAudioService = IAudioService.Stub.asInterface(
ServiceManager.getService(Context.AUDIO_SERVICE));
try{
- iAudioService.setMasterMute(on, 0, who.getPackageName(), null);
+ iAudioService.adjustMasterVolume(
+ on ? AudioManager.ADJUST_MUTE : AudioManager.ADJUST_UNMUTE, 0,
+ who.getPackageName());
} catch (RemoteException re) {
Slog.e(LOG_TAG, "Failed to setMasterMute", re);
}
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 01a044e..b508c89 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -20,12 +20,15 @@
import android.alsa.AlsaDevicesParser;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbInterface;
import android.media.AudioSystem;
import android.media.IAudioService;
+import android.midi.MidiDeviceInfo;
import android.os.FileObserver;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -73,6 +76,9 @@
private UsbAudioDevice mSelectedAudioDevice = null;
+ // UsbMidiDevice for USB peripheral mode (gadget) device
+ private UsbMidiDevice mPeripheralMidiDevice = null;
+
private final class AlsaDevice {
public static final int TYPE_UNKNOWN = 0;
public static final int TYPE_PLAYBACK = 1;
@@ -391,7 +397,17 @@
int device = mDevicesParser.getDefaultDeviceNum(addedCard);
AlsaDevice alsaDevice = waitForAlsaDevice(addedCard, device, AlsaDevice.TYPE_MIDI);
if (alsaDevice != null) {
- UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, usbDevice,
+ Bundle properties = new Bundle();
+ properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER,
+ usbDevice.getManufacturerName());
+ properties.putString(MidiDeviceInfo.PROPERTY_MODEL, usbDevice.getProductName());
+ properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER,
+ usbDevice.getSerialNumber());
+ properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, alsaDevice.mCard);
+ properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, alsaDevice.mDevice);
+ properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice);
+
+ UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, properties,
alsaDevice.mCard, alsaDevice.mDevice);
if (usbMidiDevice != null) {
mMidiDevices.put(usbDevice, usbMidiDevice);
@@ -410,17 +426,16 @@
if (audioDevice != null) {
if (audioDevice.mHasPlayback || audioDevice.mHasPlayback) {
notifyDeviceState(audioDevice, false);
+ mSelectedAudioDevice = null;
+
+ // if there any external devices left, select one of them
+ selectDefaultDevice();
}
}
UsbMidiDevice usbMidiDevice = mMidiDevices.remove(usbDevice);
if (usbMidiDevice != null) {
IoUtils.closeQuietly(usbMidiDevice);
}
-
- mSelectedAudioDevice = null;
-
- // if there any external devices left, select one of them
- selectDefaultDevice();
}
/* package */ void setAccessoryAudioState(boolean enabled, int card, int device) {
@@ -437,6 +452,23 @@
}
}
+ /* package */ void setPeripheralMidiState(boolean enabled, int card, int device) {
+ if (enabled) {
+ Bundle properties = new Bundle();
+ Resources r = mContext.getResources();
+ properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, r.getString(
+ com.android.internal.R.string.usb_midi_peripheral_manufacturer_name));
+ properties.putString(MidiDeviceInfo.PROPERTY_MODEL, r.getString(
+ com.android.internal.R.string.usb_midi_peripheral_model_name));
+ properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, card);
+ properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, device);
+ mPeripheralMidiDevice = UsbMidiDevice.create(mContext, properties, card, device);
+ } else if (mPeripheralMidiDevice != null) {
+ IoUtils.closeQuietly(mPeripheralMidiDevice);
+ mPeripheralMidiDevice = null;
+ }
+ }
+
//
// Devices List
//
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 1426551..2fb6dbf 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -31,6 +31,7 @@
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
import android.media.AudioManager;
+import android.midi.MidiDeviceInfo;
import android.os.FileUtils;
import android.os.Handler;
import android.os.Looper;
@@ -85,6 +86,8 @@
"/sys/class/android_usb/android0/f_rndis/ethaddr";
private static final String AUDIO_SOURCE_PCM_PATH =
"/sys/class/android_usb/android0/f_audio_source/pcm";
+ private static final String MIDI_ALSA_PATH =
+ "/sys/class/android_usb/android0/f_midi/alsa";
private static final int MSG_UPDATE_STATE = 0;
private static final int MSG_ENABLE_ADB = 1;
@@ -124,6 +127,7 @@
private boolean mUseUsbNotification;
private boolean mAdbEnabled;
private boolean mAudioSourceEnabled;
+ private boolean mMidiEnabled;
private Map<String, List<Pair<String, String>>> mOemModeMap;
private String[] mAccessoryStrings;
private UsbDebuggingManager mDebuggingManager;
@@ -618,6 +622,31 @@
}
}
+ private void updateMidiFunction() {
+ boolean enabled = containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MIDI);
+ if (enabled != mMidiEnabled) {
+ int card = -1;
+ int device = -1;
+
+ if (enabled) {
+ Scanner scanner = null;
+ try {
+ scanner = new Scanner(new File(MIDI_ALSA_PATH));
+ card = scanner.nextInt();
+ device = scanner.nextInt();
+ } catch (FileNotFoundException e) {
+ Slog.e(TAG, "could not open MIDI PCM file", e);
+ } finally {
+ if (scanner != null) {
+ scanner.close();
+ }
+ }
+ }
+ mUsbAlsaManager.setPeripheralMidiState(enabled, card, device);
+ mMidiEnabled = enabled;
+ }
+ }
+
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -636,6 +665,7 @@
if (mBootCompleted) {
updateUsbState();
updateAudioSourceFunction();
+ updateMidiFunction();
}
break;
case MSG_ENABLE_ADB:
@@ -651,6 +681,7 @@
updateAdbNotification();
updateUsbState();
updateAudioSourceFunction();
+ updateMidiFunction();
break;
case MSG_BOOT_COMPLETED:
mBootCompleted = true;
diff --git a/services/usb/java/com/android/server/usb/UsbMidiDevice.java b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
index 01b2df9..8d44905 100644
--- a/services/usb/java/com/android/server/usb/UsbMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
@@ -17,13 +17,12 @@
package com.android.server.usb;
import android.content.Context;
-import android.hardware.usb.UsbDevice;
import android.midi.MidiDeviceInfo;
import android.midi.MidiDeviceServer;
import android.midi.MidiManager;
+import android.midi.MidiPort;
import android.midi.MidiReceiver;
import android.midi.MidiSender;
-import android.midi.MidiUtils;
import android.os.Bundle;
import android.system.ErrnoException;
import android.system.Os;
@@ -45,10 +44,12 @@
// for polling multiple FileDescriptors for MIDI events
private final StructPollfd[] mPollFDs;
+ // streams for reading from ALSA driver
private final FileInputStream[] mInputStreams;
+ // streams for writing to ALSA driver
private final FileOutputStream[] mOutputStreams;
- public static UsbMidiDevice create(Context context, UsbDevice usbDevice, int card, int device) {
+ public static UsbMidiDevice create(Context context, Bundle properties, int card, int device) {
MidiManager midiManager = (MidiManager)context.getSystemService(Context.MIDI_SERVICE);
if (midiManager == null) {
Log.e(TAG, "No MidiManager in UsbMidiDevice.create()");
@@ -69,13 +70,6 @@
return null;
}
- Bundle properties = new Bundle();
- properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, usbDevice.getManufacturerName());
- properties.putString(MidiDeviceInfo.PROPERTY_MODEL, usbDevice.getProductName());
- properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER, usbDevice.getSerialNumber());
- properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, card);
- properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, device);
- properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice);
MidiDeviceServer server = midiManager.createDeviceServer(subDevices, subDevices, properties,
false, MidiDeviceInfo.TYPE_USB);
if (server == null) {
@@ -131,7 +125,7 @@
new Thread() {
@Override
public void run() {
- byte[] buffer = new byte[3];
+ byte[] buffer = new byte[MidiPort.MAX_PACKET_DATA_SIZE];
try {
boolean done = false;
while (!done) {
@@ -141,7 +135,8 @@
if ((pfd.revents & OsConstants.POLLIN) != 0) {
// clear readable flag
pfd.revents = 0;
- int count = readMessage(buffer, index);
+
+ int count = mInputStreams[index].read(buffer);
mOutputPortReceivers[index].onPost(buffer, 0, count,
System.nanoTime());
} else if ((pfd.revents & (OsConstants.POLLERR
@@ -150,7 +145,7 @@
}
}
- // poll if none are readable
+ // wait until we have a readable port
Os.poll(mPollFDs, -1 /* infinite timeout */);
}
} catch (IOException e) {
@@ -174,26 +169,6 @@
}
}
- private int readMessage(byte[] buffer, int index) throws IOException {
- FileInputStream inputStream = mInputStreams[index];
-
- if (inputStream.read(buffer, 0, 1) != 1) {
- Log.e(TAG, "could not read command byte");
- return -1;
- }
- int dataSize = MidiUtils.getMessageDataSize(buffer[0]);
- if (dataSize < 0) {
- return -1;
- }
- if (dataSize > 0) {
- if (inputStream.read(buffer, 1, dataSize) != dataSize) {
- Log.e(TAG, "could not read command data");
- return -1;
- }
- }
- return dataSize + 1;
- }
-
private static native int nativeGetSubdeviceCount(int card, int device);
private static native FileDescriptor[] nativeOpen(int card, int device, int subdeviceCount);
}
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 073dcd5..43a92cb 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -235,8 +235,12 @@
@Override
public void setVideoProvider(String callId, IVideoProvider videoProvider) {
+ RemoteConnection.VideoProvider remoteVideoProvider = null;
+ if (videoProvider != null) {
+ remoteVideoProvider = new RemoteConnection.VideoProvider(videoProvider);
+ }
findConnectionForAction(callId, "setVideoProvider")
- .setVideoProvider(new RemoteConnection.VideoProvider(videoProvider));
+ .setVideoProvider(remoteVideoProvider);
}
@Override
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index a59505c..07476e3 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -39,8 +39,6 @@
import android.util.SparseIntArray;
import static com.android.internal.telephony.PhoneConstants.SUBSCRIPTION_KEY;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY;
import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_IDP_STRING;
import java.util.Locale;
@@ -2200,8 +2198,8 @@
if (!TextUtils.isEmpty(dialStr)) {
if (isReallyDialable(dialStr.charAt(0)) &&
isNonSeparator(dialStr)) {
- String currIso = SystemProperties.get(PROPERTY_OPERATOR_ISO_COUNTRY, "");
- String defaultIso = SystemProperties.get(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
+ String currIso = TelephonyManager.getDefault().getNetworkCountryIso();
+ String defaultIso = TelephonyManager.getDefault().getSimCountryIso();
if (!TextUtils.isEmpty(currIso) && !TextUtils.isEmpty(defaultIso)) {
return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr,
getFormatTypeFromCountryCode(currIso),
@@ -2223,7 +2221,7 @@
public static String cdmaCheckAndProcessPlusCodeForSms(String dialStr) {
if (!TextUtils.isEmpty(dialStr)) {
if (isReallyDialable(dialStr.charAt(0)) && isNonSeparator(dialStr)) {
- String defaultIso = SystemProperties.get(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
+ String defaultIso = TelephonyManager.getDefault().getSimCountryIso();
if (!TextUtils.isEmpty(defaultIso)) {
int format = getFormatTypeFromCountryCode(defaultIso);
return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr, format, format);
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index adbe1d8..ca3c636 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -335,7 +335,7 @@
public String toString() {
return "{id=" + mId + ", iccId=" + mIccId + " simSlotIndex=" + mSimSlotIndex
+ " displayName=" + mDisplayName + " carrierName=" + mCarrierName
- + " nameSource=" + mNameSource + " iconTint=" + mIconTint + " number=" + mNumber
+ + " nameSource=" + mNameSource + " iconTint=" + mIconTint
+ " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc
+ " mnc " + mMnc + "}";
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index c67629d..aca94e9 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1101,9 +1101,7 @@
// What else can we do?
return false;
}
- // FIXME: use better way to get roaming status instead of reading from system property
- return Boolean.parseBoolean(TelephonyManager.getTelephonyProperty(phoneId,
- TelephonyProperties.PROPERTY_OPERATOR_ISROAMING, null));
+ return TelephonyManager.getDefault().isNetworkRoaming(subId);
}
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ace945d..ba5a679 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -640,13 +640,9 @@
*/
/** {@hide} */
public String getDeviceId(int slotId) {
- // FIXME methods taking slot id should not use subscription, instead us Uicc directly
- int[] subId = SubscriptionManager.getSubId(slotId);
- if (subId == null || subId.length == 0) {
- return null;
- }
+ // FIXME this assumes phoneId == slotId
try {
- return getSubscriberInfo().getDeviceIdForSubscriber(subId[0]);
+ return getSubscriberInfo().getDeviceIdForPhone(slotId);
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -703,7 +699,11 @@
public String getNai(int slotId) {
int[] subId = SubscriptionManager.getSubId(slotId);
try {
- return getSubscriberInfo().getNaiForSubscriber(subId[0]);
+ String nai = getSubscriberInfo().getNaiForSubscriber(subId[0]);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Rlog.v(TAG, "Nai = " + nai);
+ }
+ return nai;
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
@@ -1077,7 +1077,7 @@
* on a CDMA network).
*/
public String getNetworkOperator() {
- return getNetworkOperator(getDefaultSubscription());
+ return getNetworkOperatorForPhone(getDefaultPhone());
}
/**
@@ -1091,8 +1091,23 @@
* @param subId
*/
/** {@hide} */
- public String getNetworkOperator(int subId) {
+ public String getNetworkOperatorForSubscription(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
+ return getNetworkOperatorForPhone(phoneId);
+ }
+
+ /**
+ * Returns the numeric name (MCC+MNC) of current registered operator
+ * for a particular subscription.
+ * <p>
+ * Availability: Only when user is registered to a network. Result may be
+ * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
+ * on a CDMA network).
+ *
+ * @param phoneId
+ * @hide
+ **/
+ public String getNetworkOperatorForPhone(int phoneId) {
return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, "");
}
@@ -1130,7 +1145,7 @@
* on a CDMA network).
*/
public String getNetworkCountryIso() {
- return getNetworkCountryIso(getDefaultSubscription());
+ return getNetworkCountryIsoForPhone(getDefaultPhone());
}
/**
@@ -1144,8 +1159,23 @@
* @param subId for which Network CountryIso is returned
*/
/** {@hide} */
- public String getNetworkCountryIso(int subId) {
+ public String getNetworkCountryIsoForSubscription(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
+ return getNetworkCountryIsoForPhone(phoneId);
+ }
+
+ /**
+ * Returns the ISO country code equivalent of the current registered
+ * operator's MCC (Mobile Country Code) of a subscription.
+ * <p>
+ * Availability: Only when user is registered to a network. Result may be
+ * unreliable on CDMA networks (use {@link #getPhoneType()} to determine if
+ * on a CDMA network).
+ *
+ * @param phoneId for which Network CountryIso is returned
+ */
+ /** {@hide} */
+ public String getNetworkCountryIsoForPhone(int phoneId) {
return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
}
@@ -1537,6 +1567,34 @@
* @see #getSimState
*/
public String getSimOperator() {
+ return getSimOperatorNumeric();
+ }
+
+ /**
+ * Returns the MCC+MNC (mobile country code + mobile network code) of the
+ * provider of the SIM. 5 or 6 decimal digits.
+ * <p>
+ * Availability: SIM state must be {@link #SIM_STATE_READY}
+ *
+ * @see #getSimState
+ *
+ * @param subId for which SimOperator is returned
+ * @hide
+ */
+ public String getSimOperator(int subId) {
+ return getSimOperatorNumericForSubscription(subId);
+ }
+
+ /**
+ * Returns the MCC+MNC (mobile country code + mobile network code) of the
+ * provider of the SIM. 5 or 6 decimal digits.
+ * <p>
+ * Availability: SIM state must be {@link #SIM_STATE_READY}
+ *
+ * @see #getSimState
+ * @hide
+ */
+ public String getSimOperatorNumeric() {
int subId = SubscriptionManager.getDefaultDataSubId();
if (!SubscriptionManager.isUsableSubIdValue(subId)) {
subId = SubscriptionManager.getDefaultSmsSubId();
@@ -1547,8 +1605,8 @@
}
}
}
- Rlog.d(TAG, "getSimOperator(): default subId=" + subId);
- return getSimOperator(subId);
+ Rlog.d(TAG, "getSimOperatorNumeric(): default subId=" + subId);
+ return getSimOperatorNumericForSubscription(subId);
}
/**
@@ -1560,14 +1618,24 @@
* @see #getSimState
*
* @param subId for which SimOperator is returned
+ * @hide
*/
- /** {@hide} */
- public String getSimOperator(int subId) {
+ public String getSimOperatorNumericForSubscription(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
- String operator = getTelephonyProperty(phoneId,
+ return getSimOperatorNumericForPhone(phoneId);
+ }
+
+ /**
+ * Returns the MCC+MNC (mobile country code + mobile network code) of the
+ * provider of the SIM for a particular subscription. 5 or 6 decimal digits.
+ * <p>
+ *
+ * @param phoneId for which SimOperator is returned
+ * @hide
+ */
+ public String getSimOperatorNumericForPhone(int phoneId) {
+ return getTelephonyProperty(phoneId,
TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, "");
- Rlog.d(TAG, "getSimOperator: subId=" + subId + " operator=" + operator);
- return operator;
}
/**
@@ -1578,7 +1646,7 @@
* @see #getSimState
*/
public String getSimOperatorName() {
- return getSimOperatorName(getDefaultSubscription());
+ return getSimOperatorNameForPhone(getDefaultPhone());
}
/**
@@ -1589,30 +1657,61 @@
* @see #getSimState
*
* @param subId for which SimOperatorName is returned
+ * @hide
*/
- /** {@hide} */
- public String getSimOperatorName(int subId) {
+ public String getSimOperatorNameForSubscription(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
- return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, "");
+ return getSimOperatorNameForPhone(phoneId);
+ }
+
+ /**
+ * Returns the Service Provider Name (SPN).
+ *
+ * @hide
+ */
+ public String getSimOperatorNameForPhone(int phoneId) {
+ return getTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, "");
}
/**
* Returns the ISO country code equivalent for the SIM provider's country code.
*/
public String getSimCountryIso() {
- return getSimCountryIso(getDefaultSubscription());
+ return getSimCountryIsoForPhone(getDefaultPhone());
}
/**
* Returns the ISO country code equivalent for the SIM provider's country code.
*
* @param subId for which SimCountryIso is returned
+ *
+ * @hide
*/
- /** {@hide} */
public String getSimCountryIso(int subId) {
+ return getSimCountryIsoForSubscription(subId);
+ }
+
+ /**
+ * Returns the ISO country code equivalent for the SIM provider's country code.
+ *
+ * @param subId for which SimCountryIso is returned
+ *
+ * @hide
+ */
+ public String getSimCountryIsoForSubscription(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
- return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY,
- "");
+ return getSimCountryIsoForPhone(phoneId);
+ }
+
+ /**
+ * Returns the ISO country code equivalent for the SIM provider's country code.
+ *
+ * @hide
+ */
+ public String getSimCountryIsoForPhone(int phoneId) {
+ return getTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
}
/**
@@ -3677,4 +3776,344 @@
return false;
}
}
+
+ /**
+ * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC for the default phone.
+ *
+ * @hide
+ */
+ public void setSimOperatorNumeric(String numeric) {
+ int phoneId = getDefaultPhone();
+ setSimOperatorNumericForPhone(phoneId, numeric);
+ }
+
+ /**
+ * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC for the given phone.
+ *
+ * @hide
+ */
+ public void setSimOperatorNumericForPhone(int phoneId, String numeric) {
+ setTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, numeric);
+ }
+
+ /**
+ * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC for the default phone.
+ *
+ * @hide
+ */
+ public void setSimOperatorName(String name) {
+ int phoneId = getDefaultPhone();
+ setSimOperatorNameForPhone(phoneId, name);
+ }
+
+ /**
+ * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC for the given phone.
+ *
+ * @hide
+ */
+ public void setSimOperatorNameForPhone(int phoneId, String name) {
+ setTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, name);
+ }
+
+ /**
+ * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY for the default phone.
+ *
+ * @hide
+ */
+ public void setSimCountryIso(String iso) {
+ int phoneId = getDefaultPhone();
+ setSimCountryIsoForPhone(phoneId, iso);
+ }
+
+ /**
+ * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY for the given phone.
+ *
+ * @hide
+ */
+ public void setSimCountryIsoForPhone(int phoneId, String iso) {
+ setTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY, iso);
+ }
+
+ /**
+ * Set TelephonyProperties.PROPERTY_SIM_STATE for the default phone.
+ *
+ * @hide
+ */
+ public void setSimState(String state) {
+ int phoneId = getDefaultPhone();
+ setSimStateForPhone(phoneId, state);
+ }
+
+ /**
+ * Set TelephonyProperties.PROPERTY_SIM_STATE for the given phone.
+ *
+ * @hide
+ */
+ public void setSimStateForPhone(int phoneId, String state) {
+ setTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_SIM_STATE, state);
+ }
+
+ /**
+ * Set baseband version for the default phone.
+ *
+ * @param version baseband version
+ * @hide
+ */
+ public void setBasebandVersion(String version) {
+ int phoneId = getDefaultPhone();
+ setBasebandVersionForPhone(phoneId, version);
+ }
+
+ /**
+ * Set baseband version by phone id.
+ *
+ * @param phoneId for which baseband version is set
+ * @param version baseband version
+ * @hide
+ */
+ public void setBasebandVersionForPhone(int phoneId, String version) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ String prop = TelephonyProperties.PROPERTY_BASEBAND_VERSION +
+ ((phoneId == 0) ? "" : Integer.toString(phoneId));
+ SystemProperties.set(prop, version);
+ }
+ }
+
+ /**
+ * Set phone type for the default phone.
+ *
+ * @param type phone type
+ *
+ * @hide
+ */
+ public void setPhoneType(int type) {
+ int phoneId = getDefaultPhone();
+ setPhoneType(phoneId, type);
+ }
+
+ /**
+ * Set phone type by phone id.
+ *
+ * @param phoneId for which phone type is set
+ * @param type phone type
+ *
+ * @hide
+ */
+ public void setPhoneType(int phoneId, int type) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ TelephonyManager.setTelephonyProperty(phoneId,
+ TelephonyProperties.CURRENT_ACTIVE_PHONE, String.valueOf(type));
+ }
+ }
+
+ /**
+ * Get OTASP number schema for the default phone.
+ *
+ * @param defaultValue default value
+ * @return OTA SP number schema
+ *
+ * @hide
+ */
+ public String getOtaSpNumberSchema(String defaultValue) {
+ int phoneId = getDefaultPhone();
+ return getOtaSpNumberSchemaForPhone(phoneId, defaultValue);
+ }
+
+ /**
+ * Get OTASP number schema by phone id.
+ *
+ * @param phoneId for which OTA SP number schema is get
+ * @param defaultValue default value
+ * @return OTA SP number schema
+ *
+ * @hide
+ */
+ public String getOtaSpNumberSchemaForPhone(int phoneId, String defaultValue) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ return TelephonyManager.getTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_OTASP_NUM_SCHEMA, defaultValue);
+ }
+
+ return defaultValue;
+ }
+
+ /**
+ * Get SMS receive capable from system property for the default phone.
+ *
+ * @param defaultValue default value
+ * @return SMS receive capable
+ *
+ * @hide
+ */
+ public boolean getSmsReceiveCapable(boolean defaultValue) {
+ int phoneId = getDefaultPhone();
+ return getSmsReceiveCapableForPhone(phoneId, defaultValue);
+ }
+
+ /**
+ * Get SMS receive capable from system property by phone id.
+ *
+ * @param phoneId for which SMS receive capable is get
+ * @param defaultValue default value
+ * @return SMS receive capable
+ *
+ * @hide
+ */
+ public boolean getSmsReceiveCapableForPhone(int phoneId, boolean defaultValue) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ return Boolean.valueOf(TelephonyManager.getTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_SMS_RECEIVE, String.valueOf(defaultValue)));
+ }
+
+ return defaultValue;
+ }
+
+ /**
+ * Get SMS send capable from system property for the default phone.
+ *
+ * @param defaultValue default value
+ * @return SMS send capable
+ *
+ * @hide
+ */
+ public boolean getSmsSendCapable(boolean defaultValue) {
+ int phoneId = getDefaultPhone();
+ return getSmsSendCapableForPhone(phoneId, defaultValue);
+ }
+
+ /**
+ * Get SMS send capable from system property by phone id.
+ *
+ * @param phoneId for which SMS send capable is get
+ * @param defaultValue default value
+ * @return SMS send capable
+ *
+ * @hide
+ */
+ public boolean getSmsSendCapableForPhone(int phoneId, boolean defaultValue) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ return Boolean.valueOf(TelephonyManager.getTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_SMS_SEND, String.valueOf(defaultValue)));
+ }
+
+ return defaultValue;
+ }
+
+ /**
+ * Set the alphabetic name of current registered operator.
+ * @param name the alphabetic name of current registered operator.
+ * @hide
+ */
+ public void setNetworkOperatorName(String name) {
+ int phoneId = getDefaultPhone();
+ setNetworkOperatorNameForPhone(phoneId, name);
+ }
+
+ /**
+ * Set the alphabetic name of current registered operator.
+ * @param phoneId which phone you want to set
+ * @param name the alphabetic name of current registered operator.
+ * @hide
+ */
+ public void setNetworkOperatorNameForPhone(int phoneId, String name) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ setTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_ALPHA, name);
+ }
+ }
+
+ /**
+ * Set the numeric name (MCC+MNC) of current registered operator.
+ * @param operator the numeric name (MCC+MNC) of current registered operator
+ * @hide
+ */
+ public void setNetworkOperatorNumeric(String numeric) {
+ int phoneId = getDefaultPhone();
+ setNetworkOperatorNumericForPhone(phoneId, numeric);
+ }
+
+ /**
+ * Set the numeric name (MCC+MNC) of current registered operator.
+ * @param phoneId for which phone type is set
+ * @param operator the numeric name (MCC+MNC) of current registered operator
+ * @hide
+ */
+ public void setNetworkOperatorNumericForPhone(int phoneId, String numeric) {
+ setTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, numeric);
+ }
+
+ /**
+ * Set roaming state of the current network, for GSM purposes.
+ * @param isRoaming is network in romaing state or not
+ * @hide
+ */
+ public void setNetworkRoaming(boolean isRoaming) {
+ int phoneId = getDefaultPhone();
+ setNetworkRoamingForPhone(phoneId, isRoaming);
+ }
+
+ /**
+ * Set roaming state of the current network, for GSM purposes.
+ * @param phoneId which phone you want to set
+ * @param isRoaming is network in romaing state or not
+ * @hide
+ */
+ public void setNetworkRoamingForPhone(int phoneId, boolean isRoaming) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ setTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_ISROAMING,
+ isRoaming ? "true" : "false");
+ }
+ }
+
+ /**
+ * Set the ISO country code equivalent of the current registered
+ * operator's MCC (Mobile Country Code).
+ * @param iso the ISO country code equivalent of the current registered
+ * @hide
+ */
+ public void setNetworkCountryIso(String iso) {
+ int phoneId = getDefaultPhone();
+ setNetworkCountryIsoForPhone(phoneId, iso);
+ }
+
+ /**
+ * Set the ISO country code equivalent of the current registered
+ * operator's MCC (Mobile Country Code).
+ * @param phoneId which phone you want to set
+ * @param iso the ISO country code equivalent of the current registered
+ * @hide
+ */
+ public void setNetworkCountryIsoForPhone(int phoneId, String iso) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ setTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, iso);
+ }
+ }
+
+ /**
+ * Set the network type currently in use on the device for data transmission.
+ * @param type the network type currently in use on the device for data transmission
+ * @hide
+ */
+ public void setDataNetworkType(int type) {
+ int phoneId = getDefaultPhone();
+ setDataNetworkTypeForPhone(phoneId, type);
+ }
+
+ /**
+ * Set the network type currently in use on the device for data transmission.
+ * @param phoneId which phone you want to set
+ * @param type the network type currently in use on the device for data transmission
+ * @hide
+ */
+ public void setDataNetworkTypeForPhone(int phoneId, int type) {
+ if (SubscriptionManager.isValidPhoneId(phoneId)) {
+ setTelephonyProperty(phoneId,
+ TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
+ ServiceState.rilRadioTechnologyToString(type));
+ }
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
index eec5333..c91a59c 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
@@ -33,10 +33,10 @@
String getNaiForSubscriber(int subId);
/**
- * Retrieves the unique device ID of a subId for the device, e.g., IMEI
+ * Retrieves the unique device ID of a phone for the device, e.g., IMEI
* for GSM phones.
*/
- String getDeviceIdForSubscriber(int subId);
+ String getDeviceIdForPhone(int phoneId);
/**
* Retrieves the IMEI.
diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl
index 6fdf121..70ac268 100644
--- a/telephony/java/com/android/internal/telephony/ISms.aidl
+++ b/telephony/java/com/android/internal/telephony/ISms.aidl
@@ -453,10 +453,6 @@
*/
void setPremiumSmsPermission(String packageName, int permission);
- /**
- * Set the SMS send permission for the specified package.
- * Requires system permission.
- */
/**
* Set the SMS send permission for the specified package.
* Requires system permission.
@@ -483,6 +479,14 @@
*/
boolean isImsSmsSupportedForSubscriber(int subId);
+ /**
+ * User needs to pick SIM for SMS if multiple SIMs present and if current subId passed in is not
+ * active/valid.
+ * @param subId current subId for sending SMS
+ * @return true if SIM for SMS sending needs to be chosen
+ */
+ boolean isSmsSimPickActivityNeeded(int subId);
+
/*
* get user prefered SMS subId
* @return subId id
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 62c92a1..e44969d 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -103,7 +103,7 @@
}
try {
- mWm.setAppGroupId(null, 0);
+ mWm.setAppTask(null, 0);
fail("IWindowManager.setAppGroupId did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
index a2bd6d7..18036927 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
@@ -614,15 +614,27 @@
int pos = value.indexOf('/');
String idName = value.substring(pos + 1);
+ boolean create = value.startsWith("@+");
+ boolean isFrameworkId =
+ mPlatformFile || value.startsWith("@android") || value.startsWith("@+android");
- // if this is a framework id
- if (mPlatformFile || value.startsWith("@android") || value.startsWith("@+android")) {
- // look for idName in the android R classes
- return mContext.getFrameworkResourceValue(ResourceType.ID, idName, defValue);
+ // Look for the idName in project or android R class depending on isPlatform.
+ if (create) {
+ Integer idValue;
+ if (isFrameworkId) {
+ idValue = Bridge.getResourceId(ResourceType.ID, idName);
+ } else {
+ idValue = mContext.getProjectCallback().getResourceId(ResourceType.ID, idName);
+ }
+ return idValue == null ? defValue : idValue;
}
-
- // look for idName in the project R class.
- return mContext.getProjectResourceValue(ResourceType.ID, idName, defValue);
+ // This calls the same method as in if(create), but doesn't create a dynamic id, if
+ // one is not found.
+ if (isFrameworkId) {
+ return mContext.getFrameworkResourceValue(ResourceType.ID, idName, defValue);
+ } else {
+ return mContext.getProjectResourceValue(ResourceType.ID, idName, defValue);
+ }
}
// not a direct id valid reference? resolve it
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 5176419..d90271b 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -293,7 +293,7 @@
}
@Override
- public void setAppGroupId(IBinder arg0, int arg1) throws RemoteException {
+ public void setAppTask(IBinder arg0, int arg1) throws RemoteException {
// TODO Auto-generated method stub
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 3953624..3441878 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -289,6 +289,11 @@
value = mRenderResources.resolveResValue(value);
}
+ if (value == null) {
+ // unable to find the attribute.
+ return false;
+ }
+
// check if this is a style resource
if (value instanceof StyleResourceValue) {
// get the id that will represent this style.
@@ -296,7 +301,6 @@
return true;
}
-
int a;
// if this is a framework value.
if (value.isFramework()) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index 0f51d00..62a03e1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -191,12 +191,6 @@
}
@Override
- public void setUniverseTransform(IBinder window, float alpha, float offx, float offy,
- float dsdx, float dtdx, float dsdy, float dtdy) {
- // pass for now.
- }
-
- @Override
public IBinder asBinder() {
// pass for now.
return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
new file mode 100644
index 0000000..e5023b8
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.bars;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.SessionParams;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.impl.ResourceHelper;
+import com.android.resources.ResourceType;
+
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+
+/**
+ * Assumes that the AppCompat library is present in the project's classpath and creates an
+ * actionbar around it.
+ */
+public class AppCompatActionBar extends BridgeActionBar {
+
+ private Object mWindowDecorActionBar;
+ private static final String WINDOW_ACTION_BAR_CLASS = "android.support.v7.internal.app.WindowDecorActionBar";
+ private Class<?> mWindowActionBarClass;
+
+ /**
+ * Inflate the action bar and attach it to {@code parentView}
+ */
+ public AppCompatActionBar(@NonNull BridgeContext context, @NonNull SessionParams params,
+ @NonNull ViewGroup parentView) {
+ super(context, params, parentView);
+ int contentRootId = context.getProjectResourceValue(ResourceType.ID,
+ "action_bar_activity_content", 0);
+ View contentView = getDecorContent().findViewById(contentRootId);
+ if (contentView != null) {
+ assert contentView instanceof FrameLayout;
+ setContentRoot(((FrameLayout) contentView));
+ } else {
+ // Something went wrong. Create a new FrameLayout in the enclosing layout.
+ FrameLayout contentRoot = new FrameLayout(context);
+ setMatchParent(contentRoot);
+ mEnclosingLayout.addView(contentRoot);
+ setContentRoot(contentRoot);
+ }
+ try {
+ Class[] constructorParams = {View.class};
+ Object[] constructorArgs = {getDecorContent()};
+ mWindowDecorActionBar = params.getProjectCallback().loadView(WINDOW_ACTION_BAR_CLASS,
+ constructorParams, constructorArgs);
+
+ mWindowActionBarClass = mWindowDecorActionBar == null ? null :
+ mWindowDecorActionBar.getClass();
+ setupActionBar();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ protected ResourceValue getLayoutResource(BridgeContext context) {
+ // We always assume that the app has requested the action bar.
+ return context.getRenderResources().getProjectResource(ResourceType.LAYOUT,
+ "abc_screen_toolbar");
+ }
+
+ @Override
+ protected void setTitle(CharSequence title) {
+ if (title != null && mWindowDecorActionBar != null) {
+ Method setTitle = getMethod(mWindowActionBarClass, "setTitle", CharSequence.class);
+ invoke(setTitle, mWindowDecorActionBar, title);
+ }
+ }
+
+ @Override
+ protected void setSubtitle(CharSequence subtitle) {
+ if (subtitle != null && mWindowDecorActionBar != null) {
+ Method setSubtitle = getMethod(mWindowActionBarClass, "setSubtitle", CharSequence.class);
+ invoke(setSubtitle, mWindowDecorActionBar, subtitle);
+ }
+ }
+
+ @Override
+ protected void setIcon(String icon) {
+ // Do this only if the action bar doesn't already have an icon.
+ if (icon != null && !icon.isEmpty() && mWindowDecorActionBar != null) {
+ if (((Boolean) invoke(getMethod(mWindowActionBarClass, "hasIcon"), mWindowDecorActionBar)
+ )) {
+ Drawable iconDrawable = getDrawable(icon, false);
+ if (iconDrawable != null) {
+ Method setIcon = getMethod(mWindowActionBarClass, "setIcon", Drawable.class);
+ invoke(setIcon, mWindowDecorActionBar, iconDrawable);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void setHomeAsUp(boolean homeAsUp) {
+ if (mWindowDecorActionBar != null) {
+ Method setHomeAsUp = getMethod(mWindowActionBarClass,
+ "setDefaultDisplayHomeAsUpEnabled", boolean.class);
+ invoke(setHomeAsUp, mWindowDecorActionBar, homeAsUp);
+ }
+ }
+
+ @Override
+ public void createMenuPopup() {
+ // it's hard to addd menus to appcompat's actionbar, since it'll use a lot of reflection.
+ // so we skip it for now.
+ }
+
+ @Nullable
+ private static Method getMethod(Class<?> owner, String name, Class<?>... parameterTypes) {
+ try {
+ return owner == null ? null : owner.getMethod(name, parameterTypes);
+ } catch (NoSuchMethodException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ @Nullable
+ private static Object invoke(Method method, Object owner, Object... args) {
+ try {
+ return method == null ? null : method.invoke(owner, args);
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ // TODO: this is duplicated from FrameworkActionBarWrapper$WindowActionBarWrapper
+ @Nullable
+ private Drawable getDrawable(@NonNull String name, boolean isFramework) {
+ RenderResources res = mBridgeContext.getRenderResources();
+ ResourceValue value = res.findResValue(name, isFramework);
+ value = res.resolveResValue(value);
+ if (value != null) {
+ return ResourceHelper.getDrawable(value, mBridgeContext);
+ }
+ return null;
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/BridgeActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/BridgeActionBar.java
new file mode 100644
index 0000000..b29d25f
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/BridgeActionBar.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.layoutlib.bridge.bars;
+
+import com.android.annotations.NonNull;
+import com.android.ide.common.rendering.api.ActionBarCallback;
+import com.android.ide.common.rendering.api.ActionBarCallback.HomeButtonStyle;
+import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.SessionParams;
+import com.android.layoutlib.bridge.android.BridgeContext;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.FrameLayout;
+import android.widget.RelativeLayout;
+
+/**
+ * An abstraction over two implementations of the ActionBar - framework and appcompat.
+ */
+public abstract class BridgeActionBar {
+ // Store a reference to the context so that we don't have to cast it repeatedly.
+ @NonNull protected final BridgeContext mBridgeContext;
+ @NonNull protected final SessionParams mParams;
+ // A Layout that contains the inflated action bar. The menu popup is added to this layout.
+ @NonNull protected final ViewGroup mEnclosingLayout;
+
+ private final View mDecorContent;
+ private final ActionBarCallback mCallback;
+
+ @NonNull private FrameLayout mContentRoot;
+
+ public BridgeActionBar(@NonNull BridgeContext context, @NonNull SessionParams params,
+ @NonNull ViewGroup parentView) {
+ mBridgeContext = context;
+ mParams = params;
+ mCallback = params.getProjectCallback().getActionBarCallback();
+ ResourceValue layoutName = getLayoutResource(context);
+ if (layoutName == null) {
+ throw new RuntimeException("Unable to find the layout for Action Bar.");
+ }
+ int layoutId;
+ if (layoutName.isFramework()) {
+ layoutId = context.getFrameworkResourceValue(layoutName.getResourceType(),
+ layoutName.getName(), 0);
+ } else {
+ layoutId = context.getProjectResourceValue(layoutName.getResourceType(),
+ layoutName.getName(), 0);
+
+ }
+ if (layoutId == 0) {
+ throw new RuntimeException(
+ String.format("Unable to resolve attribute \"%1$s\" of type \"%2$s\"",
+ layoutName.getName(), layoutName.getResourceType()));
+ }
+ if (mCallback.isOverflowPopupNeeded()) {
+ // Create a RelativeLayout around the action bar, to which the overflow popup may be
+ // added.
+ mEnclosingLayout = new RelativeLayout(mBridgeContext);
+ setMatchParent(mEnclosingLayout);
+ parentView.addView(mEnclosingLayout);
+ } else {
+ mEnclosingLayout = parentView;
+ }
+
+ // Inflate action bar layout.
+ mDecorContent = LayoutInflater.from(context).inflate(layoutId, mEnclosingLayout, true);
+
+ }
+
+ /**
+ * Returns the Layout Resource that should be used to inflate the action bar. This layout
+ * should cover the complete screen, and have a FrameLayout included, where the content will
+ * be inflated.
+ */
+ protected abstract ResourceValue getLayoutResource(BridgeContext context);
+
+ protected void setContentRoot(FrameLayout contentRoot) {
+ mContentRoot = contentRoot;
+ }
+
+ @NonNull
+ public FrameLayout getContentRoot() {
+ return mContentRoot;
+ }
+
+ /**
+ * Returns the view inflated. This should contain both the ActionBar and the app content in it.
+ */
+ protected View getDecorContent() {
+ return mDecorContent;
+ }
+
+ /** Setup things like the title, subtitle, icon etc. */
+ protected void setupActionBar() {
+ setTitle();
+ setSutTitle();
+ setIcon();
+ setHomeAsUp(mCallback.getHomeButtonStyle() == HomeButtonStyle.SHOW_HOME_AS_UP);
+ }
+
+ protected abstract void setTitle(CharSequence title);
+ protected abstract void setSubtitle(CharSequence subtitle);
+ protected abstract void setIcon(String icon);
+ protected abstract void setHomeAsUp(boolean homeAsUp);
+
+ private void setTitle() {
+ RenderResources res = mBridgeContext.getRenderResources();
+
+ String title = mParams.getAppLabel();
+ ResourceValue titleValue = res.findResValue(title, false);
+ if (titleValue != null && titleValue.getValue() != null) {
+ setTitle(titleValue.getValue());
+ } else {
+ setTitle(title);
+ }
+ }
+
+ private void setSutTitle() {
+ String subTitle = mCallback.getSubTitle();
+ if (subTitle != null) {
+ setSubtitle(subTitle);
+ }
+ }
+
+ private void setIcon() {
+ String appIcon = mParams.getAppIcon();
+ if (appIcon != null) {
+ setIcon(appIcon);
+ }
+ }
+
+ public abstract void createMenuPopup();
+
+ public ActionBarCallback getCallBack() {
+ return mCallback;
+ }
+
+ protected static void setMatchParent(View view) {
+ view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT));
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBar.java
similarity index 76%
rename from tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
rename to tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBar.java
index 2ff8d37..a1c9065 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBar.java
@@ -31,7 +31,7 @@
import android.content.res.TypedArray;
import android.util.DisplayMetrics;
import android.util.TypedValue;
-import android.view.LayoutInflater;
+import android.view.InflateException;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
@@ -44,70 +44,30 @@
import java.util.ArrayList;
-public class ActionBarLayout {
+/**
+ * Creates the ActionBar as done by the framework.
+ */
+public class FrameworkActionBar extends BridgeActionBar {
private static final String LAYOUT_ATTR_NAME = "windowActionBarFullscreenDecorLayout";
// The Action Bar
- @NonNull
- private CustomActionBarWrapper mActionBar;
-
- // Store another reference to the context so that we don't have to cast it repeatedly.
- @NonNull
- private final BridgeContext mBridgeContext;
-
- @NonNull
- private FrameLayout mContentRoot;
+ @NonNull private FrameworkActionBarWrapper mActionBar;
// A fake parent for measuring views.
- @Nullable
- private ViewGroup mMeasureParent;
-
- // A Layout that contains the inflated action bar. The menu popup is added to this layout.
- @NonNull
- private final RelativeLayout mEnclosingLayout;
+ @Nullable private ViewGroup mMeasureParent;
/**
* Inflate the action bar and attach it to {@code parentView}
*/
- public ActionBarLayout(@NonNull BridgeContext context, @NonNull SessionParams params,
+ public FrameworkActionBar(@NonNull BridgeContext context, @NonNull SessionParams params,
@NonNull ViewGroup parentView) {
+ super(context, params, parentView);
- mBridgeContext = context;
+ View decorContent = getDecorContent();
- ResourceValue layoutName = context.getRenderResources()
- .findItemInTheme(LAYOUT_ATTR_NAME, true);
- if (layoutName != null) {
- // We may need to resolve the reference obtained.
- layoutName = context.getRenderResources().findResValue(layoutName.getValue(),
- layoutName.isFramework());
- }
- int layoutId = 0;
- String error = null;
- if (layoutName == null) {
- error = "Unable to find action bar layout (" + LAYOUT_ATTR_NAME
- + ") in the current theme.";
- } else {
- layoutId = context.getFrameworkResourceValue(layoutName.getResourceType(),
- layoutName.getName(), 0);
- if (layoutId == 0) {
- error = String.format("Unable to resolve attribute \"%s\" of type \"%s\"",
- layoutName.getName(), layoutName.getResourceType());
- }
- }
- if (layoutId == 0) {
- throw new RuntimeException(error);
- }
- // Create a RelativeLayout to hold the action bar. The layout is needed so that we may
- // add the menu popup to it.
- mEnclosingLayout = new RelativeLayout(mBridgeContext);
- setMatchParent(mEnclosingLayout);
- parentView.addView(mEnclosingLayout);
-
- // Inflate action bar layout.
- View decorContent = LayoutInflater.from(context).inflate(layoutId, mEnclosingLayout, true);
-
- mActionBar = CustomActionBarWrapper.getActionBarWrapper(context, params, decorContent);
+ mActionBar = FrameworkActionBarWrapper.getActionBarWrapper(context, getCallBack(),
+ decorContent);
FrameLayout contentRoot = (FrameLayout) mEnclosingLayout.findViewById(android.R.id.content);
@@ -117,27 +77,62 @@
contentRoot = new FrameLayout(context);
setMatchParent(contentRoot);
mEnclosingLayout.addView(contentRoot);
- mContentRoot = contentRoot;
+ setContentRoot(contentRoot);
} else {
- mContentRoot = contentRoot;
- mActionBar.setupActionBar();
+ setContentRoot(contentRoot);
+ setupActionBar();
mActionBar.inflateMenus();
}
}
- private void setMatchParent(View view) {
- view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT));
+ @Override
+ protected ResourceValue getLayoutResource(BridgeContext context) {
+ ResourceValue layoutName =
+ context.getRenderResources().findItemInTheme(LAYOUT_ATTR_NAME, true);
+ if (layoutName != null) {
+ // We may need to resolve the reference obtained.
+ layoutName = context.getRenderResources().findResValue(layoutName.getValue(),
+ layoutName.isFramework());
+ }
+ if (layoutName == null) {
+ throw new InflateException("Unable to find action bar layout (" + LAYOUT_ATTR_NAME
+ + ") in the current theme.");
+ }
+ return layoutName;
+ }
+
+ @Override
+ protected void setupActionBar() {
+ super.setupActionBar();
+ mActionBar.setupActionBar();
+ }
+
+ @Override
+ protected void setHomeAsUp(boolean homeAsUp) {
+ mActionBar.setHomeAsUp(homeAsUp);
+ }
+
+ @Override
+ protected void setTitle(CharSequence title) {
+ mActionBar.setTitle(title);
+ }
+
+ @Override
+ protected void setSubtitle(CharSequence subtitle) {
+ mActionBar.setSubTitle(subtitle);
+ }
+
+ @Override
+ protected void setIcon(String icon) {
+ mActionBar.setIcon(icon);
}
/**
* Creates a Popup and adds it to the content frame. It also adds another {@link FrameLayout} to
* the content frame which shall serve as the new content root.
*/
+ @Override
public void createMenuPopup() {
- assert mEnclosingLayout.getChildCount() == 1
- : "Action Bar Menus have already been created.";
-
if (!isOverflowPopupNeeded()) {
return;
}
@@ -193,11 +188,6 @@
return needed;
}
- @NonNull
- public FrameLayout getContentRoot() {
- return mContentRoot;
- }
-
// Copied from com.android.internal.view.menu.MenuPopHelper.measureContentWidth()
private int measureContentWidth(@NonNull ListAdapter adapter) {
// Menus don't tend to be long, so this is more sane than it looks.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomActionBarWrapper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBarWrapper.java
similarity index 81%
rename from tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomActionBarWrapper.java
rename to tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBarWrapper.java
index 6db722e..44c2cd8 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomActionBarWrapper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FrameworkActionBarWrapper.java
@@ -19,10 +19,8 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.ide.common.rendering.api.ActionBarCallback;
-import com.android.ide.common.rendering.api.ActionBarCallback.HomeButtonStyle;
import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.ResourceValue;
-import com.android.ide.common.rendering.api.SessionParams;
import com.android.internal.R;
import com.android.internal.app.ToolbarActionBar;
import com.android.internal.app.WindowDecorActionBar;
@@ -54,10 +52,9 @@
/**
* A common API to access {@link ToolbarActionBar} and {@link WindowDecorActionBar}.
*/
-public abstract class CustomActionBarWrapper {
+public abstract class FrameworkActionBarWrapper {
@NonNull protected ActionBar mActionBar;
- @NonNull protected SessionParams mParams;
@NonNull protected ActionBarCallback mCallback;
@NonNull protected BridgeContext mContext;
@@ -68,49 +65,48 @@
* ?attr/windowActionBarFullscreenDecorLayout
*/
@NonNull
- public static CustomActionBarWrapper getActionBarWrapper(@NonNull BridgeContext context,
- @NonNull SessionParams params, @NonNull View decorContent) {
+ public static FrameworkActionBarWrapper getActionBarWrapper(@NonNull BridgeContext context,
+ @NonNull ActionBarCallback callback, @NonNull View decorContent) {
View view = decorContent.findViewById(R.id.action_bar);
if (view instanceof Toolbar) {
- return new ToolbarWrapper(context, params, ((Toolbar) view));
+ return new ToolbarWrapper(context, callback, (Toolbar) view);
} else if (view instanceof ActionBarView) {
- return new WindowActionBarWrapper(context, params, decorContent,
- ((ActionBarView) view));
+ return new WindowActionBarWrapper(context, callback, decorContent,
+ (ActionBarView) view);
} else {
throw new IllegalStateException("Can't make an action bar out of " +
view.getClass().getSimpleName());
}
}
- CustomActionBarWrapper(@NonNull BridgeContext context, @NonNull SessionParams params,
+ FrameworkActionBarWrapper(@NonNull BridgeContext context, ActionBarCallback callback,
@NonNull ActionBar actionBar) {
mActionBar = actionBar;
- mParams = params;
- mCallback = params.getProjectCallback().getActionBarCallback();
+ mCallback = callback;
mContext = context;
}
+ /** A call to setup any custom properties. */
protected void setupActionBar() {
- // Do the things that are common to all implementations.
- RenderResources res = mContext.getRenderResources();
+ // Nothing to do here.
+ }
- String title = mParams.getAppLabel();
- ResourceValue titleValue = res.findResValue(title, false);
- if (titleValue != null && titleValue.getValue() != null) {
- mActionBar.setTitle(titleValue.getValue());
- } else {
- mActionBar.setTitle(title);
- }
+ public void setTitle(CharSequence title) {
+ mActionBar.setTitle(title);
+ }
- String subTitle = mCallback.getSubTitle();
+ public void setSubTitle(CharSequence subTitle) {
if (subTitle != null) {
mActionBar.setSubtitle(subTitle);
}
+ }
- // Add show home as up icon.
- if (mCallback.getHomeButtonStyle() == HomeButtonStyle.SHOW_HOME_AS_UP) {
- mActionBar.setDisplayOptions(0xFF, ActionBar.DISPLAY_HOME_AS_UP);
- }
+ public void setHomeAsUp(boolean homeAsUp) {
+ mActionBar.setDisplayHomeAsUpEnabled(homeAsUp);
+ }
+
+ public void setIcon(String icon) {
+ // Nothing to do.
}
protected boolean isSplit() {
@@ -186,15 +182,14 @@
* Material theme uses {@link Toolbar} as the action bar. This wrapper provides access to
* Toolbar using a common API.
*/
- private static class ToolbarWrapper extends CustomActionBarWrapper {
+ private static class ToolbarWrapper extends FrameworkActionBarWrapper {
@NonNull
private final Toolbar mToolbar; // This is the view.
- ToolbarWrapper(@NonNull BridgeContext context, @NonNull SessionParams params,
+ ToolbarWrapper(@NonNull BridgeContext context, @NonNull ActionBarCallback callback,
@NonNull Toolbar toolbar) {
- super(context, params, new ToolbarActionBar(toolbar, "", new WindowCallback())
- );
+ super(context, callback, new ToolbarActionBar(toolbar, "", new WindowCallback()));
mToolbar = toolbar;
}
@@ -248,19 +243,17 @@
* Holo theme uses {@link WindowDecorActionBar} as the action bar. This wrapper provides
* access to it using a common API.
*/
- private static class WindowActionBarWrapper extends CustomActionBarWrapper {
+ private static class WindowActionBarWrapper extends FrameworkActionBarWrapper {
- @NonNull
- private final WindowDecorActionBar mActionBar;
- @NonNull
- private final ActionBarView mActionBarView;
- @NonNull
- private final View mDecorContentRoot;
+ @NonNull private final WindowDecorActionBar mActionBar;
+ @NonNull private final ActionBarView mActionBarView;
+ @NonNull private final View mDecorContentRoot;
private MenuBuilder mMenuBuilder;
- public WindowActionBarWrapper(@NonNull BridgeContext context, @NonNull SessionParams params,
- @NonNull View decorContentRoot, @NonNull ActionBarView actionBarView) {
- super(context, params, new WindowDecorActionBar(decorContentRoot));
+ public WindowActionBarWrapper(@NonNull BridgeContext context,
+ @NonNull ActionBarCallback callback, @NonNull View decorContentRoot,
+ @NonNull ActionBarView actionBarView) {
+ super(context, callback, new WindowDecorActionBar(decorContentRoot));
mActionBarView = actionBarView;
mActionBar = ((WindowDecorActionBar) super.mActionBar);
mDecorContentRoot = decorContentRoot;
@@ -268,7 +261,6 @@
@Override
protected void setupActionBar() {
- super.setupActionBar();
// Set the navigation mode.
int navMode = mCallback.getNavigationMode();
@@ -278,16 +270,6 @@
setupTabs(3);
}
- String icon = mParams.getAppIcon();
- // If the action bar style doesn't specify an icon, set the icon obtained from the
- // session params.
- if (!mActionBar.hasIcon() && icon != null) {
- Drawable iconDrawable = getDrawable(icon, false);
- if (iconDrawable != null) {
- mActionBar.setIcon(iconDrawable);
- }
- }
-
// Set action bar to be split, if needed.
ViewGroup splitView = (ViewGroup) mDecorContentRoot.findViewById(R.id.split_action_bar);
if (splitView != null) {
@@ -300,6 +282,17 @@
}
@Override
+ public void setIcon(String icon) {
+ // Set the icon only if the action bar doesn't specify an icon.
+ if (!mActionBar.hasIcon() && icon != null) {
+ Drawable iconDrawable = getDrawable(icon, false);
+ if (iconDrawable != null) {
+ mActionBar.setIcon(iconDrawable);
+ }
+ }
+ }
+
+ @Override
protected void inflateMenus() {
super.inflateMenus();
// The super implementation doesn't set the menu on the view. Set it here.
@@ -340,7 +333,7 @@
@Override
int getMenuPopupMargin() {
- return -ActionBarLayout.getPixelValue("10dp", mContext.getMetrics());
+ return -FrameworkActionBar.getPixelValue("10dp", mContext.getMetrics());
}
// TODO: Use an adapter, like List View to set up tabs.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index fd976af..84ee914 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -51,11 +51,13 @@
import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
import com.android.layoutlib.bridge.android.SessionParamsFlags;
+import com.android.layoutlib.bridge.bars.BridgeActionBar;
+import com.android.layoutlib.bridge.bars.AppCompatActionBar;
import com.android.layoutlib.bridge.bars.Config;
import com.android.layoutlib.bridge.bars.NavigationBar;
import com.android.layoutlib.bridge.bars.StatusBar;
import com.android.layoutlib.bridge.bars.TitleBar;
-import com.android.layoutlib.bridge.bars.ActionBarLayout;
+import com.android.layoutlib.bridge.bars.FrameworkActionBar;
import com.android.layoutlib.bridge.impl.binding.FakeAdapter;
import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter;
import com.android.resources.Density;
@@ -354,7 +356,7 @@
// if the theme says no title/action bar, then the size will be 0
if (mActionBarSize > 0) {
- ActionBarLayout actionBar = createActionBar(context, params, backgroundLayout);
+ BridgeActionBar actionBar = createActionBar(context, params, backgroundLayout);
actionBar.createMenuPopup();
mContentRoot = actionBar.getContentRoot();
} else if (mTitleBarSize > 0) {
@@ -1190,8 +1192,22 @@
// android.support.v7.app.ActionBarActivity, and not care about the theme name at all.
if (mIsThemeAppCompat == null) {
StyleResourceValue defaultTheme = resources.getDefaultTheme();
- StyleResourceValue val = resources.getStyle("Theme.AppCompat", false);
- mIsThemeAppCompat = defaultTheme == val || resources.themeIsParentOf(val, defaultTheme);
+ // We can't simply check for parent using resources.themeIsParentOf() since the
+ // inheritance structure isn't really what one would expect. The first common parent
+ // between Theme.AppCompat.Light and Theme.AppCompat is Theme.Material (for v21).
+ boolean isThemeAppCompat = false;
+ for (int i = 0; i < 50; i++) {
+ // for loop ensures that we don't run into cyclic theme inheritance.
+ if (defaultTheme.getName().startsWith("Theme.AppCompat")) {
+ isThemeAppCompat = true;
+ break;
+ }
+ defaultTheme = resources.getParent(defaultTheme);
+ if (defaultTheme == null) {
+ break;
+ }
+ }
+ mIsThemeAppCompat = isThemeAppCompat;
}
return mIsThemeAppCompat;
}
@@ -1647,9 +1663,13 @@
/**
* Creates the action bar. Also queries the project callback for missing information.
*/
- private ActionBarLayout createActionBar(BridgeContext context, SessionParams params,
+ private BridgeActionBar createActionBar(BridgeContext context, SessionParams params,
ViewGroup parentView) {
- return new ActionBarLayout(context, params, parentView);
+ if (mIsThemeAppCompat == Boolean.TRUE) {
+ return new AppCompatActionBar(context, params, parentView);
+ } else {
+ return new FrameworkActionBar(context, params, parentView);
+ }
}
public BufferedImage getImage() {
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 5202cc3..52f2372 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -733,6 +733,31 @@
/**
* @hide
+ * Last time the system tried to roam and failed because of authentication failure or DHCP
+ * RENEW failure.
+ */
+ public long lastRoamingFailure;
+
+ /** @hide */
+ public static int ROAMING_FAILURE_IP_CONFIG = 1;
+ /** @hide */
+ public static int ROAMING_FAILURE_AUTH_FAILURE = 2;
+
+ /**
+ * @hide
+ * Initial amount of time this Wifi configuration gets blacklisted for network switching
+ * because of roaming failure
+ */
+ public long roamingFailureBlackListTimeMilli = 1000;
+
+ /**
+ * @hide
+ * Last roaming failure reason code
+ */
+ public int lastRoamingFailureReason;
+
+ /**
+ * @hide
* Last time the system was disconnected to this configuration.
*/
public long lastDisconnected;
@@ -1209,6 +1234,18 @@
sbuf.append( "sec");
}
}
+ if (this.lastRoamingFailure != 0) {
+ sbuf.append('\n');
+ long diff = now_ms - this.lastRoamingFailure;
+ if (diff <= 0) {
+ sbuf.append("lastRoamingFailure since <incorrect>");
+ } else {
+ sbuf.append("lastRoamingFailure: ").append(Long.toString(diff/1000));
+ sbuf.append( "sec");
+ }
+ }
+ sbuf.append("roamingFailureBlackListTimeMilli: ").
+ append(Long.toString(this.roamingFailureBlackListTimeMilli));
sbuf.append('\n');
if (this.linkedConfigurations != null) {
for(String key : this.linkedConfigurations.keySet()) {
@@ -1595,6 +1632,9 @@
lastConnected = source.lastConnected;
lastDisconnected = source.lastDisconnected;
lastConnectionFailure = source.lastConnectionFailure;
+ lastRoamingFailure = source.lastRoamingFailure;
+ lastRoamingFailureReason = source.lastRoamingFailureReason;
+ roamingFailureBlackListTimeMilli = source.roamingFailureBlackListTimeMilli;
numConnectionFailures = source.numConnectionFailures;
numIpConfigFailures = source.numIpConfigFailures;
numAuthFailures = source.numAuthFailures;
@@ -1667,6 +1707,9 @@
dest.writeString(lastUpdateName);
dest.writeLong(blackListTimestamp);
dest.writeLong(lastConnectionFailure);
+ dest.writeLong(lastRoamingFailure);
+ dest.writeInt(lastRoamingFailureReason);
+ dest.writeLong(roamingFailureBlackListTimeMilli);
dest.writeInt(numConnectionFailures);
dest.writeInt(numIpConfigFailures);
dest.writeInt(numAuthFailures);
@@ -1732,6 +1775,9 @@
config.lastUpdateName = in.readString();
config.blackListTimestamp = in.readLong();
config.lastConnectionFailure = in.readLong();
+ config.lastRoamingFailure = in.readLong();
+ config.lastRoamingFailureReason = in.readInt();
+ config.roamingFailureBlackListTimeMilli = in.readLong();
config.numConnectionFailures = in.readInt();
config.numIpConfigFailures = in.readInt();
config.numAuthFailures = in.readInt();