Merge "Fix issue #20655182: API Review: ViewAssistStructure" into mnc-dev
diff --git a/Android.mk b/Android.mk
index b71f08c..27dedd9 100644
--- a/Android.mk
+++ b/Android.mk
@@ -348,12 +348,6 @@
media/java/android/media/projection/IMediaProjectionCallback.aidl \
media/java/android/media/projection/IMediaProjectionManager.aidl \
media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl \
- media/java/android/media/routing/IMediaRouteService.aidl \
- media/java/android/media/routing/IMediaRouteClientCallback.aidl \
- media/java/android/media/routing/IMediaRouter.aidl \
- media/java/android/media/routing/IMediaRouterDelegate.aidl \
- media/java/android/media/routing/IMediaRouterRoutingCallback.aidl \
- media/java/android/media/routing/IMediaRouterStateCallback.aidl \
media/java/android/media/session/IActiveSessionsListener.aidl \
media/java/android/media/session/ISessionController.aidl \
media/java/android/media/session/ISessionControllerCallback.aidl \
@@ -460,7 +454,6 @@
frameworks/base/location/java/android/location/Criteria.aidl \
frameworks/base/media/java/android/media/MediaMetadata.aidl \
frameworks/base/media/java/android/media/MediaDescription.aidl \
- frameworks/base/media/java/android/media/routing/MediaRouteSelector.aidl \
frameworks/base/media/java/android/media/Rating.aidl \
frameworks/base/media/java/android/media/AudioAttributes.aidl \
frameworks/base/media/java/android/media/AudioFocusInfo.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index c812b6a..667ed02 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -233,6 +233,7 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/system/lib/libinputflingerhost.so $(PRODUCT_OUT)/symbols/system/lib64/libinputflingerhost.so)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/lib/libinputflingerhost.so $(PRODUCT_OUT)/obj_arm/lib/libinputflingerhost.so)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libinputflingerhost_intermediates $(PRODUCT_OUT)/obj_arm/SHARED_LIBRARIES/libinputflingerhost_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/target/common/obj/framework.aidl)
# ******************************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
diff --git a/api/current.txt b/api/current.txt
index 209dd50..c38c0e7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -28,7 +28,6 @@
field public static final java.lang.String BIND_DREAM_SERVICE = "android.permission.BIND_DREAM_SERVICE";
field public static final java.lang.String BIND_INCALL_SERVICE = "android.permission.BIND_INCALL_SERVICE";
field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD";
- field public static final java.lang.String BIND_MEDIA_ROUTE_SERVICE = "android.permission.BIND_MEDIA_ROUTE_SERVICE";
field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
@@ -5836,7 +5835,6 @@
field public static final java.lang.String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD";
field public static final java.lang.String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION";
field public static final java.lang.String ACTION_SYSTEM_UPDATE_POLICY_CHANGED = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED";
- field public static final int DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2
field public static final int ENCRYPTION_STATUS_ACTIVE = 3; // 0x3
field public static final int ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY = 4; // 0x4
@@ -5901,6 +5899,7 @@
field public static final int PERMISSION_POLICY_AUTO_DENY = 2; // 0x2
field public static final int PERMISSION_POLICY_AUTO_GRANT = 1; // 0x1
field public static final int PERMISSION_POLICY_PROMPT = 0; // 0x0
+ field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1
field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
@@ -17427,248 +17426,11 @@
}
-package android.media.routing {
-
- public final class MediaRouteSelector implements android.os.Parcelable {
- method public boolean containsProtocol(java.lang.Class<?>);
- method public boolean containsProtocol(java.lang.String);
- method public int describeContents();
- method public android.os.Bundle getExtras();
- method public int getOptionalFeatures();
- method public java.util.List<java.lang.String> getOptionalProtocols();
- method public int getRequiredFeatures();
- method public java.util.List<java.lang.String> getRequiredProtocols();
- method public java.lang.String getServicePackageName();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.media.routing.MediaRouteSelector> CREATOR;
- }
-
- public static final class MediaRouteSelector.Builder {
- ctor public MediaRouteSelector.Builder();
- method public android.media.routing.MediaRouteSelector.Builder addOptionalProtocol(java.lang.Class<?>);
- method public android.media.routing.MediaRouteSelector.Builder addOptionalProtocol(java.lang.String);
- method public android.media.routing.MediaRouteSelector.Builder addRequiredProtocol(java.lang.Class<?>);
- method public android.media.routing.MediaRouteSelector.Builder addRequiredProtocol(java.lang.String);
- method public android.media.routing.MediaRouteSelector build();
- method public android.media.routing.MediaRouteSelector.Builder setExtras(android.os.Bundle);
- method public android.media.routing.MediaRouteSelector.Builder setOptionalFeatures(int);
- method public android.media.routing.MediaRouteSelector.Builder setRequiredFeatures(int);
- method public android.media.routing.MediaRouteSelector.Builder setServicePackageName(java.lang.String);
- }
-
- public abstract class MediaRouteService extends android.app.Service {
- ctor public MediaRouteService();
- method public android.media.routing.MediaRouter.ServiceMetadata getServiceMetadata();
- method public android.os.IBinder onBind(android.content.Intent);
- method public abstract android.media.routing.MediaRouteService.ClientSession onCreateClientSession(android.media.routing.MediaRouteService.ClientInfo);
- field public static final java.lang.String SERVICE_INTERFACE = "android.media.routing.MediaRouteService";
- }
-
- public static final class MediaRouteService.ClientInfo {
- method public java.lang.String getPackageName();
- method public int getUid();
- }
-
- public static abstract class MediaRouteService.ClientSession {
- ctor public MediaRouteService.ClientSession();
- method public abstract boolean onConnect(android.media.routing.MediaRouter.ConnectionRequest, android.media.routing.MediaRouteService.ConnectionCallback);
- method public abstract void onDisconnect();
- method public void onPauseStream();
- method public void onRelease();
- method public void onResumeStream();
- method public abstract boolean onStartDiscovery(android.media.routing.MediaRouter.DiscoveryRequest, android.media.routing.MediaRouteService.DiscoveryCallback);
- method public abstract void onStopDiscovery();
- }
-
- public final class MediaRouteService.ConnectionCallback {
- method public void onConnected(android.media.routing.MediaRouter.ConnectionInfo);
- method public void onConnectionFailed(int, java.lang.CharSequence, android.os.Bundle);
- method public void onDisconnected();
- }
-
- public final class MediaRouteService.DiscoveryCallback {
- method public void onDestinationFound(android.media.routing.MediaRouter.DestinationInfo, java.util.List<android.media.routing.MediaRouter.RouteInfo>);
- method public void onDestinationLost(android.media.routing.MediaRouter.DestinationInfo);
- method public void onDiscoveryFailed(int, java.lang.CharSequence, android.os.Bundle);
- }
-
- public final class MediaRouter {
- ctor public MediaRouter(android.content.Context);
- method public void addSelector(android.media.routing.MediaRouteSelector);
- method public void clearSelectors();
- method public android.media.routing.MediaRouter.Delegate createDelegate();
- method public android.media.routing.MediaRouter.ConnectionInfo getConnection();
- method public int getConnectionState();
- method public java.util.List<android.media.routing.MediaRouter.DestinationInfo> getDiscoveredDestinations();
- method public java.util.List<android.media.routing.MediaRouter.RouteInfo> getDiscoveredRoutes(android.media.routing.MediaRouter.DestinationInfo);
- method public int getDiscoveryState();
- method public android.media.AudioAttributes getPreferredAudioAttributes();
- method public android.view.Display getPreferredPresentationDisplay();
- method public android.media.VolumeProvider getPreferredVolumeProvider();
- method public android.media.routing.MediaRouter.DestinationInfo getSelectedDestination();
- method public android.media.routing.MediaRouter.RouteInfo getSelectedRoute();
- method public java.util.List<android.media.routing.MediaRouteSelector> getSelectors();
- method public boolean isReleased();
- method public void pauseStream();
- method public void release();
- method public void removeSelector(android.media.routing.MediaRouteSelector);
- method public void resumeStream();
- method public void setRoutingCallback(android.media.routing.MediaRouter.RoutingCallback, android.os.Handler);
- field public static final int CONNECTION_ERROR_ABORTED = 1; // 0x1
- field public static final int CONNECTION_ERROR_BARGED = 7; // 0x7
- field public static final int CONNECTION_ERROR_BROKEN = 6; // 0x6
- field public static final int CONNECTION_ERROR_BUSY = 4; // 0x4
- field public static final int CONNECTION_ERROR_TIMEOUT = 5; // 0x5
- field public static final int CONNECTION_ERROR_UNAUTHORIZED = 2; // 0x2
- field public static final int CONNECTION_ERROR_UNKNOWN = 0; // 0x0
- field public static final int CONNECTION_ERROR_UNREACHABLE = 3; // 0x3
- field public static final int CONNECTION_FLAG_BARGE = 1; // 0x1
- field public static final int CONNECTION_STATE_CONNECTED = 2; // 0x2
- field public static final int CONNECTION_STATE_CONNECTING = 1; // 0x1
- field public static final int CONNECTION_STATE_DISCONNECTED = 0; // 0x0
- field public static final int DISCONNECTION_REASON_APPLICATION_REQUEST = 0; // 0x0
- field public static final int DISCONNECTION_REASON_ERROR = 2; // 0x2
- field public static final int DISCONNECTION_REASON_USER_REQUEST = 1; // 0x1
- field public static final int DISCOVERY_ERROR_ABORTED = 1; // 0x1
- field public static final int DISCOVERY_ERROR_NO_CONNECTIVITY = 2; // 0x2
- field public static final int DISCOVERY_ERROR_UNKNOWN = 0; // 0x0
- field public static final int DISCOVERY_FLAG_BACKGROUND = 1; // 0x1
- field public static final int DISCOVERY_STATE_STARTED = 1; // 0x1
- field public static final int DISCOVERY_STATE_STOPPED = 0; // 0x0
- field public static final int ROUTE_FEATURE_LIVE_AUDIO = 1; // 0x1
- field public static final int ROUTE_FEATURE_LIVE_VIDEO = 2; // 0x2
- }
-
- public static final class MediaRouter.ConnectionInfo {
- method public android.media.AudioAttributes getAudioAttributes();
- method public android.os.Bundle getExtras();
- method public int getFeatures();
- method public android.view.Display getPresentationDisplay();
- method public android.os.IBinder getProtocolBinder(java.lang.String);
- method public android.os.IBinder getProtocolBinder(int);
- method public T getProtocolObject(java.lang.Class<T>);
- method public java.util.List<java.lang.String> getProtocols();
- method public android.media.routing.MediaRouter.RouteInfo getRoute();
- method public android.media.VolumeProvider getVolumeProvider();
- }
-
- public static final class MediaRouter.ConnectionInfo.Builder {
- ctor public MediaRouter.ConnectionInfo.Builder(android.media.routing.MediaRouter.RouteInfo);
- method public android.media.routing.MediaRouter.ConnectionInfo build();
- method public android.media.routing.MediaRouter.ConnectionInfo.Builder setAudioAttributes(android.media.AudioAttributes);
- method public android.media.routing.MediaRouter.ConnectionInfo.Builder setExtras(android.os.Bundle);
- method public android.media.routing.MediaRouter.ConnectionInfo.Builder setPresentationDisplay(android.view.Display);
- method public android.media.routing.MediaRouter.ConnectionInfo.Builder setProtocolBinder(java.lang.String, android.os.IBinder);
- method public android.media.routing.MediaRouter.ConnectionInfo.Builder setProtocolStub(java.lang.Class<?>, android.os.IInterface);
- method public android.media.routing.MediaRouter.ConnectionInfo.Builder setVolumeProvider(android.media.VolumeProvider);
- }
-
- public static final class MediaRouter.ConnectionRequest {
- method public android.os.Bundle getExtras();
- method public int getFlags();
- method public android.media.routing.MediaRouter.RouteInfo getRoute();
- method public void setExtras(android.os.Bundle);
- method public void setFlags(int);
- method public void setRoute(android.media.routing.MediaRouter.RouteInfo);
- }
-
- public static final class MediaRouter.Delegate {
- ctor public MediaRouter.Delegate();
- method public void addStateCallback(android.media.routing.MediaRouter.StateCallback, android.os.Handler);
- method public void connect(android.media.routing.MediaRouter.DestinationInfo, int);
- method public void disconnect(int);
- method public int getConnectionState();
- method public java.util.List<android.media.routing.MediaRouter.DestinationInfo> getDiscoveredDestinations();
- method public int getDiscoveryState();
- method public android.media.routing.MediaRouter.DestinationInfo getSelectedDestination();
- method public boolean isReleased();
- method public void removeStateCallback(android.media.routing.MediaRouter.StateCallback);
- method public void startDiscovery(int);
- method public void stopDiscovery();
- }
-
- public static final class MediaRouter.DestinationInfo {
- method public java.lang.CharSequence getDescription();
- method public android.os.Bundle getExtras();
- method public int getIconResourceId();
- method public java.lang.String getId();
- method public java.lang.CharSequence getName();
- method public android.media.routing.MediaRouter.ServiceMetadata getServiceMetadata();
- method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
- }
-
- public static final class MediaRouter.DestinationInfo.Builder {
- ctor public MediaRouter.DestinationInfo.Builder(java.lang.String, android.media.routing.MediaRouter.ServiceMetadata, java.lang.CharSequence);
- method public android.media.routing.MediaRouter.DestinationInfo build();
- method public android.media.routing.MediaRouter.DestinationInfo.Builder setDescription(java.lang.CharSequence);
- method public android.media.routing.MediaRouter.DestinationInfo.Builder setExtras(android.os.Bundle);
- method public android.media.routing.MediaRouter.DestinationInfo.Builder setIconResourceId(int);
- }
-
- public static final class MediaRouter.DiscoveryRequest {
- method public int getFlags();
- method public java.util.List<android.media.routing.MediaRouteSelector> getSelectors();
- method public void setFlags(int);
- method public void setSelectors(java.util.List<android.media.routing.MediaRouteSelector>);
- }
-
- public static final class MediaRouter.RouteInfo {
- method public android.media.routing.MediaRouter.DestinationInfo getDestination();
- method public android.os.Bundle getExtras();
- method public int getFeatures();
- method public java.lang.String getId();
- method public java.util.List<java.lang.String> getProtocols();
- method public android.media.routing.MediaRouteSelector getSelector();
- }
-
- public static final class MediaRouter.RouteInfo.Builder {
- ctor public MediaRouter.RouteInfo.Builder(java.lang.String, android.media.routing.MediaRouter.DestinationInfo, android.media.routing.MediaRouteSelector);
- method public android.media.routing.MediaRouter.RouteInfo.Builder addProtocol(java.lang.Class<T>);
- method public android.media.routing.MediaRouter.RouteInfo.Builder addProtocol(java.lang.String);
- method public android.media.routing.MediaRouter.RouteInfo build();
- method public android.media.routing.MediaRouter.RouteInfo.Builder setExtras(android.os.Bundle);
- method public android.media.routing.MediaRouter.RouteInfo.Builder setFeatures(int);
- }
-
- public static abstract class MediaRouter.RoutingCallback extends android.media.routing.MediaRouter.StateCallback {
- ctor public MediaRouter.RoutingCallback();
- method public boolean onPrepareConnectionRequest(android.media.routing.MediaRouter.ConnectionRequest, android.media.routing.MediaRouter.DestinationInfo, java.util.List<android.media.routing.MediaRouter.RouteInfo>);
- method public boolean onPrepareDiscoveryRequest(android.media.routing.MediaRouter.DiscoveryRequest, java.util.List<android.media.routing.MediaRouteSelector>);
- }
-
- public static final class MediaRouter.ServiceMetadata {
- method public android.content.ComponentName getComponentName();
- method public android.graphics.drawable.Drawable getIcon(android.content.pm.PackageManager);
- method public java.lang.CharSequence getLabel(android.content.pm.PackageManager);
- method public java.lang.String getPackageName();
- method public android.content.pm.ServiceInfo getService();
- }
-
- public static abstract class MediaRouter.StateCallback {
- ctor public MediaRouter.StateCallback();
- method public void onConnected();
- method public void onConnecting();
- method public void onConnectionFailed(int, java.lang.CharSequence, android.os.Bundle);
- method public void onConnectionStateChanged(int);
- method public void onDestinationFound(android.media.routing.MediaRouter.DestinationInfo);
- method public void onDestinationLost(android.media.routing.MediaRouter.DestinationInfo);
- method public void onDisconnected();
- method public void onDiscoveryFailed(int, java.lang.CharSequence, android.os.Bundle);
- method public void onDiscoveryStarted();
- method public void onDiscoveryStateChanged(int);
- method public void onDiscoveryStopped();
- method public void onReleased();
- method public void onSelectedDestinationChanged(android.media.routing.MediaRouter.DestinationInfo);
- }
-
-}
-
package android.media.session {
public final class MediaController {
ctor public MediaController(android.content.Context, android.media.session.MediaSession.Token);
method public void adjustVolume(int, int);
- method public android.media.routing.MediaRouter.Delegate createMediaRouterDelegate();
method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
method public android.os.Bundle getExtras();
method public long getFlags();
@@ -17742,7 +17504,6 @@
method public void setExtras(android.os.Bundle);
method public void setFlags(int);
method public void setMediaButtonReceiver(android.app.PendingIntent);
- method public void setMediaRouter(android.media.routing.MediaRouter);
method public void setMetadata(android.media.MediaMetadata);
method public void setPlaybackState(android.media.session.PlaybackState);
method public void setPlaybackToLocal(android.media.AudioAttributes);
@@ -22906,6 +22667,7 @@
ctor public Build.VERSION();
field public static final java.lang.String CODENAME;
field public static final java.lang.String INCREMENTAL;
+ field public static final int PREVIEW_SDK_INT;
field public static final java.lang.String RELEASE;
field public static final deprecated java.lang.String SDK;
field public static final int SDK_INT;
@@ -28745,6 +28507,53 @@
public abstract class KeyStoreKeyProperties {
}
+ public static abstract class KeyStoreKeyProperties.Algorithm {
+ field public static final java.lang.String AES = "AES";
+ field public static final java.lang.String EC = "EC";
+ field public static final java.lang.String HMAC_SHA1 = "HmacSHA1";
+ field public static final java.lang.String HMAC_SHA224 = "HmacSHA224";
+ field public static final java.lang.String HMAC_SHA256 = "HmacSHA256";
+ field public static final java.lang.String HMAC_SHA384 = "HmacSHA384";
+ field public static final java.lang.String HMAC_SHA512 = "HmacSHA512";
+ field public static final java.lang.String RSA = "RSA";
+ }
+
+ public static abstract class KeyStoreKeyProperties.AlgorithmEnum implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract class KeyStoreKeyProperties.BlockMode {
+ field public static final java.lang.String CBC = "CBC";
+ field public static final java.lang.String CTR = "CTR";
+ field public static final java.lang.String ECB = "ECB";
+ field public static final java.lang.String GCM = "GCM";
+ }
+
+ public static abstract class KeyStoreKeyProperties.BlockModeEnum implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract class KeyStoreKeyProperties.Digest {
+ field public static final java.lang.String MD5 = "MD5";
+ field public static final java.lang.String NONE = "NONE";
+ field public static final java.lang.String SHA1 = "SHA-1";
+ field public static final java.lang.String SHA224 = "SHA-224";
+ field public static final java.lang.String SHA256 = "SHA-256";
+ field public static final java.lang.String SHA384 = "SHA-384";
+ field public static final java.lang.String SHA512 = "SHA-512";
+ }
+
+ public static abstract class KeyStoreKeyProperties.DigestEnum implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract class KeyStoreKeyProperties.EncryptionPadding {
+ field public static final java.lang.String NONE = "NoPadding";
+ field public static final java.lang.String PKCS7 = "PKCS7Padding";
+ field public static final java.lang.String RSA_OAEP = "OAEPPadding";
+ field public static final java.lang.String RSA_PKCS1 = "PKCS1Padding";
+ }
+
+ public static abstract class KeyStoreKeyProperties.EncryptionPaddingEnum implements java.lang.annotation.Annotation {
+ }
+
public static abstract class KeyStoreKeyProperties.Origin {
field public static final int GENERATED = 1; // 0x1
field public static final int IMPORTED = 2; // 0x2
@@ -28764,6 +28573,14 @@
public static abstract class KeyStoreKeyProperties.PurposeEnum implements java.lang.annotation.Annotation {
}
+ public static abstract class KeyStoreKeyProperties.SignaturePadding {
+ field public static final java.lang.String RSA_PKCS1 = "PKCS1";
+ field public static final java.lang.String RSA_PSS = "PSS";
+ }
+
+ public static abstract class KeyStoreKeyProperties.SignaturePaddingEnum implements java.lang.annotation.Annotation {
+ }
+
public class KeyStoreKeySpec implements java.security.spec.KeySpec {
method public java.lang.String[] getBlockModes();
method public java.lang.String[] getDigests();
@@ -28777,9 +28594,9 @@
method public int getPurposes();
method public java.lang.String[] getSignaturePaddings();
method public int getUserAuthenticationValidityDurationSeconds();
- method public boolean isTeeBacked();
+ method public boolean isInsideSecureHardware();
method public boolean isUserAuthenticationRequired();
- method public boolean isUserAuthenticationRequirementTeeEnforced();
+ method public boolean isUserAuthenticationRequirementEnforcedBySecureHardware();
}
public final class KeyStoreParameter implements java.security.KeyStore.ProtectionParameter {
@@ -30347,6 +30164,8 @@
method public android.telecom.Connection getPrimaryConnection();
method public final int getState();
method public final android.telecom.StatusHints getStatusHints();
+ method public android.telecom.Connection.VideoProvider getVideoProvider();
+ method public int getVideoState();
method public void onAudioStateChanged(android.telecom.AudioState);
method public void onConnectionAdded(android.telecom.Connection);
method public void onDisconnect();
@@ -30366,6 +30185,8 @@
method public final void setDisconnected(android.telecom.DisconnectCause);
method public final void setOnHold();
method public final void setStatusHints(android.telecom.StatusHints);
+ method public final void setVideoProvider(android.telecom.Connection, android.telecom.Connection.VideoProvider);
+ method public final void setVideoState(android.telecom.Connection, int);
field public static final long CONNECT_TIME_NOT_SPECIFIED = 0L; // 0x0L
}
@@ -30393,6 +30214,7 @@
method public final android.telecom.Connection.VideoProvider getVideoProvider();
method public final boolean isRingbackRequested();
method public void onAbort();
+ method public void onAnswer(int);
method public void onAnswer();
method public void onAudioStateChanged(android.telecom.AudioState);
method public void onDisconnect();
@@ -30422,7 +30244,11 @@
method public final void setRingbackRequested(boolean);
method public final void setRinging();
method public final void setStatusHints(android.telecom.StatusHints);
+ method public final void setVideoProvider(android.telecom.Connection.VideoProvider);
+ method public final void setVideoState(int);
method public static java.lang.String stateToString(int);
+ field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
+ field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000
field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
field public static final int CAPABILITY_HOLD = 1; // 0x1
field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
@@ -30430,6 +30256,12 @@
field public static final int CAPABILITY_MUTE = 64; // 0x40
field public static final int CAPABILITY_RESPOND_VIA_TEXT = 32; // 0x20
field public static final int CAPABILITY_SEPARATE_FROM_CONFERENCE = 4096; // 0x1000
+ field public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL = 768; // 0x300
+ field public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 256; // 0x100
+ field public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 512; // 0x200
+ field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL = 3072; // 0xc00
+ field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 1024; // 0x400
+ field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800
field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
field public static final int STATE_ACTIVE = 4; // 0x4
@@ -30475,10 +30307,12 @@
public final class ConnectionRequest implements android.os.Parcelable {
ctor public ConnectionRequest(android.telecom.PhoneAccountHandle, android.net.Uri, android.os.Bundle);
+ ctor public ConnectionRequest(android.telecom.PhoneAccountHandle, android.net.Uri, android.os.Bundle, int);
method public int describeContents();
method public android.telecom.PhoneAccountHandle getAccountHandle();
method public android.net.Uri getAddress();
method public android.os.Bundle getExtras();
+ method public int getVideoState();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telecom.ConnectionRequest> CREATOR;
}
@@ -34190,14 +34024,6 @@
}
public deprecated class FloatMath {
- method public static float ceil(float);
- method public static float cos(float);
- method public static float exp(float);
- method public static float floor(float);
- method public static float hypot(float, float);
- method public static float pow(float, float);
- method public static float sin(float);
- method public static float sqrt(float);
}
public final class JsonReader implements java.io.Closeable {
diff --git a/api/removed.txt b/api/removed.txt
index 5e77e15..0046a70 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -108,6 +108,21 @@
}
+package android.util {
+
+ public deprecated class FloatMath {
+ method public static float ceil(float);
+ method public static float cos(float);
+ method public static float exp(float);
+ method public static float floor(float);
+ method public static float hypot(float, float);
+ method public static float pow(float, float);
+ method public static float sin(float);
+ method public static float sqrt(float);
+ }
+
+}
+
package android.view {
public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
diff --git a/api/system-current.txt b/api/system-current.txt
index 11cbfc7..0c57b5a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -39,7 +39,6 @@
field public static final java.lang.String BIND_INCALL_SERVICE = "android.permission.BIND_INCALL_SERVICE";
field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD";
field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
- field public static final java.lang.String BIND_MEDIA_ROUTE_SERVICE = "android.permission.BIND_MEDIA_ROUTE_SERVICE";
field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
@@ -5944,7 +5943,6 @@
field public static final java.lang.String ACTION_SET_PROFILE_OWNER = "android.app.action.SET_PROFILE_OWNER";
field public static final java.lang.String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION";
field public static final java.lang.String ACTION_SYSTEM_UPDATE_POLICY_CHANGED = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED";
- field public static final int DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2
field public static final int ENCRYPTION_STATUS_ACTIVE = 3; // 0x3
field public static final int ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY = 4; // 0x4
@@ -6012,6 +6010,7 @@
field public static final int PERMISSION_POLICY_AUTO_DENY = 2; // 0x2
field public static final int PERMISSION_POLICY_AUTO_GRANT = 1; // 0x1
field public static final int PERMISSION_POLICY_PROMPT = 0; // 0x0
+ field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
field public static final int WIPE_EXTERNAL_STORAGE = 1; // 0x1
field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
@@ -18720,248 +18719,11 @@
}
-package android.media.routing {
-
- public final class MediaRouteSelector implements android.os.Parcelable {
- method public boolean containsProtocol(java.lang.Class<?>);
- method public boolean containsProtocol(java.lang.String);
- method public int describeContents();
- method public android.os.Bundle getExtras();
- method public int getOptionalFeatures();
- method public java.util.List<java.lang.String> getOptionalProtocols();
- method public int getRequiredFeatures();
- method public java.util.List<java.lang.String> getRequiredProtocols();
- method public java.lang.String getServicePackageName();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.media.routing.MediaRouteSelector> CREATOR;
- }
-
- public static final class MediaRouteSelector.Builder {
- ctor public MediaRouteSelector.Builder();
- method public android.media.routing.MediaRouteSelector.Builder addOptionalProtocol(java.lang.Class<?>);
- method public android.media.routing.MediaRouteSelector.Builder addOptionalProtocol(java.lang.String);
- method public android.media.routing.MediaRouteSelector.Builder addRequiredProtocol(java.lang.Class<?>);
- method public android.media.routing.MediaRouteSelector.Builder addRequiredProtocol(java.lang.String);
- method public android.media.routing.MediaRouteSelector build();
- method public android.media.routing.MediaRouteSelector.Builder setExtras(android.os.Bundle);
- method public android.media.routing.MediaRouteSelector.Builder setOptionalFeatures(int);
- method public android.media.routing.MediaRouteSelector.Builder setRequiredFeatures(int);
- method public android.media.routing.MediaRouteSelector.Builder setServicePackageName(java.lang.String);
- }
-
- public abstract class MediaRouteService extends android.app.Service {
- ctor public MediaRouteService();
- method public android.media.routing.MediaRouter.ServiceMetadata getServiceMetadata();
- method public android.os.IBinder onBind(android.content.Intent);
- method public abstract android.media.routing.MediaRouteService.ClientSession onCreateClientSession(android.media.routing.MediaRouteService.ClientInfo);
- field public static final java.lang.String SERVICE_INTERFACE = "android.media.routing.MediaRouteService";
- }
-
- public static final class MediaRouteService.ClientInfo {
- method public java.lang.String getPackageName();
- method public int getUid();
- }
-
- public static abstract class MediaRouteService.ClientSession {
- ctor public MediaRouteService.ClientSession();
- method public abstract boolean onConnect(android.media.routing.MediaRouter.ConnectionRequest, android.media.routing.MediaRouteService.ConnectionCallback);
- method public abstract void onDisconnect();
- method public void onPauseStream();
- method public void onRelease();
- method public void onResumeStream();
- method public abstract boolean onStartDiscovery(android.media.routing.MediaRouter.DiscoveryRequest, android.media.routing.MediaRouteService.DiscoveryCallback);
- method public abstract void onStopDiscovery();
- }
-
- public final class MediaRouteService.ConnectionCallback {
- method public void onConnected(android.media.routing.MediaRouter.ConnectionInfo);
- method public void onConnectionFailed(int, java.lang.CharSequence, android.os.Bundle);
- method public void onDisconnected();
- }
-
- public final class MediaRouteService.DiscoveryCallback {
- method public void onDestinationFound(android.media.routing.MediaRouter.DestinationInfo, java.util.List<android.media.routing.MediaRouter.RouteInfo>);
- method public void onDestinationLost(android.media.routing.MediaRouter.DestinationInfo);
- method public void onDiscoveryFailed(int, java.lang.CharSequence, android.os.Bundle);
- }
-
- public final class MediaRouter {
- ctor public MediaRouter(android.content.Context);
- method public void addSelector(android.media.routing.MediaRouteSelector);
- method public void clearSelectors();
- method public android.media.routing.MediaRouter.Delegate createDelegate();
- method public android.media.routing.MediaRouter.ConnectionInfo getConnection();
- method public int getConnectionState();
- method public java.util.List<android.media.routing.MediaRouter.DestinationInfo> getDiscoveredDestinations();
- method public java.util.List<android.media.routing.MediaRouter.RouteInfo> getDiscoveredRoutes(android.media.routing.MediaRouter.DestinationInfo);
- method public int getDiscoveryState();
- method public android.media.AudioAttributes getPreferredAudioAttributes();
- method public android.view.Display getPreferredPresentationDisplay();
- method public android.media.VolumeProvider getPreferredVolumeProvider();
- method public android.media.routing.MediaRouter.DestinationInfo getSelectedDestination();
- method public android.media.routing.MediaRouter.RouteInfo getSelectedRoute();
- method public java.util.List<android.media.routing.MediaRouteSelector> getSelectors();
- method public boolean isReleased();
- method public void pauseStream();
- method public void release();
- method public void removeSelector(android.media.routing.MediaRouteSelector);
- method public void resumeStream();
- method public void setRoutingCallback(android.media.routing.MediaRouter.RoutingCallback, android.os.Handler);
- field public static final int CONNECTION_ERROR_ABORTED = 1; // 0x1
- field public static final int CONNECTION_ERROR_BARGED = 7; // 0x7
- field public static final int CONNECTION_ERROR_BROKEN = 6; // 0x6
- field public static final int CONNECTION_ERROR_BUSY = 4; // 0x4
- field public static final int CONNECTION_ERROR_TIMEOUT = 5; // 0x5
- field public static final int CONNECTION_ERROR_UNAUTHORIZED = 2; // 0x2
- field public static final int CONNECTION_ERROR_UNKNOWN = 0; // 0x0
- field public static final int CONNECTION_ERROR_UNREACHABLE = 3; // 0x3
- field public static final int CONNECTION_FLAG_BARGE = 1; // 0x1
- field public static final int CONNECTION_STATE_CONNECTED = 2; // 0x2
- field public static final int CONNECTION_STATE_CONNECTING = 1; // 0x1
- field public static final int CONNECTION_STATE_DISCONNECTED = 0; // 0x0
- field public static final int DISCONNECTION_REASON_APPLICATION_REQUEST = 0; // 0x0
- field public static final int DISCONNECTION_REASON_ERROR = 2; // 0x2
- field public static final int DISCONNECTION_REASON_USER_REQUEST = 1; // 0x1
- field public static final int DISCOVERY_ERROR_ABORTED = 1; // 0x1
- field public static final int DISCOVERY_ERROR_NO_CONNECTIVITY = 2; // 0x2
- field public static final int DISCOVERY_ERROR_UNKNOWN = 0; // 0x0
- field public static final int DISCOVERY_FLAG_BACKGROUND = 1; // 0x1
- field public static final int DISCOVERY_STATE_STARTED = 1; // 0x1
- field public static final int DISCOVERY_STATE_STOPPED = 0; // 0x0
- field public static final int ROUTE_FEATURE_LIVE_AUDIO = 1; // 0x1
- field public static final int ROUTE_FEATURE_LIVE_VIDEO = 2; // 0x2
- }
-
- public static final class MediaRouter.ConnectionInfo {
- method public android.media.AudioAttributes getAudioAttributes();
- method public android.os.Bundle getExtras();
- method public int getFeatures();
- method public android.view.Display getPresentationDisplay();
- method public android.os.IBinder getProtocolBinder(java.lang.String);
- method public android.os.IBinder getProtocolBinder(int);
- method public T getProtocolObject(java.lang.Class<T>);
- method public java.util.List<java.lang.String> getProtocols();
- method public android.media.routing.MediaRouter.RouteInfo getRoute();
- method public android.media.VolumeProvider getVolumeProvider();
- }
-
- public static final class MediaRouter.ConnectionInfo.Builder {
- ctor public MediaRouter.ConnectionInfo.Builder(android.media.routing.MediaRouter.RouteInfo);
- method public android.media.routing.MediaRouter.ConnectionInfo build();
- method public android.media.routing.MediaRouter.ConnectionInfo.Builder setAudioAttributes(android.media.AudioAttributes);
- method public android.media.routing.MediaRouter.ConnectionInfo.Builder setExtras(android.os.Bundle);
- method public android.media.routing.MediaRouter.ConnectionInfo.Builder setPresentationDisplay(android.view.Display);
- method public android.media.routing.MediaRouter.ConnectionInfo.Builder setProtocolBinder(java.lang.String, android.os.IBinder);
- method public android.media.routing.MediaRouter.ConnectionInfo.Builder setProtocolStub(java.lang.Class<?>, android.os.IInterface);
- method public android.media.routing.MediaRouter.ConnectionInfo.Builder setVolumeProvider(android.media.VolumeProvider);
- }
-
- public static final class MediaRouter.ConnectionRequest {
- method public android.os.Bundle getExtras();
- method public int getFlags();
- method public android.media.routing.MediaRouter.RouteInfo getRoute();
- method public void setExtras(android.os.Bundle);
- method public void setFlags(int);
- method public void setRoute(android.media.routing.MediaRouter.RouteInfo);
- }
-
- public static final class MediaRouter.Delegate {
- ctor public MediaRouter.Delegate();
- method public void addStateCallback(android.media.routing.MediaRouter.StateCallback, android.os.Handler);
- method public void connect(android.media.routing.MediaRouter.DestinationInfo, int);
- method public void disconnect(int);
- method public int getConnectionState();
- method public java.util.List<android.media.routing.MediaRouter.DestinationInfo> getDiscoveredDestinations();
- method public int getDiscoveryState();
- method public android.media.routing.MediaRouter.DestinationInfo getSelectedDestination();
- method public boolean isReleased();
- method public void removeStateCallback(android.media.routing.MediaRouter.StateCallback);
- method public void startDiscovery(int);
- method public void stopDiscovery();
- }
-
- public static final class MediaRouter.DestinationInfo {
- method public java.lang.CharSequence getDescription();
- method public android.os.Bundle getExtras();
- method public int getIconResourceId();
- method public java.lang.String getId();
- method public java.lang.CharSequence getName();
- method public android.media.routing.MediaRouter.ServiceMetadata getServiceMetadata();
- method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
- }
-
- public static final class MediaRouter.DestinationInfo.Builder {
- ctor public MediaRouter.DestinationInfo.Builder(java.lang.String, android.media.routing.MediaRouter.ServiceMetadata, java.lang.CharSequence);
- method public android.media.routing.MediaRouter.DestinationInfo build();
- method public android.media.routing.MediaRouter.DestinationInfo.Builder setDescription(java.lang.CharSequence);
- method public android.media.routing.MediaRouter.DestinationInfo.Builder setExtras(android.os.Bundle);
- method public android.media.routing.MediaRouter.DestinationInfo.Builder setIconResourceId(int);
- }
-
- public static final class MediaRouter.DiscoveryRequest {
- method public int getFlags();
- method public java.util.List<android.media.routing.MediaRouteSelector> getSelectors();
- method public void setFlags(int);
- method public void setSelectors(java.util.List<android.media.routing.MediaRouteSelector>);
- }
-
- public static final class MediaRouter.RouteInfo {
- method public android.media.routing.MediaRouter.DestinationInfo getDestination();
- method public android.os.Bundle getExtras();
- method public int getFeatures();
- method public java.lang.String getId();
- method public java.util.List<java.lang.String> getProtocols();
- method public android.media.routing.MediaRouteSelector getSelector();
- }
-
- public static final class MediaRouter.RouteInfo.Builder {
- ctor public MediaRouter.RouteInfo.Builder(java.lang.String, android.media.routing.MediaRouter.DestinationInfo, android.media.routing.MediaRouteSelector);
- method public android.media.routing.MediaRouter.RouteInfo.Builder addProtocol(java.lang.Class<T>);
- method public android.media.routing.MediaRouter.RouteInfo.Builder addProtocol(java.lang.String);
- method public android.media.routing.MediaRouter.RouteInfo build();
- method public android.media.routing.MediaRouter.RouteInfo.Builder setExtras(android.os.Bundle);
- method public android.media.routing.MediaRouter.RouteInfo.Builder setFeatures(int);
- }
-
- public static abstract class MediaRouter.RoutingCallback extends android.media.routing.MediaRouter.StateCallback {
- ctor public MediaRouter.RoutingCallback();
- method public boolean onPrepareConnectionRequest(android.media.routing.MediaRouter.ConnectionRequest, android.media.routing.MediaRouter.DestinationInfo, java.util.List<android.media.routing.MediaRouter.RouteInfo>);
- method public boolean onPrepareDiscoveryRequest(android.media.routing.MediaRouter.DiscoveryRequest, java.util.List<android.media.routing.MediaRouteSelector>);
- }
-
- public static final class MediaRouter.ServiceMetadata {
- method public android.content.ComponentName getComponentName();
- method public android.graphics.drawable.Drawable getIcon(android.content.pm.PackageManager);
- method public java.lang.CharSequence getLabel(android.content.pm.PackageManager);
- method public java.lang.String getPackageName();
- method public android.content.pm.ServiceInfo getService();
- }
-
- public static abstract class MediaRouter.StateCallback {
- ctor public MediaRouter.StateCallback();
- method public void onConnected();
- method public void onConnecting();
- method public void onConnectionFailed(int, java.lang.CharSequence, android.os.Bundle);
- method public void onConnectionStateChanged(int);
- method public void onDestinationFound(android.media.routing.MediaRouter.DestinationInfo);
- method public void onDestinationLost(android.media.routing.MediaRouter.DestinationInfo);
- method public void onDisconnected();
- method public void onDiscoveryFailed(int, java.lang.CharSequence, android.os.Bundle);
- method public void onDiscoveryStarted();
- method public void onDiscoveryStateChanged(int);
- method public void onDiscoveryStopped();
- method public void onReleased();
- method public void onSelectedDestinationChanged(android.media.routing.MediaRouter.DestinationInfo);
- }
-
-}
-
package android.media.session {
public final class MediaController {
ctor public MediaController(android.content.Context, android.media.session.MediaSession.Token);
method public void adjustVolume(int, int);
- method public android.media.routing.MediaRouter.Delegate createMediaRouterDelegate();
method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
method public android.os.Bundle getExtras();
method public long getFlags();
@@ -19035,7 +18797,6 @@
method public void setExtras(android.os.Bundle);
method public void setFlags(int);
method public void setMediaButtonReceiver(android.app.PendingIntent);
- method public void setMediaRouter(android.media.routing.MediaRouter);
method public void setMetadata(android.media.MediaMetadata);
method public void setPlaybackState(android.media.session.PlaybackState);
method public void setPlaybackToLocal(android.media.AudioAttributes);
@@ -24805,6 +24566,7 @@
ctor public Build.VERSION();
field public static final java.lang.String CODENAME;
field public static final java.lang.String INCREMENTAL;
+ field public static final int PREVIEW_SDK_INT;
field public static final java.lang.String RELEASE;
field public static final deprecated java.lang.String SDK;
field public static final int SDK_INT;
@@ -30759,6 +30521,53 @@
public abstract class KeyStoreKeyProperties {
}
+ public static abstract class KeyStoreKeyProperties.Algorithm {
+ field public static final java.lang.String AES = "AES";
+ field public static final java.lang.String EC = "EC";
+ field public static final java.lang.String HMAC_SHA1 = "HmacSHA1";
+ field public static final java.lang.String HMAC_SHA224 = "HmacSHA224";
+ field public static final java.lang.String HMAC_SHA256 = "HmacSHA256";
+ field public static final java.lang.String HMAC_SHA384 = "HmacSHA384";
+ field public static final java.lang.String HMAC_SHA512 = "HmacSHA512";
+ field public static final java.lang.String RSA = "RSA";
+ }
+
+ public static abstract class KeyStoreKeyProperties.AlgorithmEnum implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract class KeyStoreKeyProperties.BlockMode {
+ field public static final java.lang.String CBC = "CBC";
+ field public static final java.lang.String CTR = "CTR";
+ field public static final java.lang.String ECB = "ECB";
+ field public static final java.lang.String GCM = "GCM";
+ }
+
+ public static abstract class KeyStoreKeyProperties.BlockModeEnum implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract class KeyStoreKeyProperties.Digest {
+ field public static final java.lang.String MD5 = "MD5";
+ field public static final java.lang.String NONE = "NONE";
+ field public static final java.lang.String SHA1 = "SHA-1";
+ field public static final java.lang.String SHA224 = "SHA-224";
+ field public static final java.lang.String SHA256 = "SHA-256";
+ field public static final java.lang.String SHA384 = "SHA-384";
+ field public static final java.lang.String SHA512 = "SHA-512";
+ }
+
+ public static abstract class KeyStoreKeyProperties.DigestEnum implements java.lang.annotation.Annotation {
+ }
+
+ public static abstract class KeyStoreKeyProperties.EncryptionPadding {
+ field public static final java.lang.String NONE = "NoPadding";
+ field public static final java.lang.String PKCS7 = "PKCS7Padding";
+ field public static final java.lang.String RSA_OAEP = "OAEPPadding";
+ field public static final java.lang.String RSA_PKCS1 = "PKCS1Padding";
+ }
+
+ public static abstract class KeyStoreKeyProperties.EncryptionPaddingEnum implements java.lang.annotation.Annotation {
+ }
+
public static abstract class KeyStoreKeyProperties.Origin {
field public static final int GENERATED = 1; // 0x1
field public static final int IMPORTED = 2; // 0x2
@@ -30778,6 +30587,14 @@
public static abstract class KeyStoreKeyProperties.PurposeEnum implements java.lang.annotation.Annotation {
}
+ public static abstract class KeyStoreKeyProperties.SignaturePadding {
+ field public static final java.lang.String RSA_PKCS1 = "PKCS1";
+ field public static final java.lang.String RSA_PSS = "PSS";
+ }
+
+ public static abstract class KeyStoreKeyProperties.SignaturePaddingEnum implements java.lang.annotation.Annotation {
+ }
+
public class KeyStoreKeySpec implements java.security.spec.KeySpec {
method public java.lang.String[] getBlockModes();
method public java.lang.String[] getDigests();
@@ -30791,9 +30608,9 @@
method public int getPurposes();
method public java.lang.String[] getSignaturePaddings();
method public int getUserAuthenticationValidityDurationSeconds();
- method public boolean isTeeBacked();
+ method public boolean isInsideSecureHardware();
method public boolean isUserAuthenticationRequired();
- method public boolean isUserAuthenticationRequirementTeeEnforced();
+ method public boolean isUserAuthenticationRequirementEnforcedBySecureHardware();
}
public final class KeyStoreParameter implements java.security.KeyStore.ProtectionParameter {
@@ -32468,6 +32285,8 @@
method public android.telecom.Connection getPrimaryConnection();
method public final int getState();
method public final android.telecom.StatusHints getStatusHints();
+ method public android.telecom.Connection.VideoProvider getVideoProvider();
+ method public int getVideoState();
method public void onAudioStateChanged(android.telecom.AudioState);
method public void onConnectionAdded(android.telecom.Connection);
method public void onDisconnect();
@@ -32487,6 +32306,8 @@
method public final void setDisconnected(android.telecom.DisconnectCause);
method public final void setOnHold();
method public final void setStatusHints(android.telecom.StatusHints);
+ method public final void setVideoProvider(android.telecom.Connection, android.telecom.Connection.VideoProvider);
+ method public final void setVideoState(android.telecom.Connection, int);
field public static final long CONNECT_TIME_NOT_SPECIFIED = 0L; // 0x0L
}
@@ -32514,6 +32335,7 @@
method public final android.telecom.Connection.VideoProvider getVideoProvider();
method public final boolean isRingbackRequested();
method public void onAbort();
+ method public void onAnswer(int);
method public void onAnswer();
method public void onAudioStateChanged(android.telecom.AudioState);
method public void onDisconnect();
@@ -32543,7 +32365,11 @@
method public final void setRingbackRequested(boolean);
method public final void setRinging();
method public final void setStatusHints(android.telecom.StatusHints);
+ method public final void setVideoProvider(android.telecom.Connection.VideoProvider);
+ method public final void setVideoState(int);
method public static java.lang.String stateToString(int);
+ field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
+ field public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 524288; // 0x80000
field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
field public static final int CAPABILITY_HOLD = 1; // 0x1
field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
@@ -32551,6 +32377,12 @@
field public static final int CAPABILITY_MUTE = 64; // 0x40
field public static final int CAPABILITY_RESPOND_VIA_TEXT = 32; // 0x20
field public static final int CAPABILITY_SEPARATE_FROM_CONFERENCE = 4096; // 0x1000
+ field public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL = 768; // 0x300
+ field public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 256; // 0x100
+ field public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 512; // 0x200
+ field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL = 3072; // 0xc00
+ field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 1024; // 0x400
+ field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800
field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
field public static final int STATE_ACTIVE = 4; // 0x4
@@ -32596,10 +32428,12 @@
public final class ConnectionRequest implements android.os.Parcelable {
ctor public ConnectionRequest(android.telecom.PhoneAccountHandle, android.net.Uri, android.os.Bundle);
+ ctor public ConnectionRequest(android.telecom.PhoneAccountHandle, android.net.Uri, android.os.Bundle, int);
method public int describeContents();
method public android.telecom.PhoneAccountHandle getAccountHandle();
method public android.net.Uri getAddress();
method public android.os.Bundle getExtras();
+ method public int getVideoState();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telecom.ConnectionRequest> CREATOR;
}
@@ -36401,14 +36235,6 @@
}
public deprecated class FloatMath {
- method public static float ceil(float);
- method public static float cos(float);
- method public static float exp(float);
- method public static float floor(float);
- method public static float hypot(float, float);
- method public static float pow(float, float);
- method public static float sin(float);
- method public static float sqrt(float);
}
public final class JsonReader implements java.io.Closeable {
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 5e77e15..0046a70 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -108,6 +108,21 @@
}
+package android.util {
+
+ public deprecated class FloatMath {
+ method public static float ceil(float);
+ method public static float cos(float);
+ method public static float exp(float);
+ method public static float floor(float);
+ method public static float hypot(float, float);
+ method public static float pow(float, float);
+ method public static float sin(float);
+ method public static float sqrt(float);
+ }
+
+}
+
package android.view {
public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index bb25ec6..21dc1e2 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -630,7 +630,10 @@
}
glDisable(GL_SCISSOR_TEST);
}
- glDrawTexiOES(xc, yc, 0, animation.width, animation.height);
+ // specify the y center as ceiling((mHeight - animation.height) / 2)
+ // which is equivalent to mHeight - (yc + animation.height)
+ glDrawTexiOES(xc, mHeight - (yc + animation.height),
+ 0, animation.width, animation.height);
eglSwapBuffers(mDisplay, mSurface);
nsecs_t now = systemTime();
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java
index 427ecce..435d5ab 100644
--- a/core/java/android/animation/AnimatorInflater.java
+++ b/core/java/android/animation/AnimatorInflater.java
@@ -108,7 +108,7 @@
float pathErrorScale) throws NotFoundException {
final ConfigurationBoundResourceCache<Animator> animatorCache = resources
.getAnimatorCache();
- Animator animator = animatorCache.get(id, theme);
+ Animator animator = animatorCache.getInstance(id, theme);
if (animator != null) {
if (DBG_ANIMATOR_INFLATER) {
Log.d(TAG, "loaded animator from cache, " + resources.getResourceName(id));
@@ -157,7 +157,7 @@
final ConfigurationBoundResourceCache<StateListAnimator> cache = resources
.getStateListAnimatorCache();
final Theme theme = context.getTheme();
- StateListAnimator animator = cache.get(id, theme);
+ StateListAnimator animator = cache.getInstance(id, theme);
if (animator != null) {
return animator;
}
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
index 5790682..cdd72be 100644
--- a/core/java/android/animation/LayoutTransition.java
+++ b/core/java/android/animation/LayoutTransition.java
@@ -28,6 +28,7 @@
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
/**
* This class enables automatic animations on layout changes in ViewGroup objects. To enable
@@ -757,7 +758,7 @@
// reset the inter-animation delay, in case we use it later
staggerDelay = 0;
- final ViewTreeObserver observer = parent.getViewTreeObserver(); // used for later cleanup
+ final ViewTreeObserver observer = parent.getViewTreeObserver();
if (!observer.isAlive()) {
// If the observer's not in a good state, skip the transition
return;
@@ -790,21 +791,9 @@
// This is the cleanup step. When we get this rendering event, we know that all of
// the appropriate animations have been set up and run. Now we can clear out the
// layout listeners.
- observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
- public boolean onPreDraw() {
- parent.getViewTreeObserver().removeOnPreDrawListener(this);
- int count = layoutChangeListenerMap.size();
- if (count > 0) {
- Collection<View> views = layoutChangeListenerMap.keySet();
- for (View view : views) {
- View.OnLayoutChangeListener listener = layoutChangeListenerMap.get(view);
- view.removeOnLayoutChangeListener(listener);
- }
- }
- layoutChangeListenerMap.clear();
- return true;
- }
- });
+ CleanupCallback callback = new CleanupCallback(layoutChangeListenerMap, parent);
+ observer.addOnPreDrawListener(callback);
+ parent.addOnAttachStateChangeListener(callback);
}
/**
@@ -1499,4 +1488,50 @@
View view, int transitionType);
}
+ /**
+ * Utility class to clean up listeners after animations are setup. Cleanup happens
+ * when either the OnPreDrawListener method is called or when the parent is detached,
+ * whichever comes first.
+ */
+ private static final class CleanupCallback implements ViewTreeObserver.OnPreDrawListener,
+ View.OnAttachStateChangeListener {
+
+ final Map<View, View.OnLayoutChangeListener> layoutChangeListenerMap;
+ final ViewGroup parent;
+
+ CleanupCallback(Map<View, View.OnLayoutChangeListener> listenerMap, ViewGroup parent) {
+ this.layoutChangeListenerMap = listenerMap;
+ this.parent = parent;
+ }
+
+ private void cleanup() {
+ parent.getViewTreeObserver().removeOnPreDrawListener(this);
+ parent.removeOnAttachStateChangeListener(this);
+ int count = layoutChangeListenerMap.size();
+ if (count > 0) {
+ Collection<View> views = layoutChangeListenerMap.keySet();
+ for (View view : views) {
+ View.OnLayoutChangeListener listener = layoutChangeListenerMap.get(view);
+ view.removeOnLayoutChangeListener(listener);
+ }
+ layoutChangeListenerMap.clear();
+ }
+ }
+
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ cleanup();
+ }
+
+ @Override
+ public boolean onPreDraw() {
+ cleanup();
+ return true;
+ }
+ };
+
}
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java
index 94e3b66..4d34349 100644
--- a/core/java/android/app/ActionBar.java
+++ b/core/java/android/app/ActionBar.java
@@ -33,6 +33,7 @@
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
+import android.view.ViewHierarchyEncoder;
import android.view.Window;
import android.widget.SpinnerAdapter;
import java.lang.annotation.Retention;
@@ -1373,5 +1374,13 @@
* version of the SDK an app can end up statically linking to the new MarginLayoutParams
* overload, causing a crash when running on older platform versions with no other changes.
*/
+
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
+ super.encodeProperties(encoder);
+
+ encoder.addProperty("gravity", gravity);
+ }
}
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 7260d102..9968dbb 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -89,7 +89,7 @@
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
-import android.view.PhoneWindow;
+import com.android.internal.policy.PhoneWindow;
import android.view.SearchEvent;
import android.view.View;
import android.view.View.OnCreateContextMenuListener;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index da6d8c5..f16406a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -38,6 +38,7 @@
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.Resources.Theme;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDebug;
import android.database.sqlite.SQLiteDebug.DbStats;
@@ -4186,9 +4187,14 @@
if (!mConfiguration.isOtherSeqNewer(config) && compat == null) {
return;
}
- configDiff = mConfiguration.diff(config);
- mConfiguration.updateFrom(config);
+
+ configDiff = mConfiguration.updateFrom(config);
config = applyCompatConfiguration(mCurDefaultDisplayDpi);
+
+ final Theme systemTheme = getSystemContext().getTheme();
+ if ((systemTheme.getChangingConfigurations() & configDiff) != 0) {
+ systemTheme.rebase();
+ }
}
ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config);
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index 2939322..968c956 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -476,9 +476,8 @@
tempRect.set(0, 0, width, height);
view.getMatrix().mapRect(tempRect);
- ViewGroup parent = (ViewGroup) view.getParent();
- left = leftInParent - tempRect.left + parent.getScrollX();
- top = topInParent - tempRect.top + parent.getScrollY();
+ left = leftInParent - tempRect.left;
+ top = topInParent - tempRect.top;
right = left + width;
bottom = top + height;
}
@@ -506,7 +505,7 @@
ViewGroup parent = (ViewGroup) view.getParent();
Matrix matrix = new Matrix();
parent.transformMatrixToLocal(matrix);
-
+ matrix.postTranslate(parent.getScrollX(), parent.getScrollY());
mSharedElementParentMatrices.add(matrix);
}
}
@@ -521,6 +520,7 @@
// Find the location in the view's parent
ViewGroup parent = (ViewGroup) viewParent;
parent.transformMatrixToLocal(matrix);
+ matrix.postTranslate(parent.getScrollX(), parent.getScrollY());
}
} else {
// The indices of mSharedElementParentMatrices matches the
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index 75e4bab..1174387 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -18,6 +18,7 @@
import java.util.ArrayList;
+import android.annotation.CallSuper;
import android.content.ComponentCallbacks;
import android.content.ComponentCallbacks2;
import android.content.Context;
@@ -89,6 +90,7 @@
* service, or receiver in a process.
* If you override this method, be sure to call super.onCreate().
*/
+ @CallSuper
public void onCreate() {
}
@@ -98,9 +100,11 @@
* removed by simply killing them; no user code (including this callback)
* is executed when doing so.
*/
+ @CallSuper
public void onTerminate() {
}
+ @CallSuper
public void onConfigurationChanged(Configuration newConfig) {
Object[] callbacks = collectComponentCallbacks();
if (callbacks != null) {
@@ -110,6 +114,7 @@
}
}
+ @CallSuper
public void onLowMemory() {
Object[] callbacks = collectComponentCallbacks();
if (callbacks != null) {
@@ -119,6 +124,7 @@
}
}
+ @CallSuper
public void onTrimMemory(int level) {
Object[] callbacks = collectComponentCallbacks();
if (callbacks != null) {
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index d049104..f6e0e1e 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -48,7 +48,7 @@
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
-import android.view.PhoneWindow;
+import com.android.internal.policy.PhoneWindow;
import android.view.SearchEvent;
import android.view.View;
import android.view.View.OnCreateContextMenuListener;
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 46da025..391131a 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -91,7 +91,6 @@
import android.os.IUserManager;
import android.os.PowerManager;
import android.os.Process;
-import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemVibrator;
import android.os.UserHandle;
@@ -111,7 +110,7 @@
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
-import android.view.PhoneLayoutInflater;
+import com.android.internal.policy.PhoneLayoutInflater;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
import android.view.accessibility.AccessibilityManager;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 8009b6c..355f298 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1653,9 +1653,9 @@
}
/**
- * Queries whether {@link #DO_NOT_ASK_CREDENTIALS_ON_BOOT} flag is set.
+ * Queries whether {@link #RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT} flag is set.
*
- * @return true if DO_NOT_ASK_CREDENTIALS_ON_BOOT flag is set.
+ * @return true if RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT flag is set.
* @hide
*/
public boolean getDoNotAskCredentialsOnBoot() {
@@ -1753,7 +1753,7 @@
* is ignored. Once the flag is set, it cannot be reverted back without resetting the
* device to factory defaults.
*/
- public static final int DO_NOT_ASK_CREDENTIALS_ON_BOOT = 0x0002;
+ public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 0x0002;
/**
* Force a new device unlock password (the password needed to access the
@@ -1779,7 +1779,7 @@
*
* @param password The new password for the user. Null or empty clears the password.
* @param flags May be 0 or combination of {@link #RESET_PASSWORD_REQUIRE_ENTRY} and
- * {@link #DO_NOT_ASK_CREDENTIALS_ON_BOOT}.
+ * {@link #RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT}.
* @return Returns true if the password was applied, or false if it is
* not acceptable for the current constraints.
*/
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 0a77868..ec6f18d 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -28,14 +28,11 @@
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
+import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.app.ActivityThread;
-import android.os.SystemProperties;
-import android.provider.Settings;
-import android.os.Binder;
import android.util.Log;
import android.util.Pair;
@@ -1255,7 +1252,7 @@
* @return true if chipset supports on-chip filtering
*/
public boolean isOffloadedFilteringSupported() {
- if (getState() != STATE_ON) return false;
+ if (!getLeAccess()) return false;
try {
return mService.isOffloadedFilteringSupported();
} catch (RemoteException e) {
@@ -1270,7 +1267,7 @@
* @return true if chipset supports on-chip scan batching
*/
public boolean isOffloadedScanBatchingSupported() {
- if (getState() != STATE_ON) return false;
+ if (!getLeAccess()) return false;
try {
return mService.isOffloadedScanBatchingSupported();
} catch (RemoteException e) {
@@ -1286,7 +1283,7 @@
* @hide
*/
public boolean isHardwareTrackingFiltersAvailable() {
- if (getState() != STATE_ON) return false;
+ if (!getLeAccess()) return false;
try {
IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
if (iGatt == null) {
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 525059f..8d96f5c2 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -785,6 +785,7 @@
private native final void deleteTheme(long theme);
/*package*/ native static final void applyThemeStyle(long theme, int styleRes, boolean force);
/*package*/ native static final void copyTheme(long dest, long source);
+ /*package*/ native static final void clearTheme(long theme);
/*package*/ native static final int loadThemeAttributeValue(long theme, int ident,
TypedValue outValue,
boolean resolve);
diff --git a/core/java/android/content/res/ConfigurationBoundResourceCache.java b/core/java/android/content/res/ConfigurationBoundResourceCache.java
index cde7e84..fecda87 100644
--- a/core/java/android/content/res/ConfigurationBoundResourceCache.java
+++ b/core/java/android/content/res/ConfigurationBoundResourceCache.java
@@ -1,138 +1,58 @@
/*
-* 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.content.res;
+ * 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.
+ */
-import android.util.ArrayMap;
-import android.util.LongSparseArray;
-import java.lang.ref.WeakReference;
+package android.content.res;
/**
* A Cache class which can be used to cache resource objects that are easy to clone but more
* expensive to inflate.
- * @hide
+ *
+ * @hide For internal use only.
*/
-public class ConfigurationBoundResourceCache<T> {
-
- private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState<T>>>> mCache =
- new ArrayMap<String, LongSparseArray<WeakReference<ConstantState<T>>>>();
-
- final Resources mResources;
+public class ConfigurationBoundResourceCache<T> extends ThemedResourceCache<ConstantState<T>> {
+ private final Resources mResources;
/**
- * Creates a Resource cache for the given Resources instance.
+ * Creates a cache for the given Resources instance.
*
- * @param resources The Resource which can be used when creating new instances.
+ * @param resources the resources to use when creating new instances
*/
public ConfigurationBoundResourceCache(Resources resources) {
mResources = resources;
}
/**
- * Adds a new item to the cache.
+ * If the resource is cached, creates and returns a new instance of it.
*
- * @param key A custom key that uniquely identifies the resource.
- * @param theme The Theme instance where this resource was loaded.
- * @param constantState The constant state that can create new instances of the resource.
- *
+ * @param key a key that uniquely identifies the drawable resource
+ * @param theme the theme where the resource will be used
+ * @return a new instance of the resource, or {@code null} if not in
+ * the cache
*/
- public void put(long key, Resources.Theme theme, ConstantState<T> constantState) {
- if (constantState == null) {
- return;
- }
- final String themeKey = theme == null ? "" : theme.getKey();
- LongSparseArray<WeakReference<ConstantState<T>>> themedCache;
- synchronized (this) {
- themedCache = mCache.get(themeKey);
- if (themedCache == null) {
- themedCache = new LongSparseArray<WeakReference<ConstantState<T>>>(1);
- mCache.put(themeKey, themedCache);
- }
- themedCache.put(key, new WeakReference<ConstantState<T>>(constantState));
- }
- }
-
- /**
- * If the resource is cached, creates a new instance of it and returns.
- *
- * @param key The long key which can be used to uniquely identify the resource.
- * @param theme The The Theme instance where we want to load this resource.
- *
- * @return If this resources was loaded before, returns a new instance of it. Otherwise, returns
- * null.
- */
- public T get(long key, Resources.Theme theme) {
- final String themeKey = theme != null ? theme.getKey() : "";
- final LongSparseArray<WeakReference<ConstantState<T>>> themedCache;
- final WeakReference<ConstantState<T>> wr;
- synchronized (this) {
- themedCache = mCache.get(themeKey);
- if (themedCache == null) {
- return null;
- }
- wr = themedCache.get(key);
- }
- if (wr == null) {
- return null;
- }
- final ConstantState entry = wr.get();
+ public T getInstance(long key, Resources.Theme theme) {
+ final ConstantState<T> entry = get(key, theme);
if (entry != null) {
- return (T) entry.newInstance(mResources, theme);
- } else { // our entry has been purged
- synchronized (this) {
- // there is a potential race condition here where this entry may be put in
- // another thread. But we prefer it to minimize lock duration
- themedCache.delete(key);
- }
+ return entry.newInstance(mResources, theme);
}
+
return null;
}
- /**
- * Users of ConfigurationBoundResourceCache must call this method whenever a configuration
- * change happens. On this callback, the cache invalidates all resources that are not valid
- * anymore.
- *
- * @param configChanges The configuration changes
- */
- public void onConfigurationChange(final int configChanges) {
- synchronized (this) {
- final int size = mCache.size();
- for (int i = size - 1; i >= 0; i--) {
- final LongSparseArray<WeakReference<ConstantState<T>>>
- themeCache = mCache.valueAt(i);
- onConfigurationChangeInt(themeCache, configChanges);
- if (themeCache.size() == 0) {
- mCache.removeAt(i);
- }
- }
- }
+ @Override
+ public boolean shouldInvalidateEntry(ConstantState<T> entry, int configChanges) {
+ return Configuration.needNewResources(configChanges, entry.getChangingConfigurations());
}
-
- private void onConfigurationChangeInt(
- final LongSparseArray<WeakReference<ConstantState<T>>> themeCache,
- final int configChanges) {
- final int size = themeCache.size();
- for (int i = size - 1; i >= 0; i--) {
- final WeakReference<ConstantState<T>> wr = themeCache.valueAt(i);
- final ConstantState<T> constantState = wr.get();
- if (constantState == null || Configuration.needNewResources(
- configChanges, constantState.getChangingConfigurations())) {
- themeCache.removeAt(i);
- }
- }
- }
-
}
diff --git a/core/java/android/content/res/DrawableCache.java b/core/java/android/content/res/DrawableCache.java
new file mode 100644
index 0000000..fc70bc60
--- /dev/null
+++ b/core/java/android/content/res/DrawableCache.java
@@ -0,0 +1,57 @@
+/*
+ * 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 android.content.res;
+
+import android.graphics.drawable.Drawable;
+
+/**
+ * Class which can be used to cache Drawable resources against a theme.
+ */
+class DrawableCache extends ThemedResourceCache<Drawable.ConstantState> {
+ private final Resources mResources;
+
+ /**
+ * Creates a cache for the given Resources instance.
+ *
+ * @param resources the resources to use when creating new instances
+ */
+ public DrawableCache(Resources resources) {
+ mResources = resources;
+ }
+
+ /**
+ * If the resource is cached, creates and returns a new instance of it.
+ *
+ * @param key a key that uniquely identifies the drawable resource
+ * @param theme the theme where the resource will be used
+ * @return a new instance of the resource, or {@code null} if not in
+ * the cache
+ */
+ public Drawable getInstance(long key, Resources.Theme theme) {
+ final Drawable.ConstantState entry = get(key, theme);
+ if (entry != null) {
+ return entry.newDrawable(mResources, theme);
+ }
+
+ return null;
+ }
+
+ @Override
+ public boolean shouldInvalidateEntry(Drawable.ConstantState entry, int configChanges) {
+ return false;
+ }
+}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index ae41b69..a572590 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -20,6 +20,7 @@
import android.annotation.ColorInt;
import android.annotation.StyleRes;
import android.annotation.StyleableRes;
+import com.android.internal.util.GrowingArrayUtils;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -60,6 +61,7 @@
import android.util.Slog;
import android.util.TypedValue;
import android.view.ViewDebug;
+import android.view.ViewHierarchyEncoder;
import java.io.IOException;
import java.io.InputStream;
@@ -132,10 +134,8 @@
// These are protected by mAccessLock.
private final Object mAccessLock = new Object();
private final Configuration mTmpConfig = new Configuration();
- private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> mDrawableCache =
- new ArrayMap<>();
- private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> mColorDrawableCache =
- new ArrayMap<>();
+ private final DrawableCache mDrawableCache = new DrawableCache(this);
+ private final DrawableCache mColorDrawableCache = new DrawableCache(this);
private final ConfigurationBoundResourceCache<ColorStateList> mColorStateListCache =
new ConfigurationBoundResourceCache<>(this);
private final ConfigurationBoundResourceCache<Animator> mAnimatorCache =
@@ -1441,7 +1441,7 @@
AssetManager.applyThemeStyle(mTheme, resId, force);
mThemeResId = resId;
- mKey += Integer.toHexString(resId) + (force ? "! " : " ");
+ mKey.append(resId, force);
}
/**
@@ -1457,7 +1457,7 @@
AssetManager.copyTheme(mTheme, other.mTheme);
mThemeResId = other.mThemeResId;
- mKey = other.mKey;
+ mKey.setTo(other.getKey());
}
/**
@@ -1765,6 +1765,9 @@
mTheme = mAssets.createTheme();
}
+ /** Unique key for the series of styles applied to this theme. */
+ private final ThemeKey mKey = new ThemeKey();
+
@SuppressWarnings("hiding")
private final AssetManager mAssets;
private final long mTheme;
@@ -1772,9 +1775,6 @@
/** Resource identifier for the theme. */
private int mThemeResId = 0;
- /** Unique key for the series of styles applied to this theme. */
- private String mKey = "";
-
// Needed by layoutlib.
/*package*/ long getNativeTheme() {
return mTheme;
@@ -1784,7 +1784,7 @@
return mThemeResId;
}
- /*package*/ String getKey() {
+ /*package*/ ThemeKey getKey() {
return mKey;
}
@@ -1793,29 +1793,133 @@
}
/**
- * Parses {@link #mKey} and returns a String array that holds pairs of adjacent Theme data:
- * resource name followed by whether or not it was forced, as specified by
- * {@link #applyStyle(int, boolean)}.
+ * Parses {@link #mKey} and returns a String array that holds pairs of
+ * adjacent Theme data: resource name followed by whether or not it was
+ * forced, as specified by {@link #applyStyle(int, boolean)}.
*
* @hide
*/
@ViewDebug.ExportedProperty(category = "theme", hasAdjacentMapping = true)
public String[] getTheme() {
- String[] themeData = mKey.split(" ");
- String[] themes = new String[themeData.length * 2];
- String theme;
- boolean forced;
-
- for (int i = 0, j = themeData.length - 1; i < themes.length; i += 2, --j) {
- theme = themeData[j];
- forced = theme.endsWith("!");
- themes[i] = forced ?
- getResourceNameFromHexString(theme.substring(0, theme.length() - 1)) :
- getResourceNameFromHexString(theme);
+ final int N = mKey.mCount;
+ final String[] themes = new String[N * 2];
+ for (int i = 0, j = N - 1; i < themes.length; i += 2, --j) {
+ final int resId = mKey.mResId[j];
+ final boolean forced = mKey.mForce[j];
+ try {
+ themes[i] = getResourceName(resId);
+ } catch (NotFoundException e) {
+ themes[i] = Integer.toHexString(i);
+ }
themes[i + 1] = forced ? "forced" : "not forced";
}
return themes;
}
+
+ /** @hide */
+ public void encode(@NonNull ViewHierarchyEncoder encoder) {
+ encoder.beginObject(this);
+ final String[] properties = getTheme();
+ for (int i = 0; i < properties.length; i += 2) {
+ encoder.addProperty(properties[i], properties[i+1]);
+ }
+ encoder.endObject();
+ }
+
+ /**
+ * Rebases the theme against the parent Resource object's current
+ * configuration by re-applying the styles passed to
+ * {@link #applyStyle(int, boolean)}.
+ *
+ * @hide
+ */
+ public void rebase() {
+ AssetManager.clearTheme(mTheme);
+
+ // Reapply the same styles in the same order.
+ for (int i = 0; i < mKey.mCount; i++) {
+ final int resId = mKey.mResId[i];
+ final boolean force = mKey.mForce[i];
+ AssetManager.applyThemeStyle(mTheme, resId, force);
+ }
+ }
+ }
+
+ static class ThemeKey implements Cloneable {
+ int[] mResId;
+ boolean[] mForce;
+ int mCount;
+
+ private int mHashCode = 0;
+
+ public void append(int resId, boolean force) {
+ if (mResId == null) {
+ mResId = new int[4];
+ }
+
+ if (mForce == null) {
+ mForce = new boolean[4];
+ }
+
+ mResId = GrowingArrayUtils.append(mResId, mCount, resId);
+ mForce = GrowingArrayUtils.append(mForce, mCount, force);
+ mCount++;
+
+ mHashCode = 31 * (31 * mHashCode + resId) + (force ? 1 : 0);
+ }
+
+ /**
+ * Sets up this key as a deep copy of another key.
+ *
+ * @param other the key to deep copy into this key
+ */
+ public void setTo(ThemeKey other) {
+ mResId = other.mResId == null ? null : other.mResId.clone();
+ mForce = other.mForce == null ? null : other.mForce.clone();
+ mCount = other.mCount;
+ }
+
+ @Override
+ public int hashCode() {
+ return mHashCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (o == null || getClass() != o.getClass() || hashCode() != o.hashCode()) {
+ return false;
+ }
+
+ final ThemeKey t = (ThemeKey) o;
+ if (mCount != t.mCount) {
+ return false;
+ }
+
+ final int N = mCount;
+ for (int i = 0; i < N; i++) {
+ if (mResId[i] != t.mResId[i] || mForce[i] != t.mForce[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @return a shallow copy of this key
+ */
+ @Override
+ public ThemeKey clone() {
+ final ThemeKey other = new ThemeKey();
+ other.mResId = mResId;
+ other.mForce = mForce;
+ other.mCount = mCount;
+ return other;
+ }
}
/**
@@ -1944,8 +2048,8 @@
+ " final compat is " + mCompatibilityInfo);
}
- clearDrawableCachesLocked(mDrawableCache, configChanges);
- clearDrawableCachesLocked(mColorDrawableCache, configChanges);
+ mDrawableCache.onConfigurationChange(configChanges);
+ mColorDrawableCache.onConfigurationChange(configChanges);
mColorStateListCache.onConfigurationChange(configChanges);
mAnimatorCache.onConfigurationChange(configChanges);
mStateListAnimatorCache.onConfigurationChange(configChanges);
@@ -1983,48 +2087,6 @@
return configChanges;
}
- private void clearDrawableCachesLocked(
- ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
- int configChanges) {
- final int N = caches.size();
- for (int i = 0; i < N; i++) {
- clearDrawableCacheLocked(caches.valueAt(i), configChanges);
- }
- }
-
- private void clearDrawableCacheLocked(
- LongSparseArray<WeakReference<ConstantState>> cache, int configChanges) {
- if (DEBUG_CONFIG) {
- Log.d(TAG, "Cleaning up drawables config changes: 0x"
- + Integer.toHexString(configChanges));
- }
- final int N = cache.size();
- for (int i = 0; i < N; i++) {
- final WeakReference<ConstantState> ref = cache.valueAt(i);
- if (ref != null) {
- final ConstantState cs = ref.get();
- if (cs != null) {
- if (Configuration.needNewResources(
- configChanges, cs.getChangingConfigurations())) {
- if (DEBUG_CONFIG) {
- Log.d(TAG, "FLUSHING #0x"
- + Long.toHexString(cache.keyAt(i))
- + " / " + cs + " with changes: 0x"
- + Integer.toHexString(cs.getChangingConfigurations()));
- }
- cache.setValueAt(i, null);
- } else if (DEBUG_CONFIG) {
- Log.d(TAG, "(Keeping #0x"
- + Long.toHexString(cache.keyAt(i))
- + " / " + cs + " with changes: 0x"
- + Integer.toHexString(cs.getChangingConfigurations())
- + ")");
- }
- }
- }
- }
- }
-
/**
* {@code Locale.toLanguageTag} will transform the obsolete (and deprecated)
* language codes "in", "ji" and "iw" to "id", "yi" and "he" respectively.
@@ -2436,7 +2498,7 @@
}
final boolean isColorDrawable;
- final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches;
+ final DrawableCache caches;
final long key;
if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
&& value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
@@ -2452,7 +2514,7 @@
// First, check whether we have a cached version of this drawable
// that was inflated against the specified theme.
if (!mPreloading) {
- final Drawable cachedDrawable = getCachedDrawable(caches, key, theme);
+ final Drawable cachedDrawable = caches.getInstance(key, theme);
if (cachedDrawable != null) {
return cachedDrawable;
}
@@ -2479,13 +2541,8 @@
// Determine if the drawable has unresolved theme attributes. If it
// does, we'll need to apply a theme and store it in a theme-specific
// cache.
- final String cacheKey;
- if (!dr.canApplyTheme()) {
- cacheKey = CACHE_NOT_THEMED;
- } else if (theme == null) {
- cacheKey = CACHE_NULL_THEME;
- } else {
- cacheKey = theme.getKey();
+ final boolean canApplyTheme = dr.canApplyTheme();
+ if (canApplyTheme && theme != null) {
dr = dr.mutate();
dr.applyTheme(theme);
dr.clearMutated();
@@ -2495,15 +2552,14 @@
// cache: preload, not themed, null theme, or theme-specific.
if (dr != null) {
dr.setChangingConfigurations(value.changingConfigurations);
- cacheDrawable(value, isColorDrawable, caches, cacheKey, key, dr);
+ cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr);
}
return dr;
}
- private void cacheDrawable(TypedValue value, boolean isColorDrawable,
- ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
- String cacheKey, long key, Drawable dr) {
+ private void cacheDrawable(TypedValue value, boolean isColorDrawable, DrawableCache caches,
+ Theme theme, boolean usesTheme, long key, Drawable dr) {
final ConstantState cs = dr.getConstantState();
if (cs == null) {
return;
@@ -2531,54 +2587,12 @@
}
} else {
synchronized (mAccessLock) {
- LongSparseArray<WeakReference<ConstantState>> themedCache = caches.get(cacheKey);
- if (themedCache == null) {
- // Clean out the caches before we add more. This shouldn't
- // happen very often.
- pruneCaches(caches);
- themedCache = new LongSparseArray<>(1);
- caches.put(cacheKey, themedCache);
- }
- themedCache.put(key, new WeakReference<>(cs));
+ caches.put(key, theme, cs, usesTheme);
}
}
}
/**
- * Prunes empty caches from the cache map.
- *
- * @param caches The map of caches to prune.
- */
- private void pruneCaches(ArrayMap<String,
- LongSparseArray<WeakReference<ConstantState>>> caches) {
- final int N = caches.size();
- for (int i = N - 1; i >= 0; i--) {
- final LongSparseArray<WeakReference<ConstantState>> cache = caches.valueAt(i);
- if (pruneCache(cache)) {
- caches.removeAt(i);
- }
- }
- }
-
- /**
- * Prunes obsolete weak references from a cache, returning {@code true} if
- * the cache is empty and should be removed.
- *
- * @param cache The cache of weak references to prune.
- * @return {@code true} if the cache is empty and should be removed.
- */
- private boolean pruneCache(LongSparseArray<WeakReference<ConstantState>> cache) {
- final int N = cache.size();
- for (int i = N - 1; i >= 0; i--) {
- final WeakReference entry = cache.valueAt(i);
- if (entry == null || entry.get() == null) {
- cache.removeAt(i);
- }
- }
- return cache.size() == 0;
- }
-
- /**
* Loads a drawable from XML or resources stream.
*/
private Drawable loadDrawableForCookie(TypedValue value, int id, Theme theme) {
@@ -2631,51 +2645,6 @@
return dr;
}
- private Drawable getCachedDrawable(
- ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
- long key, Theme theme) {
- synchronized (mAccessLock) {
- // First search theme-agnostic cache.
- final Drawable unthemedDrawable = getCachedDrawableLocked(
- caches, key, CACHE_NOT_THEMED);
- if (unthemedDrawable != null) {
- return unthemedDrawable;
- }
-
- // Next search theme-specific cache.
- final String themeKey = theme != null ? theme.getKey() : CACHE_NULL_THEME;
- return getCachedDrawableLocked(caches, key, themeKey);
- }
- }
-
- private Drawable getCachedDrawableLocked(
- ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> caches,
- long key, String themeKey) {
- final LongSparseArray<WeakReference<ConstantState>> cache = caches.get(themeKey);
- if (cache != null) {
- final ConstantState entry = getConstantStateLocked(cache, key);
- if (entry != null) {
- return entry.newDrawable(this);
- }
- }
- return null;
- }
-
- private ConstantState getConstantStateLocked(
- LongSparseArray<WeakReference<ConstantState>> drawableCache, long key) {
- final WeakReference<ConstantState> wr = drawableCache.get(key);
- if (wr != null) {
- final ConstantState entry = wr.get();
- if (entry != null) {
- return entry;
- } else {
- // Our entry has been purged.
- drawableCache.delete(key);
- }
- }
- return null;
- }
-
@Nullable
ColorStateList loadColorStateList(TypedValue value, int id, Theme theme)
throws NotFoundException {
@@ -2713,8 +2682,7 @@
}
final ConfigurationBoundResourceCache<ColorStateList> cache = mColorStateListCache;
-
- csl = cache.get(key, theme);
+ csl = cache.getInstance(key, theme);
if (csl != null) {
return csl;
}
diff --git a/core/java/android/content/res/ThemedResourceCache.java b/core/java/android/content/res/ThemedResourceCache.java
new file mode 100644
index 0000000..9a2d06147
--- /dev/null
+++ b/core/java/android/content/res/ThemedResourceCache.java
@@ -0,0 +1,233 @@
+/*
+ * 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 android.content.res;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Resources.Theme;
+import android.content.res.Resources.ThemeKey;
+import android.util.LongSparseArray;
+import android.util.ArrayMap;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Data structure used for caching data against themes.
+ *
+ * @param <T> type of data to cache
+ */
+abstract class ThemedResourceCache<T> {
+ private ArrayMap<ThemeKey, LongSparseArray<WeakReference<T>>> mThemedEntries;
+ private LongSparseArray<WeakReference<T>> mUnthemedEntries;
+ private LongSparseArray<WeakReference<T>> mNullThemedEntries;
+
+ /**
+ * Adds a new theme-dependent entry to the cache.
+ *
+ * @param key a key that uniquely identifies the entry
+ * @param theme the theme against which this entry was inflated, or
+ * {@code null} if the entry has no theme applied
+ * @param entry the entry to cache
+ */
+ public void put(long key, @Nullable Theme theme, @NonNull T entry) {
+ put(key, theme, entry, true);
+ }
+
+ /**
+ * Adds a new entry to the cache.
+ *
+ * @param key a key that uniquely identifies the entry
+ * @param theme the theme against which this entry was inflated, or
+ * {@code null} if the entry has no theme applied
+ * @param entry the entry to cache
+ * @param usesTheme {@code true} if the entry is affected theme changes,
+ * {@code false} otherwise
+ */
+ public void put(long key, @Nullable Theme theme, @NonNull T entry, boolean usesTheme) {
+ if (entry == null) {
+ return;
+ }
+
+ synchronized (this) {
+ final LongSparseArray<WeakReference<T>> entries;
+ if (!usesTheme) {
+ entries = getUnthemedLocked(true);
+ } else {
+ entries = getThemedLocked(theme, true);
+ }
+ if (entries != null) {
+ entries.put(key, new WeakReference<>(entry));
+ }
+ }
+ }
+
+ /**
+ * Returns an entry from the cache.
+ *
+ * @param key a key that uniquely identifies the entry
+ * @param theme the theme where the entry will be used
+ * @return a cached entry, or {@code null} if not in the cache
+ */
+ @Nullable
+ public T get(long key, @Nullable Theme theme) {
+ // The themed (includes null-themed) and unthemed caches are mutually
+ // exclusive, so we'll give priority to whichever one we think we'll
+ // hit first. Since most of the framework drawables are themed, that's
+ // probably going to be the themed cache.
+ synchronized (this) {
+ final LongSparseArray<WeakReference<T>> themedEntries = getThemedLocked(theme, false);
+ if (themedEntries != null) {
+ final WeakReference<T> themedEntry = themedEntries.get(key);
+ if (themedEntry != null) {
+ return themedEntry.get();
+ }
+ }
+
+ final LongSparseArray<WeakReference<T>> unthemedEntries = getUnthemedLocked(false);
+ if (unthemedEntries != null) {
+ final WeakReference<T> unthemedEntry = unthemedEntries.get(key);
+ if (unthemedEntry != null) {
+ return unthemedEntry.get();
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Prunes cache entries that have been invalidated by a configuration
+ * change.
+ *
+ * @param configChanges a bitmask of configuration changes
+ */
+ public void onConfigurationChange(int configChanges) {
+ prune(configChanges);
+ }
+
+ /**
+ * Returns whether a cached entry has been invalidated by a configuration
+ * change.
+ *
+ * @param entry a cached entry
+ * @param configChanges a non-zero bitmask of configuration changes
+ * @return {@code true} if the entry is invalid, {@code false} otherwise
+ */
+ protected abstract boolean shouldInvalidateEntry(@NonNull T entry, int configChanges);
+
+ /**
+ * Returns the cached data for the specified theme, optionally creating a
+ * new entry if one does not already exist.
+ *
+ * @param t the theme for which to return cached data
+ * @param create {@code true} to create an entry if one does not already
+ * exist, {@code false} otherwise
+ * @return the cached data for the theme, or {@code null} if the cache is
+ * empty and {@code create} was {@code false}
+ */
+ @Nullable
+ private LongSparseArray<WeakReference<T>> getThemedLocked(@Nullable Theme t, boolean create) {
+ if (t == null) {
+ if (mNullThemedEntries == null && create) {
+ mNullThemedEntries = new LongSparseArray<>(1);
+ }
+ return mNullThemedEntries;
+ }
+
+ if (mThemedEntries == null) {
+ if (create) {
+ mThemedEntries = new ArrayMap<>(1);
+ } else {
+ return null;
+ }
+ }
+
+ final ThemeKey key = t.getKey();
+ LongSparseArray<WeakReference<T>> cache = mThemedEntries.get(key);
+ if (cache == null && create) {
+ cache = new LongSparseArray<>(1);
+
+ final ThemeKey keyClone = key.clone();
+ mThemedEntries.put(keyClone, cache);
+ }
+
+ return cache;
+ }
+
+ /**
+ * Returns the theme-agnostic cached data.
+ *
+ * @param create {@code true} to create an entry if one does not already
+ * exist, {@code false} otherwise
+ * @return the theme-agnostic cached data, or {@code null} if the cache is
+ * empty and {@code create} was {@code false}
+ */
+ @Nullable
+ private LongSparseArray<WeakReference<T>> getUnthemedLocked(boolean create) {
+ if (mUnthemedEntries == null && create) {
+ mUnthemedEntries = new LongSparseArray<>(1);
+ }
+ return mUnthemedEntries;
+ }
+
+ /**
+ * Prunes cache entries affected by configuration changes or where weak
+ * references have expired.
+ *
+ * @param configChanges a bitmask of configuration changes, or {@code 0} to
+ * simply prune missing weak references
+ * @return {@code true} if the cache is completely empty after pruning
+ */
+ private boolean prune(int configChanges) {
+ synchronized (this) {
+ if (mThemedEntries != null) {
+ for (int i = mThemedEntries.size() - 1; i >= 0; i--) {
+ if (pruneEntriesLocked(mThemedEntries.valueAt(i), configChanges)) {
+ mThemedEntries.removeAt(i);
+ }
+ }
+ }
+
+ pruneEntriesLocked(mNullThemedEntries, configChanges);
+ pruneEntriesLocked(mUnthemedEntries, configChanges);
+
+ return mThemedEntries == null && mNullThemedEntries == null
+ && mUnthemedEntries == null;
+ }
+ }
+
+ private boolean pruneEntriesLocked(@Nullable LongSparseArray<WeakReference<T>> entries,
+ int configChanges) {
+ if (entries == null) {
+ return true;
+ }
+
+ for (int i = entries.size() - 1; i >= 0; i--) {
+ final WeakReference<T> ref = entries.valueAt(i);
+ if (ref == null || pruneEntryLocked(ref.get(), configChanges)) {
+ entries.removeAt(i);
+ }
+ }
+
+ return entries.size() == 0;
+ }
+
+ private boolean pruneEntryLocked(@Nullable T entry, int configChanges) {
+ return entry == null || (configChanges != 0
+ && shouldInvalidateEntry(entry, configChanges));
+ }
+}
diff --git a/core/java/android/ddm/DdmHandleViewDebug.java b/core/java/android/ddm/DdmHandleViewDebug.java
index 3a36b0a..be48633 100644
--- a/core/java/android/ddm/DdmHandleViewDebug.java
+++ b/core/java/android/ddm/DdmHandleViewDebug.java
@@ -229,15 +229,25 @@
private Chunk dumpHierarchy(View rootView, ByteBuffer in) {
boolean skipChildren = in.getInt() > 0;
boolean includeProperties = in.getInt() > 0;
+ boolean v2 = in.hasRemaining() && in.getInt() > 0;
- ByteArrayOutputStream b = new ByteArrayOutputStream(1024);
+ long start = System.currentTimeMillis();
+
+ ByteArrayOutputStream b = new ByteArrayOutputStream(2*1024*1024);
try {
- ViewDebug.dump(rootView, skipChildren, includeProperties, b);
- } catch (IOException e) {
+ if (v2) {
+ ViewDebug.dumpv2(rootView, b);
+ } else {
+ ViewDebug.dump(rootView, skipChildren, includeProperties, b);
+ }
+ } catch (IOException | InterruptedException e) {
return createFailChunk(1, "Unexpected error while obtaining view hierarchy: "
+ e.getMessage());
}
+ long end = System.currentTimeMillis();
+ Log.d(TAG, "Time to obtain view hierarchy (ms): " + (end - start));
+
byte[] data = b.toByteArray();
return new Chunk(CHUNK_VURT, data, 0, data.length);
}
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index aeddf03..ef71c42 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -212,8 +212,7 @@
* <p>All capture sessions can be used for capturing images from the camera but only capture
* sessions created by
* {@link CameraDevice#createReprocessibleCaptureSession createReprocessibleCaptureSession}
- * can submit reprocess capture requests. The list of requests must all be capturing images from
- * the camera or all be reprocess capture requests. Submitting a reprocess request to a regular
+ * can submit reprocess capture requests. Submitting a reprocess request to a regular
* capture session will result in an {@link IllegalArgumentException}.</p>
*
* @param requests the list of settings for this burst capture
@@ -236,9 +235,7 @@
* @throws IllegalArgumentException If the requests target no Surfaces, or the requests target
* Surfaces not currently configured as outputs; or a reprocess
* capture request is submitted in a non-reprocessible capture
- * session; or the list of requests contains both requests to
- * capture images from the camera and reprocess capture
- * requests; or one of the reprocess capture requests was
+ * session; or one of the reprocess capture requests was
* created with a {@link TotalCaptureResult} from a different
* session; or one of the captures targets a Surface in the
* middle of being {@link #prepare prepared}; or if the handler
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index 3c19529..dff6227 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -177,26 +177,20 @@
public synchronized int captureBurst(List<CaptureRequest> requests, CaptureCallback callback,
Handler handler) throws CameraAccessException {
if (requests == null) {
- throw new IllegalArgumentException("requests must not be null");
+ throw new IllegalArgumentException("Requests must not be null");
} else if (requests.isEmpty()) {
- throw new IllegalArgumentException("requests must have at least one element");
+ throw new IllegalArgumentException("Requests must have at least one element");
}
- boolean reprocess = requests.get(0).isReprocess();
- if (reprocess && !isReprocessible()) {
- throw new IllegalArgumentException("this capture session cannot handle reprocess " +
- "requests");
- } else if (reprocess && requests.get(0).getReprocessibleSessionId() != mId) {
- throw new IllegalArgumentException("capture request was created for another session");
- }
-
- for (int i = 1; i < requests.size(); i++) {
- if (requests.get(i).isReprocess() != reprocess) {
- throw new IllegalArgumentException("cannot mix regular and reprocess capture " +
- " requests");
- } else if (reprocess && requests.get(i).getReprocessibleSessionId() != mId) {
- throw new IllegalArgumentException("capture request was created for another " +
- "session");
+ for (CaptureRequest request : requests) {
+ if (request.isReprocess()) {
+ if (!isReprocessible()) {
+ throw new IllegalArgumentException("This capture session cannot handle " +
+ "reprocess requests");
+ } else if (request.getReprocessibleSessionId() != mId) {
+ throw new IllegalArgumentException("Capture request was created for another " +
+ "session");
+ }
}
}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index ff4ad79..e84b46a 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -94,11 +94,11 @@
private final int mTotalPartialCount;
/**
- * A list tracking request and its expected last frame.
- * Updated when calling ICameraDeviceUser methods.
+ * A list tracking request and its expected last regular frame number and last reprocess frame
+ * number. Updated when calling ICameraDeviceUser methods.
*/
- private final List<SimpleEntry</*frameNumber*/Long, /*requestId*/Integer>>
- mFrameNumberRequestPairs = new ArrayList<SimpleEntry<Long, Integer>>();
+ private final List<RequestLastFrameNumbersHolder> mRequestLastFrameNumbersList =
+ new ArrayList<>();
/**
* An object tracking received frame numbers.
@@ -653,8 +653,8 @@
*
* <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never
* sent to HAL. Then onCaptureSequenceAborted is immediately triggered.
- * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber pair
- * is added to the list mFrameNumberRequestPairs.</p>
+ * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber as the last
+ * regular frame number will be added to the list mRequestLastFrameNumbersList.</p>
*
* @param requestId the request ID of the current repeating request.
*
@@ -693,10 +693,6 @@
"early trigger sequence complete for request %d",
requestId));
}
- if (lastFrameNumber < Integer.MIN_VALUE
- || lastFrameNumber > Integer.MAX_VALUE) {
- throw new AssertionError(lastFrameNumber + " cannot be cast to int");
- }
holder.getCallback().onCaptureSequenceAborted(
CameraDeviceImpl.this,
requestId);
@@ -710,9 +706,11 @@
requestId));
}
} else {
- mFrameNumberRequestPairs.add(
- new SimpleEntry<Long, Integer>(lastFrameNumber,
- requestId));
+ // This function is only called for regular request so lastFrameNumber is the last
+ // regular frame number.
+ mRequestLastFrameNumbersList.add(new RequestLastFrameNumbersHolder(requestId,
+ lastFrameNumber));
+
// It is possible that the last frame has already arrived, so we need to check
// for sequence completion right away
checkAndFireSequenceComplete();
@@ -779,8 +777,8 @@
}
mRepeatingRequestId = requestId;
} else {
- mFrameNumberRequestPairs.add(
- new SimpleEntry<Long, Integer>(lastFrameNumber, requestId));
+ mRequestLastFrameNumbersList.add(new RequestLastFrameNumbersHolder(requestList,
+ requestId, lastFrameNumber));
}
if (mIdle) {
@@ -1146,7 +1144,101 @@
public int getSessionId() {
return mSessionId;
}
+ }
+ /**
+ * This class holds a capture ID and its expected last regular frame number and last reprocess
+ * frame number.
+ */
+ static class RequestLastFrameNumbersHolder {
+ // request ID
+ private final int mRequestId;
+ // The last regular frame number for this request ID. It's
+ // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no regular request.
+ private final long mLastRegularFrameNumber;
+ // The last reprocess frame number for this request ID. It's
+ // CaptureCallback.NO_FRAMES_CAPTURED if the request ID has no reprocess request.
+ private final long mLastReprocessFrameNumber;
+
+ /**
+ * Create a request-last-frame-numbers holder with a list of requests, request ID, and
+ * the last frame number returned by camera service.
+ */
+ public RequestLastFrameNumbersHolder(List<CaptureRequest> requestList, int requestId,
+ long lastFrameNumber) {
+ long lastRegularFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
+ long lastReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
+ long frameNumber = lastFrameNumber;
+
+ if (lastFrameNumber < requestList.size() - 1) {
+ throw new IllegalArgumentException("lastFrameNumber: " + lastFrameNumber +
+ " should be at least " + (requestList.size() - 1) + " for the number of " +
+ " requests in the list: " + requestList.size());
+ }
+
+ // find the last regular frame number and the last reprocess frame number
+ for (int i = requestList.size() - 1; i >= 0; i--) {
+ CaptureRequest request = requestList.get(i);
+ if (request.isReprocess() && lastReprocessFrameNumber ==
+ CaptureCallback.NO_FRAMES_CAPTURED) {
+ lastReprocessFrameNumber = frameNumber;
+ } else if (!request.isReprocess() && lastRegularFrameNumber ==
+ CaptureCallback.NO_FRAMES_CAPTURED) {
+ lastRegularFrameNumber = frameNumber;
+ }
+
+ if (lastReprocessFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED &&
+ lastRegularFrameNumber != CaptureCallback.NO_FRAMES_CAPTURED) {
+ break;
+ }
+
+ frameNumber--;
+ }
+
+ mLastRegularFrameNumber = lastRegularFrameNumber;
+ mLastReprocessFrameNumber = lastReprocessFrameNumber;
+ mRequestId = requestId;
+ }
+
+ /**
+ * Create a request-last-frame-numbers holder with a request ID and last regular frame
+ * number.
+ */
+ public RequestLastFrameNumbersHolder(int requestId, long lastRegularFrameNumber) {
+ mLastRegularFrameNumber = lastRegularFrameNumber;
+ mLastReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
+ mRequestId = requestId;
+ }
+
+ /**
+ * Return the last regular frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if
+ * it contains no regular request.
+ */
+ public long getLastRegularFrameNumber() {
+ return mLastRegularFrameNumber;
+ }
+
+ /**
+ * Return the last reprocess frame number. Return CaptureCallback.NO_FRAMES_CAPTURED if
+ * it contains no reprocess request.
+ */
+ public long getLastReprocessFrameNumber() {
+ return mLastReprocessFrameNumber;
+ }
+
+ /**
+ * Return the last frame number overall.
+ */
+ public long getLastFrameNumber() {
+ return Math.max(mLastRegularFrameNumber, mLastReprocessFrameNumber);
+ }
+
+ /**
+ * Return the request ID.
+ */
+ public int getRequestId() {
+ return mRequestId;
+ }
}
/**
@@ -1154,8 +1246,8 @@
*/
public class FrameNumberTracker {
- private long mCompletedFrameNumber = -1;
- private long mCompletedReprocessFrameNumber = -1;
+ private long mCompletedFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
+ private long mCompletedReprocessFrameNumber = CaptureCallback.NO_FRAMES_CAPTURED;
/** the skipped frame numbers that belong to regular results */
private final LinkedList<Long> mSkippedRegularFrameNumbers = new LinkedList<Long>();
/** the skipped frame numbers that belong to reprocess results */
@@ -1360,11 +1452,11 @@
long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
long completedReprocessFrameNumber = mFrameNumberTracker.getCompletedReprocessFrameNumber();
boolean isReprocess = false;
- Iterator<SimpleEntry<Long, Integer> > iter = mFrameNumberRequestPairs.iterator();
+ Iterator<RequestLastFrameNumbersHolder> iter = mRequestLastFrameNumbersList.iterator();
while (iter.hasNext()) {
- final SimpleEntry<Long, Integer> frameNumberRequestPair = iter.next();
+ final RequestLastFrameNumbersHolder requestLastFrameNumbers = iter.next();
boolean sequenceCompleted = false;
- final int requestId = frameNumberRequestPair.getValue();
+ final int requestId = requestLastFrameNumbers.getRequestId();
final CaptureCallbackHolder holder;
synchronized(mInterfaceLock) {
if (mRemoteDevice == null) {
@@ -1376,19 +1468,22 @@
holder = (index >= 0) ?
mCaptureCallbackMap.valueAt(index) : null;
if (holder != null) {
- isReprocess = holder.getRequest().isReprocess();
+ long lastRegularFrameNumber =
+ requestLastFrameNumbers.getLastRegularFrameNumber();
+ long lastReprocessFrameNumber =
+ requestLastFrameNumbers.getLastReprocessFrameNumber();
+
// check if it's okay to remove request from mCaptureCallbackMap
- if ((isReprocess && frameNumberRequestPair.getKey() <=
- completedReprocessFrameNumber) || (!isReprocess &&
- frameNumberRequestPair.getKey() <= completedFrameNumber)) {
+ if (lastRegularFrameNumber <= completedFrameNumber &&
+ lastReprocessFrameNumber <= completedReprocessFrameNumber) {
sequenceCompleted = true;
mCaptureCallbackMap.removeAt(index);
if (DEBUG) {
Log.v(TAG, String.format(
- "remove holder for requestId %d, "
- + "because lastFrame %d is <= %d",
- requestId, frameNumberRequestPair.getKey(),
- completedFrameNumber));
+ "Remove holder for requestId %d, because lastRegularFrame %d " +
+ "is <= %d and lastReprocessFrame %d is <= %d", requestId,
+ lastRegularFrameNumber, completedFrameNumber,
+ lastReprocessFrameNumber, completedReprocessFrameNumber));
}
}
}
@@ -1412,16 +1507,10 @@
requestId));
}
- long lastFrameNumber = frameNumberRequestPair.getKey();
- if (lastFrameNumber < Integer.MIN_VALUE
- || lastFrameNumber > Integer.MAX_VALUE) {
- throw new AssertionError(lastFrameNumber
- + " cannot be cast to int");
- }
holder.getCallback().onCaptureSequenceCompleted(
CameraDeviceImpl.this,
requestId,
- lastFrameNumber);
+ requestLastFrameNumbers.getLastFrameNumber());
}
}
};
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 36fc4f9..50eed3e 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -173,6 +173,27 @@
"ro.build.version.sdk", 0);
/**
+ * The developer preview revision of a prerelease SDK. This value will always
+ * be <code>0</code> on production platform builds/devices.
+ *
+ * <p>When this value is nonzero, any new API added since the last
+ * officially published {@link #SDK_INT API level} is only guaranteed to be present
+ * on that specific preview revision. For example, an API <code>Activity.fooBar()</code>
+ * might be present in preview revision 1 but renamed or removed entirely in
+ * preview revision 2, which may cause an app attempting to call it to crash
+ * at runtime.</p>
+ *
+ * <p>Experimental apps targeting preview APIs should check this value for
+ * equality (<code>==</code>) with the preview SDK revision they were built for
+ * before using any prerelease platform APIs. Apps that detect a preview SDK revision
+ * other than the specific one they expect should fall back to using APIs from
+ * the previously published API level only to avoid unwanted runtime exceptions.
+ * </p>
+ */
+ public static final int PREVIEW_SDK_INT = SystemProperties.getInt(
+ "ro.build.version.preview_sdk", 0);
+
+ /**
* The current development codename, or the string "REL" if this is
* a release build.
*/
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 293cf6f..7c3c11b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -324,23 +324,7 @@
"android.settings.BLUETOOTH_SETTINGS";
/**
- * Activity Action: Show settings to allow configuration of Wifi Displays.
- * <p>
- * In some cases, a matching Activity may not exist, so ensure you
- * safeguard against this.
- * <p>
- * Input: Nothing.
- * <p>
- * Output: Nothing.
- * @hide
- */
- @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_WIFI_DISPLAY_SETTINGS =
- "android.settings.WIFI_DISPLAY_SETTINGS";
-
- /**
- * Activity Action: Show settings to allow configuration of
- * {@link android.media.routing.MediaRouteService media route providers}.
+ * Activity Action: Show settings to allow configuration of cast endpoints.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
* safeguard against this.
@@ -5951,6 +5935,12 @@
"wireless_charging_started_sound";
/**
+ * Whether to play a sound for charging events.
+ * @hide
+ */
+ public static final String CHARGING_SOUNDS_ENABLED = "charging_sounds_enabled";
+
+ /**
* Whether we keep the device on while the device is plugged in.
* Supported values are:
* <ul>
@@ -6166,6 +6156,17 @@
*/
public static final String MOBILE_DATA = "mobile_data";
+ /**
+ * Whether the mobile data connection should remain active even when higher
+ * priority networks like WiFi are active, to help make network switching faster.
+ *
+ * See ConnectivityService for more info.
+ *
+ * (0 = disabled, 1 = enabled)
+ * @hide
+ */
+ public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on";
+
/** {@hide} */
public static final String NETSTATS_ENABLED = "netstats_enabled";
/** {@hide} */
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 29aaf30..0171869 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -40,7 +40,7 @@
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
-import android.view.PhoneWindow;
+import com.android.internal.policy.PhoneWindow;
import android.view.SearchEvent;
import android.view.View;
import android.view.ViewGroup;
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index cf29310..3eeb04a 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -1481,14 +1481,8 @@
// interface).
// Sanitize locale using isLanguageAvailable.
- int result = service.isLanguageAvailable( language, country, variant);
- if (result >= LANG_AVAILABLE){
- if (result < LANG_COUNTRY_VAR_AVAILABLE) {
- variant = "";
- if (result < LANG_COUNTRY_AVAILABLE) {
- country = "";
- }
- }
+ int result = service.isLanguageAvailable(language, country, variant);
+ if (result >= LANG_AVAILABLE) {
// Get the default voice for the locale.
String voiceName = service.getDefaultVoiceNameFor(language, country, variant);
if (TextUtils.isEmpty(voiceName)) {
@@ -1502,10 +1496,28 @@
return LANG_NOT_SUPPORTED;
}
+ // Set the language/country/variant of the voice, so #getLanguage will return
+ // the currently set voice locale when called.
+ Voice voice = getVoice(service, voiceName);
+ String voiceLanguage = "";
+ try {
+ voiceLanguage = voice.getLocale().getISO3Language();
+ } catch (MissingResourceException e) {
+ Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " +
+ voice.getLocale(), e);
+ }
+
+ String voiceCountry = "";
+ try {
+ voiceCountry = voice.getLocale().getISO3Country();
+ } catch (MissingResourceException e) {
+ Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " +
+ voice.getLocale(), e);
+ }
mParams.putString(Engine.KEY_PARAM_VOICE_NAME, voiceName);
- mParams.putString(Engine.KEY_PARAM_LANGUAGE, language);
- mParams.putString(Engine.KEY_PARAM_COUNTRY, country);
- mParams.putString(Engine.KEY_PARAM_VARIANT, variant);
+ mParams.putString(Engine.KEY_PARAM_LANGUAGE, voiceLanguage);
+ mParams.putString(Engine.KEY_PARAM_COUNTRY, voiceCountry);
+ mParams.putString(Engine.KEY_PARAM_VARIANT, voice.getLocale().getVariant());
}
return result;
}
@@ -1654,20 +1666,32 @@
if (TextUtils.isEmpty(voiceName)) {
return null;
}
- List<Voice> voices = service.getVoices();
- if (voices == null) {
- return null;
- }
- for (Voice voice : voices) {
- if (voice.getName().equals(voiceName)) {
- return voice;
- }
- }
- return null;
+ return getVoice(service, voiceName);
}
}, null, "getVoice");
}
+
+ /**
+ * Returns a Voice instance of the voice with the given voice name.
+ *
+ * @return Voice instance with the given voice name, or {@code null} if not set or on error.
+ *
+ * @see Voice
+ */
+ private Voice getVoice(ITextToSpeechService service, String voiceName) throws RemoteException {
+ List<Voice> voices = service.getVoices();
+ if (voices == null) {
+ return null;
+ }
+ for (Voice voice : voices) {
+ if (voice.getName().equals(voiceName)) {
+ return voice;
+ }
+ }
+ return null;
+ }
+
/**
* Returns a Voice instance that's the default voice for the default Text-to-speech language.
* @return The default voice instance for the default language, or {@code null} if not set or
@@ -1690,14 +1714,7 @@
// Sanitize the locale using isLanguageAvailable.
int result = service.isLanguageAvailable(language, country, variant);
- if (result >= LANG_AVAILABLE){
- if (result < LANG_COUNTRY_VAR_AVAILABLE) {
- variant = "";
- if (result < LANG_COUNTRY_AVAILABLE) {
- country = "";
- }
- }
- } else {
+ if (result < LANG_AVAILABLE) {
// The default language is not supported.
return null;
}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 451abea..59c7c6d 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -91,6 +91,7 @@
b.mEllipsizedWidth = width;
b.mEllipsize = null;
b.mMaxLines = Integer.MAX_VALUE;
+ b.mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
b.mMeasuredText = MeasuredText.obtain();
return b;
@@ -100,6 +101,8 @@
b.mPaint = null;
b.mText = null;
MeasuredText.recycle(b.mMeasuredText);
+ b.mMeasuredText = null;
+ nFinishBuilder(b.mNativePtr);
sPool.release(b);
}
diff --git a/core/java/android/text/method/WordIterator.java b/core/java/android/text/method/WordIterator.java
index 11226a9..c4dc5ed 100644
--- a/core/java/android/text/method/WordIterator.java
+++ b/core/java/android/text/method/WordIterator.java
@@ -95,6 +95,45 @@
} while (true);
}
+ /** {@inheritDoc} */
+ public boolean isBoundary(int offset) {
+ int shiftedOffset = offset - mOffsetShift;
+ checkOffsetIsValid(shiftedOffset);
+ return mIterator.isBoundary(shiftedOffset);
+ }
+
+ /**
+ * Returns the position of next boundary after the given offset. Returns
+ * {@code DONE} if there is no boundary after the given offset.
+ *
+ * @param offset the given start position to search from.
+ * @return the position of the last boundary preceding the given offset.
+ */
+ public int nextBoundary(int offset) {
+ int shiftedOffset = offset - mOffsetShift;
+ shiftedOffset = mIterator.following(shiftedOffset);
+ if (shiftedOffset == BreakIterator.DONE) {
+ return BreakIterator.DONE;
+ }
+ return shiftedOffset + mOffsetShift;
+ }
+
+ /**
+ * Returns the position of boundary preceding the given offset or
+ * {@code DONE} if the given offset specifies the starting position.
+ *
+ * @param offset the given start position to search from.
+ * @return the position of the last boundary preceding the given offset.
+ */
+ public int prevBoundary(int offset) {
+ int shiftedOffset = offset - mOffsetShift;
+ shiftedOffset = mIterator.preceding(shiftedOffset);
+ if (shiftedOffset == BreakIterator.DONE) {
+ return BreakIterator.DONE;
+ }
+ return shiftedOffset + mOffsetShift;
+ }
+
/** If <code>offset</code> is within a word, returns the index of the first character of that
* word, otherwise returns BreakIterator.DONE.
*
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index ebc2aac..1b25505 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -1639,6 +1639,7 @@
for (int i = 0; i < count; i++) {
TransitionValues values = lookIn.get(i);
if (values == null) {
+ // Null values are always added to the end of the list, so we know to stop now.
return null;
}
if (values.view == view) {
@@ -1742,6 +1743,9 @@
View oldView = oldInfo.view;
TransitionValues startValues = getTransitionValues(oldView, true);
TransitionValues endValues = getMatchedTransitionValues(oldView, true);
+ if (startValues == null && endValues == null) {
+ endValues = mEndValues.viewValues.get(oldView);
+ }
boolean cancel = (startValues != null || endValues != null) &&
oldInfo.transition.areValuesChanged(oldValues, endValues);
if (cancel) {
diff --git a/core/java/android/util/FloatMath.java b/core/java/android/util/FloatMath.java
index 8f488af..bb7d15f 100644
--- a/core/java/android/util/FloatMath.java
+++ b/core/java/android/util/FloatMath.java
@@ -25,6 +25,8 @@
* {@link java.lang.Math}. {@link java.lang.Math} should be used in
* preference.
*
+ * <p>All methods were removed from the public API in version 23.
+ *
* @deprecated Use {@link java.lang.Math} instead.
*/
@Deprecated
@@ -39,6 +41,7 @@
*
* @param value to be converted
* @return the floor of value
+ * @removed
*/
public static float floor(float value) {
return (float) Math.floor(value);
@@ -50,6 +53,7 @@
*
* @param value to be converted
* @return the ceiling of value
+ * @removed
*/
public static float ceil(float value) {
return (float) Math.ceil(value);
@@ -60,6 +64,7 @@
*
* @param angle to compute the cosine of, in radians
* @return the sine of angle
+ * @removed
*/
public static float sin(float angle) {
return (float) Math.sin(angle);
@@ -70,6 +75,7 @@
*
* @param angle to compute the cosine of, in radians
* @return the cosine of angle
+ * @removed
*/
public static float cos(float angle) {
return (float) Math.cos(angle);
@@ -81,6 +87,7 @@
*
* @param value to compute sqrt of
* @return the square root of value
+ * @removed
*/
public static float sqrt(float value) {
return (float) Math.sqrt(value);
@@ -92,6 +99,7 @@
*
* @param value to compute the exponential of
* @return the exponential of value
+ * @removed
*/
public static float exp(float value) {
return (float) Math.exp(value);
@@ -104,6 +112,7 @@
* @param x the base of the operation.
* @param y the exponent of the operation.
* @return {@code x} to the power of {@code y}.
+ * @removed
*/
public static float pow(float x, float y) {
return (float) Math.pow(x, y);
@@ -116,6 +125,7 @@
* @param x a float number
* @param y a float number
* @return the hypotenuse
+ * @removed
*/
public static float hypot(float x, float y) {
return (float) Math.hypot(x, y);
diff --git a/core/java/android/view/GhostView.java b/core/java/android/view/GhostView.java
index bc38e1a..41502b6 100644
--- a/core/java/android/view/GhostView.java
+++ b/core/java/android/view/GhostView.java
@@ -40,8 +40,7 @@
mView.mGhostView = this;
final ViewGroup parent = (ViewGroup) mView.getParent();
setGhostedVisibility(View.INVISIBLE);
- parent.mRecreateDisplayList = true;
- parent.updateDisplayListIfDirty();
+ parent.invalidate();
}
@Override
@@ -83,8 +82,7 @@
mView.mGhostView = null;
final ViewGroup parent = (ViewGroup) mView.getParent();
if (parent != null) {
- parent.mRecreateDisplayList = true;
- parent.updateDisplayListIfDirty();
+ parent.invalidate();
}
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ee6bbe1..e1f1816 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -22359,4 +22359,138 @@
final String output = bits + " " + name;
found.put(key, output);
}
+
+ /** {@hide} */
+ public void encode(@NonNull ViewHierarchyEncoder stream) {
+ stream.beginObject(this);
+ encodeProperties(stream);
+ stream.endObject();
+ }
+
+ /** {@hide} */
+ @CallSuper
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) {
+ Object resolveId = ViewDebug.resolveId(getContext(), mID);
+ if (resolveId instanceof String) {
+ stream.addProperty("id", (String) resolveId);
+ } else {
+ stream.addProperty("id", mID);
+ }
+
+ stream.addProperty("misc:transformation.alpha",
+ mTransformationInfo != null ? mTransformationInfo.mAlpha : 0);
+ stream.addProperty("misc:transitionName", getTransitionName());
+
+ // layout
+ stream.addProperty("layout:left", mLeft);
+ stream.addProperty("layout:right", mRight);
+ stream.addProperty("layout:top", mTop);
+ stream.addProperty("layout:bottom", mBottom);
+ stream.addProperty("layout:width", getWidth());
+ stream.addProperty("layout:height", getHeight());
+ stream.addProperty("layout:layoutDirection", getLayoutDirection());
+ stream.addProperty("layout:layoutRtl", isLayoutRtl());
+ stream.addProperty("layout:hasTransientState", hasTransientState());
+ stream.addProperty("layout:baseline", getBaseline());
+
+ // layout params
+ ViewGroup.LayoutParams layoutParams = getLayoutParams();
+ if (layoutParams != null) {
+ stream.addPropertyKey("layoutParams");
+ layoutParams.encode(stream);
+ }
+
+ // scrolling
+ stream.addProperty("scrolling:scrollX", mScrollX);
+ stream.addProperty("scrolling:scrollY", mScrollY);
+
+ // padding
+ stream.addProperty("padding:paddingLeft", mPaddingLeft);
+ stream.addProperty("padding:paddingRight", mPaddingRight);
+ stream.addProperty("padding:paddingTop", mPaddingTop);
+ stream.addProperty("padding:paddingBottom", mPaddingBottom);
+ stream.addProperty("padding:userPaddingRight", mUserPaddingRight);
+ stream.addProperty("padding:userPaddingLeft", mUserPaddingLeft);
+ stream.addProperty("padding:userPaddingBottom", mUserPaddingBottom);
+ stream.addProperty("padding:userPaddingStart", mUserPaddingStart);
+ stream.addProperty("padding:userPaddingEnd", mUserPaddingEnd);
+
+ // measurement
+ stream.addProperty("measurement:minHeight", mMinHeight);
+ stream.addProperty("measurement:minWidth", mMinWidth);
+ stream.addProperty("measurement:measuredWidth", mMeasuredWidth);
+ stream.addProperty("measurement:measuredHeight", mMeasuredHeight);
+
+ // drawing
+ stream.addProperty("drawing:elevation", getElevation());
+ stream.addProperty("drawing:translationX", getTranslationX());
+ stream.addProperty("drawing:translationY", getTranslationY());
+ stream.addProperty("drawing:translationZ", getTranslationZ());
+ stream.addProperty("drawing:rotation", getRotation());
+ stream.addProperty("drawing:rotationX", getRotationX());
+ stream.addProperty("drawing:rotationY", getRotationY());
+ stream.addProperty("drawing:scaleX", getScaleX());
+ stream.addProperty("drawing:scaleY", getScaleY());
+ stream.addProperty("drawing:pivotX", getPivotX());
+ stream.addProperty("drawing:pivotY", getPivotY());
+ stream.addProperty("drawing:opaque", isOpaque());
+ stream.addProperty("drawing:alpha", getAlpha());
+ stream.addProperty("drawing:transitionAlpha", getTransitionAlpha());
+ stream.addProperty("drawing:shadow", hasShadow());
+ stream.addProperty("drawing:solidColor", getSolidColor());
+ stream.addProperty("drawing:layerType", mLayerType);
+ stream.addProperty("drawing:willNotDraw", willNotDraw());
+ stream.addProperty("drawing:hardwareAccelerated", isHardwareAccelerated());
+ stream.addProperty("drawing:willNotCacheDrawing", willNotCacheDrawing());
+ stream.addProperty("drawing:drawingCacheEnabled", isDrawingCacheEnabled());
+ stream.addProperty("drawing:overlappingRendering", hasOverlappingRendering());
+
+ // focus
+ stream.addProperty("focus:hasFocus", hasFocus());
+ stream.addProperty("focus:isFocused", isFocused());
+ stream.addProperty("focus:isFocusable", isFocusable());
+ stream.addProperty("focus:isFocusableInTouchMode", isFocusableInTouchMode());
+
+ stream.addProperty("misc:clickable", isClickable());
+ stream.addProperty("misc:pressed", isPressed());
+ stream.addProperty("misc:selected", isSelected());
+ stream.addProperty("misc:touchMode", isInTouchMode());
+ stream.addProperty("misc:hovered", isHovered());
+ stream.addProperty("misc:activated", isActivated());
+
+ stream.addProperty("misc:visibility", getVisibility());
+ stream.addProperty("misc:fitsSystemWindows", getFitsSystemWindows());
+ stream.addProperty("misc:filterTouchesWhenObscured", getFilterTouchesWhenObscured());
+
+ stream.addProperty("misc:enabled", isEnabled());
+ stream.addProperty("misc:soundEffectsEnabled", isSoundEffectsEnabled());
+ stream.addProperty("misc:hapticFeedbackEnabled", isHapticFeedbackEnabled());
+
+ // theme attributes
+ Resources.Theme theme = getContext().getTheme();
+ if (theme != null) {
+ stream.addPropertyKey("theme");
+ theme.encode(stream);
+ }
+
+ // view attribute information
+ int n = mAttributes != null ? mAttributes.length : 0;
+ stream.addProperty("meta:__attrCount__", n/2);
+ for (int i = 0; i < n; i += 2) {
+ stream.addProperty("meta:__attr__" + mAttributes[i], mAttributes[i+1]);
+ }
+
+ stream.addProperty("misc:scrollBarStyle", getScrollBarStyle());
+
+ // text
+ stream.addProperty("text:textDirection", getTextDirection());
+ stream.addProperty("text:textAlignment", getTextAlignment());
+
+ // accessibility
+ CharSequence contentDescription = getContentDescription();
+ stream.addProperty("accessibility:contentDescription",
+ contentDescription == null ? "" : contentDescription.toString());
+ stream.addProperty("accessibility:labelFor", getLabelFor());
+ stream.addProperty("accessibility:importantForAccessibility", getImportantForAccessibility());
+ }
}
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 27304f5..8bf53a8 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -800,6 +801,7 @@
/**
* Dumps the view hierarchy starting from the given view.
+ * @deprecated See {@link #dumpv2(View, ByteArrayOutputStream)} below.
* @hide
*/
public static void dump(View root, boolean skipChildren, boolean includeProperties,
@@ -825,6 +827,28 @@
}
/**
+ * Dumps the view hierarchy starting from the given view.
+ * Rather than using reflection, it uses View's encode method to obtain all the properties.
+ * @hide
+ */
+ public static void dumpv2(@NonNull final View view, @NonNull ByteArrayOutputStream out)
+ throws InterruptedException {
+ final ViewHierarchyEncoder encoder = new ViewHierarchyEncoder(out);
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ view.post(new Runnable() {
+ @Override
+ public void run() {
+ view.encode(encoder);
+ latch.countDown();
+ }
+ });
+
+ latch.await(2, TimeUnit.SECONDS);
+ encoder.endStream();
+ }
+
+ /**
* Dumps the theme attributes from the given View.
* @hide
*/
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index c25042a..51c4760 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -6861,6 +6861,19 @@
}
return String.valueOf(size);
}
+
+ /** @hide */
+ void encode(@NonNull ViewHierarchyEncoder encoder) {
+ encoder.beginObject(this);
+ encodeProperties(encoder);
+ encoder.endObject();
+ }
+
+ /** @hide */
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
+ encoder.addProperty("width", width);
+ encoder.addProperty("height", height);
+ }
}
/**
@@ -7329,6 +7342,18 @@
bottomMargin,
paint);
}
+
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
+ super.encodeProperties(encoder);
+ encoder.addProperty("leftMargin", leftMargin);
+ encoder.addProperty("topMargin", topMargin);
+ encoder.addProperty("rightMargin", rightMargin);
+ encoder.addProperty("bottomMargin", bottomMargin);
+ encoder.addProperty("startMargin", startMargin);
+ encoder.addProperty("endMargin", endMargin);
+ }
}
/* Describes a touched view and the ids of the pointers that it has captured.
@@ -7665,4 +7690,23 @@
canvas.drawLines(sDebugLines, paint);
}
+
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
+ super.encodeProperties(encoder);
+
+ encoder.addProperty("focus:descendantFocusability", getDescendantFocusability());
+ encoder.addProperty("drawing:clipChildren", getClipChildren());
+ encoder.addProperty("drawing:clipToPadding", getClipToPadding());
+ encoder.addProperty("drawing:childrenDrawingOrderEnabled", isChildrenDrawingOrderEnabled());
+ encoder.addProperty("drawing:persistentDrawingCache", getPersistentDrawingCache());
+
+ int n = getChildCount();
+ encoder.addProperty("meta:__childCount__", (short)n);
+ for (int i = 0; i < n; i++) {
+ encoder.addPropertyKey("meta:__child__" + i);
+ getChildAt(i).encode(encoder);
+ }
+ }
}
diff --git a/core/java/android/view/ViewHierarchyEncoder.java b/core/java/android/view/ViewHierarchyEncoder.java
new file mode 100644
index 0000000..8770216
--- /dev/null
+++ b/core/java/android/view/ViewHierarchyEncoder.java
@@ -0,0 +1,201 @@
+package android.view;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * {@link ViewHierarchyEncoder} is a serializer that is tailored towards writing out
+ * view hierarchies (the view tree, along with the properties for each view) to a stream.
+ *
+ * It is typically used as follows:
+ * <pre>
+ * ViewHierarchyEncoder e = new ViewHierarchyEncoder();
+ *
+ * for (View view : views) {
+ * e.beginObject(view);
+ * e.addProperty("prop1", value);
+ * ...
+ * e.endObject();
+ * }
+ *
+ * // repeat above snippet for each view, finally end with:
+ * e.endStream();
+ * </pre>
+ *
+ * <p>On the stream, a snippet such as the above gets encoded as a series of Map's (one
+ * corresponding to each view) with the property name as the key and the property value
+ * as the value.
+ *
+ * <p>Since the property names are practically the same across all views, rather than using
+ * the property name directly as the key, we use a short integer id corresponding to each
+ * property name as the key. A final map is added at the end which contains the mapping
+ * from the integer to its property name.
+ *
+ * <p>A value is encoded as a single byte type identifier followed by the encoding of the
+ * value. Only primitive types are supported as values, in addition to the Map type.
+ *
+ * @hide
+ */
+public class ViewHierarchyEncoder {
+ // Prefixes for simple primitives. These match the JNI definitions.
+ private static final byte SIG_BOOLEAN = 'Z';
+ private static final byte SIG_BYTE = 'B';
+ private static final byte SIG_SHORT = 'S';
+ private static final byte SIG_INT = 'I';
+ private static final byte SIG_LONG = 'J';
+ private static final byte SIG_FLOAT = 'F';
+ private static final byte SIG_DOUBLE = 'D';
+
+ // Prefixes for some commonly used objects
+ private static final byte SIG_STRING = 'R';
+
+ private static final byte SIG_MAP = 'M'; // a map with an short key
+ private static final short SIG_END_MAP = 0;
+
+ private final DataOutputStream mStream;
+
+ private final Map<String,Short> mPropertyNames = new HashMap<String, Short>(200);
+ private short mPropertyId = 1;
+ private Charset mCharset = Charset.forName("utf-8");
+
+ public ViewHierarchyEncoder(@NonNull ByteArrayOutputStream stream) {
+ mStream = new DataOutputStream(stream);
+ }
+
+ public void beginObject(@NonNull Object o) {
+ startPropertyMap();
+ addProperty("meta:__name__", o.getClass().getName());
+ addProperty("meta:__hash__", o.hashCode());
+ }
+
+ public void endObject() {
+ endPropertyMap();
+ }
+
+ public void endStream() {
+ // write out the string table
+ startPropertyMap();
+ addProperty("__name__", "propertyIndex");
+ for (Map.Entry<String,Short> entry : mPropertyNames.entrySet()) {
+ writeShort(entry.getValue());
+ writeString(entry.getKey());
+ }
+ endPropertyMap();
+ }
+
+ public void addProperty(@NonNull String name, boolean v) {
+ writeShort(createPropertyIndex(name));
+ writeBoolean(v);
+ }
+
+ public void addProperty(@NonNull String name, short s) {
+ writeShort(createPropertyIndex(name));
+ writeShort(s);
+ }
+
+ public void addProperty(@NonNull String name, int v) {
+ writeShort(createPropertyIndex(name));
+ writeInt(v);
+ }
+
+ public void addProperty(@NonNull String name, float v) {
+ writeShort(createPropertyIndex(name));
+ writeFloat(v);
+ }
+
+ public void addProperty(@NonNull String name, @Nullable String s) {
+ writeShort(createPropertyIndex(name));
+ writeString(s);
+ }
+
+ /**
+ * Writes the given name as the property name, and leaves it to the callee
+ * to fill in value for this property.
+ */
+ public void addPropertyKey(@NonNull String name) {
+ writeShort(createPropertyIndex(name));
+ }
+
+ private short createPropertyIndex(@NonNull String name) {
+ Short index = mPropertyNames.get(name);
+ if (index == null) {
+ index = mPropertyId++;
+ mPropertyNames.put(name, index);
+ }
+
+ return index;
+ }
+
+ private void startPropertyMap() {
+ try {
+ mStream.write(SIG_MAP);
+ } catch (IOException e) {
+ // does not happen since the stream simply wraps a ByteArrayOutputStream
+ }
+ }
+
+ private void endPropertyMap() {
+ writeShort(SIG_END_MAP);
+ }
+
+ private void writeBoolean(boolean v) {
+ try {
+ mStream.write(SIG_BOOLEAN);
+ mStream.write(v ? 1 : 0);
+ } catch (IOException e) {
+ // does not happen since the stream simply wraps a ByteArrayOutputStream
+ }
+ }
+
+ private void writeShort(short s) {
+ try {
+ mStream.write(SIG_SHORT);
+ mStream.writeShort(s);
+ } catch (IOException e) {
+ // does not happen since the stream simply wraps a ByteArrayOutputStream
+ }
+ }
+
+ private void writeInt(int i) {
+ try {
+ mStream.write(SIG_INT);
+ mStream.writeInt(i);
+ } catch (IOException e) {
+ // does not happen since the stream simply wraps a ByteArrayOutputStream
+ }
+ }
+
+ private void writeFloat(float v) {
+ try {
+ mStream.write(SIG_FLOAT);
+ mStream.writeFloat(v);
+ } catch (IOException e) {
+ // does not happen since the stream simply wraps a ByteArrayOutputStream
+ }
+ }
+
+ private void writeString(@Nullable String s) {
+ if (s == null) {
+ s = "";
+ }
+
+ try {
+ mStream.write(SIG_STRING);
+ byte[] bytes = s.getBytes(mCharset);
+
+ short len = (short)Math.min(bytes.length, Short.MAX_VALUE);
+ mStream.writeShort(len);
+
+ mStream.write(bytes, 0, len);
+ } catch (IOException e) {
+ // does not happen since the stream simply wraps a ByteArrayOutputStream
+ }
+ }
+}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 080498c..ea1dadb2 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -76,6 +76,7 @@
import com.android.internal.R;
import com.android.internal.os.SomeArgs;
+import com.android.internal.policy.PhoneFallbackEventHandler;
import com.android.internal.util.ScreenShapeHelper;
import com.android.internal.view.BaseSurfaceHolder;
import com.android.internal.view.RootViewSurfaceTaker;
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index e983910..2797b6e 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -16,11 +16,11 @@
package android.view;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.app.Presentation;
import android.content.Context;
import android.content.pm.ActivityInfo;
-import android.graphics.Insets;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.IBinder;
@@ -1119,6 +1119,15 @@
public static final int PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS = 0x00000800;
/**
+ * Flag to force the status bar window to be visible all the time. If the bar is hidden when
+ * this flag is set it will be shown again and the bar will have a transparent background.
+ * This can only be set by {@link LayoutParams#TYPE_STATUS_BAR}.
+ *
+ * {@hide}
+ */
+ public static final int PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT = 0x00001000;
+
+ /**
* Control flags that are private to the platform.
* @hide
*/
@@ -2066,5 +2075,18 @@
}
private CharSequence mTitle = "";
+
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
+ super.encodeProperties(encoder);
+
+ encoder.addProperty("x", x);
+ encoder.addProperty("y", y);
+ encoder.addProperty("horizontalWeight", horizontalWeight);
+ encoder.addProperty("verticalWeight", verticalWeight);
+ encoder.addProperty("type", type);
+ encoder.addProperty("flags", flags);
+ }
}
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index b8b3719..e27e253 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -16,6 +16,7 @@
package android.webkit;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.Widget;
import android.content.Context;
@@ -43,6 +44,7 @@
import android.view.ViewStructure;
import android.view.ViewDebug;
import android.view.ViewGroup;
+import android.view.ViewHierarchyEncoder;
import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -2576,4 +2578,18 @@
super.onFinishTemporaryDetach();
mProvider.getViewDelegate().onFinishTemporaryDetach();
}
+
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
+ super.encodeProperties(encoder);
+
+ checkThread();
+ encoder.addProperty("webview:contentHeight", mProvider.getContentHeight());
+ encoder.addProperty("webview:contentWidth", mProvider.getContentWidth());
+ encoder.addProperty("webview:scale", mProvider.getScale());
+ encoder.addProperty("webview:title", mProvider.getTitle());
+ encoder.addProperty("webview:url", mProvider.getUrl());
+ encoder.addProperty("webview:originalUrl", mProvider.getOriginalUrl());
+ }
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index c57a53a..9903b7e 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -18,6 +18,7 @@
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
@@ -56,6 +57,7 @@
import android.view.ViewConfiguration;
import android.view.ViewDebug;
import android.view.ViewGroup;
+import android.view.ViewHierarchyEncoder;
import android.view.ViewParent;
import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityEvent;
@@ -6330,6 +6332,16 @@
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
+
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
+ super.encodeProperties(encoder);
+
+ encoder.addProperty("list:viewType", viewType);
+ encoder.addProperty("list:recycledHeaderFooter", recycledHeaderFooter);
+ encoder.addProperty("list:forceAdd", forceAdd);
+ }
}
/**
@@ -6912,6 +6924,25 @@
}
}
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
+ super.encodeProperties(encoder);
+
+ encoder.addProperty("drawing:cacheColorHint", getCacheColorHint());
+ encoder.addProperty("list:fastScrollEnabled", isFastScrollEnabled());
+ encoder.addProperty("list:scrollingCacheEnabled", isScrollingCacheEnabled());
+ encoder.addProperty("list:smoothScrollbarEnabled", isSmoothScrollbarEnabled());
+ encoder.addProperty("list:stackFromBottom", isStackFromBottom());
+ encoder.addProperty("list:textFilterEnabled", isTextFilterEnabled());
+
+ View selectedView = getSelectedView();
+ if (selectedView != null) {
+ encoder.addPropertyKey("selectedView");
+ selectedView.encode(encoder);
+ }
+ }
+
/**
* Abstract positon scroller used to handle smooth scrolling.
*/
diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java
index e0b0e1f..f08141c 100644
--- a/core/java/android/widget/ActionMenuPresenter.java
+++ b/core/java/android/widget/ActionMenuPresenter.java
@@ -61,6 +61,7 @@
implements ActionProvider.SubUiVisibilityListener {
private static final String TAG = "ActionMenuPresenter";
private static final int ITEM_ANIMATION_DURATION = 150;
+ private static final boolean ACTIONBAR_ANIMATIONS_ENABLED = false;
private OverflowMenuButton mOverflowButton;
private boolean mReserveOverflow;
@@ -414,7 +415,7 @@
@Override
public void updateMenuView(boolean cleared) {
final ViewGroup menuViewParent = (ViewGroup) ((View) mMenuView).getParent();
- if (menuViewParent != null) {
+ if (menuViewParent != null && ACTIONBAR_ANIMATIONS_ENABLED) {
setupItemAnimations();
}
super.updateMenuView(cleared);
diff --git a/core/java/android/widget/ActionMenuView.java b/core/java/android/widget/ActionMenuView.java
index d6f2276..278a8fb 100644
--- a/core/java/android/widget/ActionMenuView.java
+++ b/core/java/android/widget/ActionMenuView.java
@@ -15,6 +15,7 @@
*/
package android.widget;
+import android.annotation.NonNull;
import android.annotation.StyleRes;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -28,6 +29,7 @@
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
+import android.view.ViewHierarchyEncoder;
import android.view.accessibility.AccessibilityEvent;
import com.android.internal.view.menu.ActionMenuItemView;
import com.android.internal.view.menu.MenuBuilder;
@@ -835,5 +837,17 @@
super(width, height);
this.isOverflowButton = isOverflowButton;
}
+
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
+ super.encodeProperties(encoder);
+
+ encoder.addProperty("layout:overFlowButton", isOverflowButton);
+ encoder.addProperty("layout:cellsUsed", cellsUsed);
+ encoder.addProperty("layout:extraPixels", extraPixels);
+ encoder.addProperty("layout:expandable", expandable);
+ encoder.addProperty("layout:preventEdgeOffset", preventEdgeOffset);
+ }
}
}
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index 72cb0b5..54e3996 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.database.DataSetObserver;
@@ -29,6 +30,7 @@
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
+import android.view.ViewHierarchyEncoder;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -1245,4 +1247,16 @@
}
}
}
+
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
+ super.encodeProperties(encoder);
+
+ encoder.addProperty("scrolling:firstPosition", mFirstPosition);
+ encoder.addProperty("list:nextSelectedPosition", mNextSelectedPosition);
+ encoder.addProperty("list:nextSelectedRowId", mNextSelectedRowId);
+ encoder.addProperty("list:selectedPosition", mSelectedPosition);
+ encoder.addProperty("list:itemCount", mItemCount);
+ }
}
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index 22e079c..6b4b2c7 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -16,6 +16,8 @@
package android.widget;
+import android.annotation.NonNull;
+import android.view.ViewHierarchyEncoder;
import com.android.internal.R;
import android.annotation.DrawableRes;
@@ -459,4 +461,11 @@
info.setCheckable(true);
info.setChecked(mChecked);
}
+
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) {
+ super.encodeProperties(stream);
+ stream.addProperty("text:checked", isChecked());
+ }
}
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index f2afeeb..770077d 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -17,8 +17,10 @@
package android.widget;
import android.annotation.DrawableRes;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.PorterDuff;
+import android.view.ViewHierarchyEncoder;
import com.android.internal.R;
import android.content.Context;
@@ -530,9 +532,16 @@
@Override
public void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
-
+
super.onRestoreInstanceState(ss.getSuperState());
setChecked(ss.checked);
requestLayout();
}
+
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) {
+ super.encodeProperties(stream);
+ stream.addProperty("checked", isChecked());
+ }
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 712fdba..86a100f 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -688,44 +688,101 @@
private int getWordStart(int offset) {
// FIXME - For this and similar methods we're not doing anything to check if there's
// a LocaleSpan in the text, this may be something we should try handling or checking for.
- int retOffset = getWordIteratorWithText().getBeginning(offset);
- if (retOffset == BreakIterator.DONE) retOffset = offset;
- return retOffset;
- }
-
- private int getWordEnd(int offset, boolean includePunctuation) {
- int retOffset = getWordIteratorWithText().getEnd(offset);
- if (retOffset == BreakIterator.DONE) {
- retOffset = offset;
- } else if (includePunctuation) {
- retOffset = handlePunctuation(retOffset);
- }
- return retOffset;
- }
-
- private boolean isEndBoundary(int offset) {
- int thisEnd = getWordEnd(offset, false);
- return offset == thisEnd;
- }
-
- private boolean isStartBoundary(int offset) {
- int thisStart = getWordStart(offset);
- return thisStart == offset;
- }
-
- private int handlePunctuation(int offset) {
- // FIXME - Check with UX how repeated ending punctuation should be handled.
- // FIXME - Check with UX if / how we would handle non sentence ending characters.
- // FIXME - Consider punctuation in different languages.
- CharSequence text = mTextView.getText();
- if (offset < text.length()) {
- int c = Character.codePointAt(text, offset);
- if (c == 0x002e /* period */|| c == 0x003f /* question mark */
- || c == 0x0021 /* exclamation mark */) {
- offset = Character.offsetByCodePoints(text, offset, 1);
+ int retOffset = getWordIteratorWithText().prevBoundary(offset);
+ if (isPunctBoundaryBehind(retOffset, true /* isStart */)) {
+ // If we're on a punctuation boundary we should continue to get the
+ // previous offset until we're not longer on a punctuation boundary.
+ retOffset = getWordIteratorWithText().prevBoundary(retOffset);
+ while (!isPunctBoundaryBehind(retOffset, false /* isStart */)
+ && retOffset != BreakIterator.DONE) {
+ retOffset = getWordIteratorWithText().prevBoundary(retOffset);
}
}
- return offset;
+ if (retOffset == BreakIterator.DONE) {
+ return offset;
+ }
+ return retOffset;
+ }
+
+ private int getWordEnd(int offset) {
+ int retOffset = getWordIteratorWithText().nextBoundary(offset);
+ if (isPunctBoundaryForward(retOffset, true /* isStart */)) {
+ // If we're on a punctuation boundary we should continue to get the
+ // next offset until we're no longer on a punctuation boundary.
+ retOffset = getWordIteratorWithText().nextBoundary(retOffset);
+ while (!isPunctBoundaryForward(retOffset, false /* isStart */)
+ && retOffset != BreakIterator.DONE) {
+ retOffset = getWordIteratorWithText().nextBoundary(retOffset);
+ }
+ }
+ if (retOffset == BreakIterator.DONE) {
+ return offset;
+ }
+ return retOffset;
+ }
+
+ /**
+ * Checks for punctuation boundaries for the provided offset and the
+ * previous character.
+ *
+ * @param offset The offset to check from.
+ * @param isStart Whether the boundary being checked for is at the start or
+ * end of a punctuation sequence.
+ * @return Whether this is a punctuation boundary.
+ */
+ private boolean isPunctBoundaryBehind(int offset, boolean isStart) {
+ CharSequence text = mTextView.getText();
+ if (offset == BreakIterator.DONE || offset > text.length() || offset == 0) {
+ return false;
+ }
+ int cp = Character.codePointAt(text, offset);
+ int prevCp = Character.codePointBefore(text, offset);
+
+ if (isPunctuation(cp)) {
+ // If it's the start, the current cp and the prev cp are
+ // punctuation. If it's at the end of a punctuation sequence the
+ // current is punctuation and the prev is not.
+ return isStart ? isPunctuation(prevCp) : !isPunctuation(prevCp);
+ }
+ return false;
+ }
+
+ /**
+ * Checks for punctuation boundaries for the provided offset and the next
+ * character.
+ *
+ * @param offset The offset to check from.
+ * @param isStart Whether the boundary being checked for is at the start or
+ * end of a punctuation sequence.
+ * @return Whether this is a punctuation boundary.
+ */
+ private boolean isPunctBoundaryForward(int offset, boolean isStart) {
+ CharSequence text = mTextView.getText();
+ if (offset == BreakIterator.DONE || offset > text.length() || offset == 0) {
+ return false;
+ }
+ int cp = Character.codePointBefore(text, offset);
+ int nextCpOffset = Math.min(offset + Character.charCount(cp), text.length() - 1);
+ int nextCp = Character.codePointBefore(text, nextCpOffset);
+
+ if (isPunctuation(cp)) {
+ // If it's the start, the current cp and the next cp are
+ // punctuation. If it's at the end of a punctuation sequence the
+ // current is punctuation and the next is not.
+ return isStart ? isPunctuation(nextCp) : !isPunctuation(nextCp);
+ }
+ return false;
+ }
+
+ private boolean isPunctuation(int cp) {
+ int type = Character.getType(cp);
+ return (type == Character.CONNECTOR_PUNCTUATION ||
+ type == Character.DASH_PUNCTUATION ||
+ type == Character.END_PUNCTUATION ||
+ type == Character.FINAL_QUOTE_PUNCTUATION ||
+ type == Character.INITIAL_QUOTE_PUNCTUATION ||
+ type == Character.OTHER_PUNCTUATION ||
+ type == Character.START_PUNCTUATION);
}
/**
@@ -788,7 +845,7 @@
if (selectionStart == BreakIterator.DONE || selectionEnd == BreakIterator.DONE ||
selectionStart == selectionEnd) {
// Possible when the word iterator does not properly handle the text's language
- long range = getCharRange(minOffset);
+ long range = getCharClusterRange(minOffset);
selectionStart = TextUtils.unpackRangeStartFromLong(range);
selectionEnd = TextUtils.unpackRangeEndFromLong(range);
}
@@ -831,29 +888,25 @@
return mWordIteratorWithText;
}
- private long getCharRange(int offset) {
+ private int getNextCursorOffset(int offset, boolean findAfterGivenOffset) {
+ final Layout layout = mTextView.getLayout();
+ if (layout == null) return offset;
+ final CharSequence text = mTextView.getText();
+ final int nextOffset = layout.getPaint().getTextRunCursor(text, 0, text.length(),
+ layout.isRtlCharAt(offset) ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR,
+ offset, findAfterGivenOffset ? Paint.CURSOR_AFTER : Paint.CURSOR_BEFORE);
+ return nextOffset == -1 ? offset : nextOffset;
+ }
+
+ private long getCharClusterRange(int offset) {
final int textLength = mTextView.getText().length();
- if (offset + 1 < textLength) {
- final char currentChar = mTextView.getText().charAt(offset);
- final char nextChar = mTextView.getText().charAt(offset + 1);
- if (Character.isSurrogatePair(currentChar, nextChar)) {
- return TextUtils.packRangeInLong(offset, offset + 2);
- }
- }
if (offset < textLength) {
- return TextUtils.packRangeInLong(offset, offset + 1);
- }
- if (offset - 2 >= 0) {
- final char previousChar = mTextView.getText().charAt(offset - 1);
- final char previousPreviousChar = mTextView.getText().charAt(offset - 2);
- if (Character.isSurrogatePair(previousPreviousChar, previousChar)) {
- return TextUtils.packRangeInLong(offset - 2, offset);
- }
+ return TextUtils.packRangeInLong(offset, getNextCursorOffset(offset, true));
}
if (offset - 1 >= 0) {
- return TextUtils.packRangeInLong(offset - 1, offset);
+ return TextUtils.packRangeInLong(getNextCursorOffset(offset, false), offset);
}
- return TextUtils.packRangeInLong(offset, offset);
+ return TextUtils.packRangeInLong(offset, offset);
}
private boolean touchPositionIsInSelection() {
@@ -3903,13 +3956,9 @@
public void updatePosition(float x, float y) {
final int trueOffset = mTextView.getOffsetForPosition(x, y);
final int currLine = mTextView.getLineAtCoordinate(y);
-
- // Don't select white space on different lines.
- if (isWhitespaceLine(mPrevLine, currLine, trueOffset)) return;
-
boolean positionCursor = false;
int offset = trueOffset;
- int end = getWordEnd(offset, true);
+ int end = getWordEnd(offset);
int start = getWordStart(offset);
if (offset < mPreviousOffset) {
@@ -3925,7 +3974,7 @@
}
}
mTouchWordOffset = Math.max(trueOffset - offset, 0);
- mInWord = !isStartBoundary(offset);
+ mInWord = !getWordIteratorWithText().isBoundary(offset);
positionCursor = true;
} else if (offset - mTouchWordOffset > mPreviousOffset) {
// User is shrinking the selection.
@@ -3934,7 +3983,7 @@
offset = end;
}
offset -= mTouchWordOffset;
- mInWord = !isEndBoundary(offset);
+ mInWord = !getWordIteratorWithText().isBoundary(offset);
positionCursor = true;
}
@@ -3946,7 +3995,7 @@
int alteredOffset = mTextView.getOffsetAtCoordinate(mPrevLine, x);
if (alteredOffset >= selectionEnd) {
// Can't pass the other drag handle.
- offset = Math.max(0, selectionEnd - 1);
+ offset = getNextCursorOffset(selectionEnd, false);
} else {
offset = alteredOffset;
}
@@ -4005,14 +4054,9 @@
public void updatePosition(float x, float y) {
final int trueOffset = mTextView.getOffsetForPosition(x, y);
final int currLine = mTextView.getLineAtCoordinate(y);
-
- // Don't select white space on different lines.
- if (isWhitespaceLine(mPrevLine, currLine, trueOffset)) return;
-
int offset = trueOffset;
boolean positionCursor = false;
-
- int end = getWordEnd(offset, true);
+ int end = getWordEnd(offset);
int start = getWordStart(offset);
if (offset > mPreviousOffset) {
@@ -4028,7 +4072,7 @@
}
}
mTouchWordOffset = Math.max(offset - trueOffset, 0);
- mInWord = !isEndBoundary(offset);
+ mInWord = !getWordIteratorWithText().isBoundary(offset);
positionCursor = true;
} else if (offset + mTouchWordOffset < mPreviousOffset) {
// User is shrinking the selection.
@@ -4038,7 +4082,7 @@
}
offset += mTouchWordOffset;
positionCursor = true;
- mInWord = !isStartBoundary(offset);
+ mInWord = !getWordIteratorWithText().isBoundary(offset);
}
if (positionCursor) {
@@ -4049,7 +4093,7 @@
int length = mTextView.getText().length();
if (alteredOffset <= selectionStart) {
// Can't pass the other drag handle.
- offset = Math.min(selectionStart + 1, length);
+ offset = getNextCursorOffset(selectionStart, true);
} else {
offset = Math.min(alteredOffset, length);
}
@@ -4070,36 +4114,6 @@
}
/**
- * Checks whether selection is happening on a different line than previous and
- * if that line only contains whitespace up to the touch location.
- *
- * @param prevLine The previous line the selection was on.
- * @param currLine The current line being selected.
- * @param offset The offset in the text where the touch occurred.
- * @return Whether or not it was just a white space line being selected.
- */
- private boolean isWhitespaceLine(int prevLine, int currLine, int offset) {
- if (prevLine == currLine) {
- // Same line; don't care.
- return false;
- }
- CharSequence text = mTextView.getText();
- if (offset == text.length()) {
- // No character at the last position.
- return false;
- }
- int lineEndOffset = mTextView.getLayout().getLineEnd(currLine);
- for (int cp, i = offset; i < lineEndOffset; i += Character.charCount(cp)) {
- cp = Character.codePointAt(text, i);
- if (!Character.isSpaceChar(cp) && !Character.isWhitespace(cp)) {
- // There are non white space chars on the line.
- return false;
- }
- }
- return true;
- }
-
- /**
* A CursorController instance can be used to control a cursor in the text.
*/
private interface CursorController extends ViewTreeObserver.OnTouchModeChangeListener {
@@ -4178,8 +4192,6 @@
private int mStartOffset = -1;
// Indicates whether the user is selecting text and using the drag accelerator.
private boolean mDragAcceleratorActive;
- // Indicates the line of text the drag accelerator is on.
- private int mPrevLine = -1;
SelectionModifierCursorController() {
resetTouchOffsets();
@@ -4272,8 +4284,6 @@
}
}
- // New selection, reset line.
- mPrevLine = mTextView.getLineAtCoordinate(y);
mDownPositionX = x;
mDownPositionY = y;
mGestureStayedInTapRegion = true;
@@ -4318,7 +4328,7 @@
// We don't start "dragging" until the user is past the initial word that
// gets selected on long press.
int firstWordStart = getWordStart(mStartOffset);
- int firstWordEnd = getWordEnd(mStartOffset, false);
+ int firstWordEnd = getWordEnd(mStartOffset);
if (offset > firstWordEnd || offset < firstWordStart) {
// Basically the goal in the below code is to have the highlight be
@@ -4330,13 +4340,6 @@
if (my > fingerOffset) my -= fingerOffset;
offset = mTextView.getOffsetForPosition(mx, my);
- int currLine = mTextView.getLineAtCoordinate(my);
-
- // Don't select white space on different lines.
- if (isWhitespaceLine(mPrevLine, currLine, offset)) return;
-
- mPrevLine = currLine;
-
// Perform the check for closeness at edge of view, if we're very close
// don't adjust the offset to be in front of the finger - otherwise the
// user can't select words at the edge.
@@ -4365,7 +4368,7 @@
// Need to adjust start offset based on direction of movement.
int newStart = mStartOffset < offset ? getWordStart(mStartOffset)
- : getWordEnd(mStartOffset, true);
+ : getWordEnd(mStartOffset);
Selection.setSelection((Spannable) mTextView.getText(), newStart,
offset);
}
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index 0602944..7ca450a 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -33,6 +33,7 @@
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
+import android.view.ViewHierarchyEncoder;
import android.widget.RemoteViews.RemoteView;
import com.android.internal.R;
@@ -407,6 +408,18 @@
return FrameLayout.class.getName();
}
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
+ super.encodeProperties(encoder);
+
+ encoder.addProperty("measurement:measureAllChildren", mMeasureAllChildren);
+ encoder.addProperty("padding:foregroundPaddingLeft", mForegroundPaddingLeft);
+ encoder.addProperty("padding:foregroundPaddingTop", mForegroundPaddingTop);
+ encoder.addProperty("padding:foregroundPaddingRight", mForegroundPaddingRight);
+ encoder.addProperty("padding:foregroundPaddingBottom", mForegroundPaddingBottom);
+ }
+
/**
* Per-child layout information for layouts that support margins.
* See {@link android.R.styleable#FrameLayout_Layout FrameLayout Layout Attributes}
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index c959774..dcaafa5 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -17,6 +17,7 @@
package android.widget;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
@@ -31,6 +32,7 @@
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
+import android.view.ViewHierarchyEncoder;
import android.view.ViewRootImpl;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
@@ -2420,4 +2422,11 @@
row, 1, column, 1, isHeading, isSelected);
info.setCollectionItemInfo(itemInfo);
}
+
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
+ super.encodeProperties(encoder);
+ encoder.addProperty("numColumns", getNumColumns());
+ }
}
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 0879c5d..cf67905 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
@@ -35,6 +36,7 @@
import android.view.ViewConfiguration;
import android.view.ViewDebug;
import android.view.ViewGroup;
+import android.view.ViewHierarchyEncoder;
import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -1695,6 +1697,13 @@
return ss;
}
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
+ super.encodeProperties(encoder);
+ encoder.addProperty("layout:fillViewPort", mFillViewport);
+ }
+
static class SavedState extends BaseSavedState {
public int scrollPosition;
public boolean isLayoutRtl;
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 6d2f368..05059bc 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -17,6 +17,7 @@
package android.widget;
import android.annotation.DrawableRes;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Context;
@@ -43,6 +44,7 @@
import android.view.RemotableViewMethod;
import android.view.View;
import android.view.ViewDebug;
+import android.view.ViewHierarchyEncoder;
import android.view.accessibility.AccessibilityEvent;
import android.widget.RemoteViews.RemoteView;
@@ -1431,4 +1433,11 @@
public CharSequence getAccessibilityClassName() {
return ImageView.class.getName();
}
+
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) {
+ super.encodeProperties(stream);
+ stream.addProperty("layout:baseline", getBaseline());
+ }
}
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 72f51c9..f153ce5 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -19,6 +19,7 @@
import com.android.internal.R;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.TypedArray;
@@ -29,6 +30,7 @@
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
+import android.view.ViewHierarchyEncoder;
import android.widget.RemoteViews.RemoteView;
import java.lang.annotation.Retention;
@@ -1813,6 +1815,20 @@
return LinearLayout.class.getName();
}
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
+ super.encodeProperties(encoder);
+ encoder.addProperty("layout:baselineAligned", mBaselineAligned);
+ encoder.addProperty("layout:baselineAlignedChildIndex", mBaselineAlignedChildIndex);
+ encoder.addProperty("measurement:baselineChildTop", mBaselineChildTop);
+ encoder.addProperty("measurement:orientation", mOrientation);
+ encoder.addProperty("measurement:gravity", mGravity);
+ encoder.addProperty("measurement:totalLength", mTotalLength);
+ encoder.addProperty("layout:totalLength", mTotalLength);
+ encoder.addProperty("layout:useLargestChild", mUseLargestChild);
+ }
+
/**
* Per-child layout information associated with ViewLinearLayout.
*
@@ -1921,5 +1937,14 @@
return output + "LinearLayout.LayoutParams={width=" + sizeToString(width) +
", height=" + sizeToString(height) + " weight=" + weight + "}";
}
+
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
+ super.encodeProperties(encoder);
+
+ encoder.addProperty("layout:weight", weight);
+ encoder.addProperty("layout:gravity", gravity);
+ }
}
}
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index 05866f0..94b9416 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -1791,8 +1791,9 @@
private class ResizePopupRunnable implements Runnable {
public void run() {
- if (mDropDownList != null && mDropDownList.getCount() > mDropDownList.getChildCount() &&
- mDropDownList.getChildCount() <= mListItemExpandMaximum) {
+ if (mDropDownList != null && mDropDownList.isAttachedToWindow()
+ && mDropDownList.getCount() > mDropDownList.getChildCount()
+ && mDropDownList.getChildCount() <= mListItemExpandMaximum) {
mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
show();
}
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index a79c8e8..7dcaa1f 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -23,6 +23,7 @@
import com.google.android.collect.Lists;
import android.annotation.IdRes;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
@@ -40,6 +41,7 @@
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
+import android.view.ViewHierarchyEncoder;
import android.view.ViewParent;
import android.view.ViewRootImpl;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -3938,4 +3940,12 @@
position, 1, 0, 1, isHeading, isSelected);
info.setCollectionItemInfo(itemInfo);
}
+
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
+ super.encodeProperties(encoder);
+
+ encoder.addProperty("recycleOnMeasure", recycleOnMeasure());
+ }
}
diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java
index 8d8b3a3..97348e30 100644
--- a/core/java/android/widget/MediaController.java
+++ b/core/java/android/widget/MediaController.java
@@ -28,7 +28,7 @@
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
-import android.view.PhoneWindow;
+import com.android.internal.policy.PhoneWindow;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index b59ae17..639a09c 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.PorterDuff;
@@ -49,6 +50,7 @@
import android.view.RemotableViewMethod;
import android.view.View;
import android.view.ViewDebug;
+import android.view.ViewHierarchyEncoder;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AlphaAnimation;
@@ -1893,6 +1895,17 @@
postDelayed(mAccessibilityEventSender, TIMEOUT_SEND_ACCESSIBILITY_EVENT);
}
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) {
+ super.encodeProperties(stream);
+
+ stream.addProperty("progress:max", getMax());
+ stream.addProperty("progress:progress", getProgress());
+ stream.addProperty("progress:secondaryProgress", getSecondaryProgress());
+ stream.addProperty("progress:indeterminate", isIndeterminate());
+ }
+
/**
* Command for sending an accessibility event.
*/
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index d12739f..affc5da 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.annotation.NonNull;
import android.util.ArrayMap;
import com.android.internal.R;
@@ -36,6 +37,7 @@
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
+import android.view.ViewHierarchyEncoder;
import android.view.accessibility.AccessibilityEvent;
import android.widget.RemoteViews.RemoteView;
@@ -1616,6 +1618,13 @@
// This will set the layout direction
super.resolveLayoutDirection(layoutDirection);
}
+
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
+ super.encodeProperties(encoder);
+ encoder.addProperty("layout:alignWithParent", alignWithParent);
+ }
}
private static class DependencyGraph {
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 98d61d3..2709f25 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.annotation.NonNull;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -38,6 +39,7 @@
import android.view.ViewConfiguration;
import android.view.ViewDebug;
import android.view.ViewGroup;
+import android.view.ViewHierarchyEncoder;
import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -1787,6 +1789,13 @@
return ss;
}
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
+ super.encodeProperties(encoder);
+ encoder.addProperty("fillViewport", mFillViewport);
+ }
+
static class SavedState extends BaseSavedState {
public int scrollPosition;
diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java
index f73ee49..d4288d6 100644
--- a/core/java/android/widget/TableRow.java
+++ b/core/java/android/widget/TableRow.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
@@ -24,7 +25,7 @@
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
-
+import android.view.ViewHierarchyEncoder;
/**
* <p>A layout that arranges its children horizontally. A TableRow should
@@ -509,6 +510,14 @@
height = WRAP_CONTENT;
}
}
+
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
+ super.encodeProperties(encoder);
+ encoder.addProperty("layout:column", column);
+ encoder.addProperty("layout:span", span);
+ }
}
// special transparent hierarchy change listener
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index e2acaac..5d7b569 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -32,6 +33,7 @@
import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.view.RemotableViewMethod;
+import android.view.ViewHierarchyEncoder;
import com.android.internal.R;
@@ -546,4 +548,18 @@
mTime.setTimeInMillis(System.currentTimeMillis());
setText(DateFormat.format(mFormat, mTime));
}
+
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) {
+ super.encodeProperties(stream);
+
+ CharSequence s = getFormat12Hour();
+ stream.addProperty("format12Hour", s == null ? null : s.toString());
+
+ s = getFormat24Hour();
+ stream.addProperty("format24Hour", s == null ? null : s.toString());
+ stream.addProperty("format", mFormat == null ? null : mFormat.toString());
+ stream.addProperty("hasSeconds", mHasSeconds);
+ }
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d6fdee0..68c49cd 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -120,6 +120,7 @@
import android.view.ViewGroup.LayoutParams;
import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
+import android.view.ViewHierarchyEncoder;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -2844,7 +2845,8 @@
@ViewDebug.IntToString(from = Typeface.BOLD_ITALIC, to = "BOLD_ITALIC")
})
public int getTypefaceStyle() {
- return mTextPaint.getTypeface().getStyle();
+ Typeface typeface = mTextPaint.getTypeface();
+ return typeface != null ? typeface.getStyle() : Typeface.NORMAL;
}
/**
@@ -9556,6 +9558,23 @@
}
}
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) {
+ super.encodeProperties(stream);
+
+ TruncateAt ellipsize = getEllipsize();
+ stream.addProperty("text:ellipsize", ellipsize == null ? null : ellipsize.name());
+ stream.addProperty("text:textSize", getTextSize());
+ stream.addProperty("text:scaledTextSize", getScaledTextSize());
+ stream.addProperty("text:typefaceStyle", getTypefaceStyle());
+ stream.addProperty("text:selectionStart", getSelectionStart());
+ stream.addProperty("text:selectionEnd", getSelectionEnd());
+ stream.addProperty("text:curTextColor", mCurTextColor);
+ stream.addProperty("text:text", mText == null ? null : mText.toString());
+ stream.addProperty("text:gravity", mGravity);
+ }
+
/**
* User interface state that is stored by TextView for implementing
* {@link View#onSaveInstanceState}.
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index 6173832..9277f9b 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.os.Build;
+import android.view.View;
/**
* Log all the things.
@@ -33,6 +34,10 @@
public static final int ACTION_BAN_APP_NOTES = 146;
public static final int NOTIFICATION_ZEN_MODE_EVENT_RULE = 147;
public static final int ACTION_DISMISS_ALL_NOTES = 148;
+ public static final int QS_DND_DETAILS = 149;
+ public static final int QS_BLUETOOTH_DETAILS = 150;
+ public static final int QS_CAST_DETAILS = 151;
+ public static final int QS_WIFI_DETAILS = 152;
public static void visible(Context context, int category) throws IllegalArgumentException {
if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
@@ -41,13 +46,27 @@
EventLogTags.writeSysuiViewVisibility(category, 100);
}
- public static void hidden(Context context, int category) {
+ public static void hidden(Context context, int category) throws IllegalArgumentException {
if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
throw new IllegalArgumentException("Must define metric category");
}
EventLogTags.writeSysuiViewVisibility(category, 0);
}
+ public static void visibility(Context context, int category, boolean visibile)
+ throws IllegalArgumentException {
+ if (visibile) {
+ visible(context, category);
+ } else {
+ hidden(context, category);
+ }
+ }
+
+ public static void visibility(Context context, int category, int vis)
+ throws IllegalArgumentException {
+ visibility(context, category, vis == View.VISIBLE);
+ }
+
public static void action(Context context, int category) {
action(context, category, "");
}
diff --git a/core/java/android/view/PhoneFallbackEventHandler.java b/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java
similarity index 98%
rename from core/java/android/view/PhoneFallbackEventHandler.java
rename to core/java/com/android/internal/policy/PhoneFallbackEventHandler.java
index 350650d..2cb9c25 100644
--- a/core/java/android/view/PhoneFallbackEventHandler.java
+++ b/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.view;
+package com.android.internal.policy;
import android.app.KeyguardManager;
import android.app.SearchManager;
@@ -28,10 +28,11 @@
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.Log;
-import android.view.View;
-import android.view.HapticFeedbackConstants;
import android.view.FallbackEventHandler;
+import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
+import android.view.View;
+import com.android.internal.policy.PhoneWindow;
/**
* @hide
diff --git a/core/java/android/view/PhoneLayoutInflater.java b/core/java/com/android/internal/policy/PhoneLayoutInflater.java
similarity index 95%
rename from core/java/android/view/PhoneLayoutInflater.java
rename to core/java/com/android/internal/policy/PhoneLayoutInflater.java
index 7d89a0b..991b6bb 100644
--- a/core/java/android/view/PhoneLayoutInflater.java
+++ b/core/java/com/android/internal/policy/PhoneLayoutInflater.java
@@ -14,10 +14,12 @@
* limitations under the License.
*/
-package android.view;
+package com.android.internal.policy;
import android.content.Context;
import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
/**
* @hide
diff --git a/core/java/android/view/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
similarity index 98%
rename from core/java/android/view/PhoneWindow.java
rename to core/java/com/android/internal/policy/PhoneWindow.java
index a3e7a10..a578a6e 100644
--- a/core/java/android/view/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.view;
+package com.android.internal.policy;
import static android.view.View.MeasureSpec.AT_MOST;
import static android.view.View.MeasureSpec.EXACTLY;
@@ -27,6 +27,34 @@
import android.app.SearchManager;
import android.os.UserHandle;
+import android.view.ActionMode;
+import android.view.ContextThemeWrapper;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.IRotationWatcher.Stub;
+import android.view.IWindowManager;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.InputQueue;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.SearchEvent;
+import android.view.SurfaceHolder.Callback2;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.ViewManager;
+import android.view.ViewParent;
+import android.view.ViewRootImpl;
+import android.view.ViewStub;
+import android.view.ViewTreeObserver.OnPreDrawListener;
+import android.view.Window;
+import android.view.WindowInsets;
+import android.view.WindowManager;
import com.android.internal.R;
import com.android.internal.util.ScreenShapeHelper;
import com.android.internal.view.FloatingActionMode;
@@ -67,7 +95,6 @@
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemProperties;
import android.transition.Scene;
import android.transition.Transition;
import android.transition.TransitionInflater;
@@ -140,7 +167,7 @@
private ViewGroup mContentRoot;
- SurfaceHolder.Callback2 mTakeSurfaceCallback;
+ Callback2 mTakeSurfaceCallback;
InputQueue.Callback mTakeInputQueueCallback;
@@ -427,7 +454,7 @@
}
@Override
- public void takeSurface(SurfaceHolder.Callback2 callback) {
+ public void takeSurface(Callback2 callback) {
mTakeSurfaceCallback = callback;
}
@@ -2181,7 +2208,7 @@
private ActionBarContextView mPrimaryActionModeView;
private PopupWindow mPrimaryActionModePopup;
private Runnable mShowPrimaryActionModePopup;
- private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener;
+ private OnPreDrawListener mFloatingToolbarPreDrawListener;
private View mFloatingActionModeOriginatingView;
private FloatingToolbar mFloatingToolbar;
@@ -3354,7 +3381,7 @@
mContext, callback, originatingView, mFloatingToolbar);
mFloatingActionModeOriginatingView = originatingView;
mFloatingToolbarPreDrawListener =
- new ViewTreeObserver.OnPreDrawListener() {
+ new OnPreDrawListener() {
@Override
public boolean onPreDraw() {
mode.updateViewLocationInWindow();
@@ -4718,7 +4745,7 @@
}
- static class RotationWatcher extends IRotationWatcher.Stub {
+ static class RotationWatcher extends Stub {
private Handler mHandler;
private final Runnable mRotationChanged = new Runnable() {
public void run() {
diff --git a/core/java/com/android/internal/transition/EpicenterClipReveal.java b/core/java/com/android/internal/transition/EpicenterClipReveal.java
deleted file mode 100644
index 1a6736a..0000000
--- a/core/java/com/android/internal/transition/EpicenterClipReveal.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * 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.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.RectEvaluator;
-import android.animation.TimeInterpolator;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Rect;
-import android.transition.TransitionValues;
-import android.transition.Visibility;
-import android.util.AttributeSet;
-import android.util.Property;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AnimationUtils;
-import android.view.animation.PathInterpolator;
-
-import com.android.internal.R;
-
-/**
- * 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";
-
- private final TimeInterpolator mInterpolatorX;
- private final TimeInterpolator mInterpolatorY;
- private final boolean mCenterClipBounds;
-
- public EpicenterClipReveal() {
- mInterpolatorX = null;
- mInterpolatorY = null;
- mCenterClipBounds = false;
- }
-
- public EpicenterClipReveal(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- final TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.EpicenterClipReveal, 0, 0);
-
- mCenterClipBounds = a.getBoolean(R.styleable.EpicenterClipReveal_centerClipBounds, false);
-
- final int interpolatorX = a.getResourceId(R.styleable.EpicenterClipReveal_interpolatorX, 0);
- if (interpolatorX != 0) {
- mInterpolatorX = AnimationUtils.loadInterpolator(context, interpolatorX);
- } else {
- mInterpolatorX = TransitionConstants.LINEAR_OUT_SLOW_IN;
- }
-
- final int interpolatorY = a.getResourceId(R.styleable.EpicenterClipReveal_interpolatorY, 0);
- if (interpolatorY != 0) {
- mInterpolatorY = AnimationUtils.loadInterpolator(context, interpolatorY);
- } else {
- mInterpolatorY = TransitionConstants.FAST_OUT_SLOW_IN;
- }
-
- a.recycle();
- }
-
- @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 end = getBestRect(endValues);
- final Rect start = getEpicenterOrCenter(end);
-
- // Prepare the view.
- view.setClipBounds(start);
-
- return createRectAnimator(view, start, end, endValues, mInterpolatorX, mInterpolatorY);
- }
-
- @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 = getEpicenterOrCenter(start);
-
- // Prepare the view.
- view.setClipBounds(start);
-
- return createRectAnimator(view, start, end, endValues, mInterpolatorX, mInterpolatorY);
- }
-
- private Rect getEpicenterOrCenter(Rect bestRect) {
- final Rect epicenter = getEpicenter();
- if (epicenter != null) {
- // Translate the clip bounds to be centered within the target bounds.
- if (mCenterClipBounds) {
- final int offsetX = bestRect.centerX() - epicenter.centerX();
- final int offsetY = bestRect.centerY() - epicenter.centerY();
- epicenter.offset(offsetX, offsetY);
- }
- return epicenter;
- }
-
- final int centerX = bestRect.centerX();
- final int centerY = bestRect.centerY();
- return new Rect(centerX, centerY, centerX, centerY);
- }
-
- 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 static Animator createRectAnimator(final View view, Rect start, Rect end,
- TransitionValues endValues, TimeInterpolator interpolatorX,
- TimeInterpolator interpolatorY) {
- final RectEvaluator evaluator = new RectEvaluator(new Rect());
- final Rect terminalClip = (Rect) endValues.values.get(PROPNAME_CLIP);
-
- final ClipDimenProperty propX = new ClipDimenProperty(ClipDimenProperty.TARGET_X);
- final ObjectAnimator animX = ObjectAnimator.ofObject(view, propX, evaluator, start, end);
- if (interpolatorX != null) {
- animX.setInterpolator(interpolatorX);
- }
-
- final ClipDimenProperty propY = new ClipDimenProperty(ClipDimenProperty.TARGET_Y);
- final ObjectAnimator animY = ObjectAnimator.ofObject(view, propY, evaluator, start, end);
- if (interpolatorY != null) {
- animY.setInterpolator(interpolatorY);
- }
-
- final AnimatorSet animSet = new AnimatorSet();
- animSet.playTogether(animX, animY);
- animSet.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- view.setClipBounds(terminalClip);
- }
- });
- return animSet;
- }
-
- private static class ClipDimenProperty extends Property<View, Rect> {
- public static final char TARGET_X = 'x';
- public static final char TARGET_Y = 'y';
-
- private final Rect mTempRect = new Rect();
-
- private final int mTargetDimension;
-
- public ClipDimenProperty(char targetDimension) {
- super(Rect.class, "clip_bounds_" + targetDimension);
-
- mTargetDimension = targetDimension;
- }
-
- @Override
- public Rect get(View object) {
- final Rect tempRect = mTempRect;
- if (!object.getClipBounds(tempRect)) {
- tempRect.setEmpty();
- }
- return tempRect;
- }
-
- @Override
- public void set(View object, Rect value) {
- final Rect tempRect = mTempRect;
- if (object.getClipBounds(tempRect)) {
- if (mTargetDimension == TARGET_X) {
- tempRect.left = value.left;
- tempRect.right = value.right;
- } else {
- tempRect.top = value.top;
- tempRect.bottom = value.bottom;
- }
- object.setClipBounds(tempRect);
- }
- }
- }
-}
diff --git a/core/java/com/android/internal/transition/EpicenterTranslate.java b/core/java/com/android/internal/transition/EpicenterTranslate.java
deleted file mode 100644
index eac3ff8..0000000
--- a/core/java/com/android/internal/transition/EpicenterTranslate.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * 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 com.android.internal.R;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Rect;
-import android.transition.TransitionValues;
-import android.transition.Visibility;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AnimationUtils;
-
-/**
- * EpicenterTranslate captures the {@link View#getTranslationX()} and
- * {@link View#getTranslationY()} before and after the scene change and
- * animates between those and the epicenter's center during a visibility
- * transition.
- */
-public class EpicenterTranslate extends Visibility {
- private static final String PROPNAME_BOUNDS = "android:epicenterReveal:bounds";
- private static final String PROPNAME_TRANSLATE_X = "android:epicenterReveal:translateX";
- private static final String PROPNAME_TRANSLATE_Y = "android:epicenterReveal:translateY";
- private static final String PROPNAME_TRANSLATE_Z = "android:epicenterReveal:translateZ";
- private static final String PROPNAME_Z = "android:epicenterReveal:z";
-
- private final TimeInterpolator mInterpolatorX;
- private final TimeInterpolator mInterpolatorY;
- private final TimeInterpolator mInterpolatorZ;
-
- public EpicenterTranslate() {
- mInterpolatorX = null;
- mInterpolatorY = null;
- mInterpolatorZ = null;
- }
-
- public EpicenterTranslate(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- final TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.EpicenterTranslate, 0, 0);
-
- final int interpolatorX = a.getResourceId(R.styleable.EpicenterTranslate_interpolatorX, 0);
- if (interpolatorX != 0) {
- mInterpolatorX = AnimationUtils.loadInterpolator(context, interpolatorX);
- } else {
- mInterpolatorX = TransitionConstants.FAST_OUT_SLOW_IN;
- }
-
- final int interpolatorY = a.getResourceId(R.styleable.EpicenterTranslate_interpolatorY, 0);
- if (interpolatorY != 0) {
- mInterpolatorY = AnimationUtils.loadInterpolator(context, interpolatorY);
- } else {
- mInterpolatorY = TransitionConstants.FAST_OUT_SLOW_IN;
- }
-
- final int interpolatorZ = a.getResourceId(R.styleable.EpicenterTranslate_interpolatorZ, 0);
- if (interpolatorZ != 0) {
- mInterpolatorZ = AnimationUtils.loadInterpolator(context, interpolatorZ);
- } else {
- mInterpolatorZ = TransitionConstants.FAST_OUT_SLOW_IN;
- }
-
- a.recycle();
- }
-
- @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 bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
- values.values.put(PROPNAME_BOUNDS, bounds);
- values.values.put(PROPNAME_TRANSLATE_X, view.getTranslationX());
- values.values.put(PROPNAME_TRANSLATE_Y, view.getTranslationY());
- values.values.put(PROPNAME_TRANSLATE_Z, view.getTranslationZ());
- values.values.put(PROPNAME_Z, view.getZ());
- }
-
- @Override
- public Animator onAppear(ViewGroup sceneRoot, View view,
- TransitionValues startValues, TransitionValues endValues) {
- if (endValues == null) {
- return null;
- }
-
- final Rect end = (Rect) endValues.values.get(PROPNAME_BOUNDS);
- final Rect start = getEpicenterOrCenter(end);
- final float startX = start.centerX() - end.centerX();
- final float startY = start.centerY() - end.centerY();
- final float startZ = 0 - (float) endValues.values.get(PROPNAME_Z);
-
- // Translate the view to be centered on the epicenter.
- view.setTranslationX(startX);
- view.setTranslationY(startY);
- view.setTranslationZ(startZ);
-
- final float endX = (float) endValues.values.get(PROPNAME_TRANSLATE_X);
- final float endY = (float) endValues.values.get(PROPNAME_TRANSLATE_Y);
- final float endZ = (float) endValues.values.get(PROPNAME_TRANSLATE_Z);
- return createAnimator(view, startX, startY, startZ, endX, endY, endZ,
- mInterpolatorX, mInterpolatorY, mInterpolatorZ);
- }
-
- @Override
- public Animator onDisappear(ViewGroup sceneRoot, View view,
- TransitionValues startValues, TransitionValues endValues) {
- if (startValues == null) {
- return null;
- }
-
- final Rect start = (Rect) endValues.values.get(PROPNAME_BOUNDS);
- final Rect end = getEpicenterOrCenter(start);
- final float endX = end.centerX() - start.centerX();
- final float endY = end.centerY() - start.centerY();
- final float endZ = 0 - (float) startValues.values.get(PROPNAME_Z);
-
- final float startX = (float) endValues.values.get(PROPNAME_TRANSLATE_X);
- final float startY = (float) endValues.values.get(PROPNAME_TRANSLATE_Y);
- final float startZ = (float) endValues.values.get(PROPNAME_TRANSLATE_Z);
- return createAnimator(view, startX, startY, startZ, endX, endY, endZ,
- mInterpolatorX, mInterpolatorY, mInterpolatorZ);
- }
-
- private Rect getEpicenterOrCenter(Rect bestRect) {
- final Rect epicenter = getEpicenter();
- if (epicenter != null) {
- return epicenter;
- }
-
- final int centerX = bestRect.centerX();
- final int centerY = bestRect.centerY();
- return new Rect(centerX, centerY, centerX, centerY);
- }
-
- private static Animator createAnimator(final View view, float startX, float startY,
- float startZ, float endX, float endY, float endZ, TimeInterpolator interpolatorX,
- TimeInterpolator interpolatorY, TimeInterpolator interpolatorZ) {
- final ObjectAnimator animX = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, startX, endX);
- if (interpolatorX != null) {
- animX.setInterpolator(interpolatorX);
- }
-
- final ObjectAnimator animY = ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, startY, endY);
- if (interpolatorY != null) {
- animY.setInterpolator(interpolatorY);
- }
-
- final ObjectAnimator animZ = ObjectAnimator.ofFloat(view, View.TRANSLATION_Z, startZ, endZ);
- if (interpolatorZ != null) {
- animZ.setInterpolator(interpolatorZ);
- }
-
- final AnimatorSet animSet = new AnimatorSet();
- animSet.playTogether(animX, animY, animZ);
- return animSet;
- }
-}
diff --git a/core/java/com/android/internal/transition/EpicenterTranslateClipReveal.java b/core/java/com/android/internal/transition/EpicenterTranslateClipReveal.java
new file mode 100644
index 0000000..2c10297
--- /dev/null
+++ b/core/java/com/android/internal/transition/EpicenterTranslateClipReveal.java
@@ -0,0 +1,328 @@
+/*
+ * 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.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.animation.TypeEvaluator;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.transition.TransitionValues;
+import android.transition.Visibility;
+import android.util.AttributeSet;
+import android.util.Property;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
+
+import com.android.internal.R;
+
+/**
+ * EpicenterTranslateClipReveal captures the clip bounds and translation values
+ * before and after the scene change and animates between those and the
+ * epicenter bounds during a visibility transition.
+ */
+public class EpicenterTranslateClipReveal extends Visibility {
+ private static final String PROPNAME_CLIP = "android:epicenterReveal:clip";
+ private static final String PROPNAME_BOUNDS = "android:epicenterReveal:bounds";
+ private static final String PROPNAME_TRANSLATE_X = "android:epicenterReveal:translateX";
+ private static final String PROPNAME_TRANSLATE_Y = "android:epicenterReveal:translateY";
+ private static final String PROPNAME_TRANSLATE_Z = "android:epicenterReveal:translateZ";
+ private static final String PROPNAME_Z = "android:epicenterReveal:z";
+
+ private final TimeInterpolator mInterpolatorX;
+ private final TimeInterpolator mInterpolatorY;
+ private final TimeInterpolator mInterpolatorZ;
+
+ public EpicenterTranslateClipReveal() {
+ mInterpolatorX = null;
+ mInterpolatorY = null;
+ mInterpolatorZ = null;
+ }
+
+ public EpicenterTranslateClipReveal(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.EpicenterTranslateClipReveal, 0, 0);
+
+ final int interpolatorX = a.getResourceId(
+ R.styleable.EpicenterTranslateClipReveal_interpolatorX, 0);
+ if (interpolatorX != 0) {
+ mInterpolatorX = AnimationUtils.loadInterpolator(context, interpolatorX);
+ } else {
+ mInterpolatorX = TransitionConstants.LINEAR_OUT_SLOW_IN;
+ }
+
+ final int interpolatorY = a.getResourceId(
+ R.styleable.EpicenterTranslateClipReveal_interpolatorY, 0);
+ if (interpolatorY != 0) {
+ mInterpolatorY = AnimationUtils.loadInterpolator(context, interpolatorY);
+ } else {
+ mInterpolatorY = TransitionConstants.FAST_OUT_SLOW_IN;
+ }
+
+ final int interpolatorZ = a.getResourceId(
+ R.styleable.EpicenterTranslateClipReveal_interpolatorZ, 0);
+ if (interpolatorZ != 0) {
+ mInterpolatorZ = AnimationUtils.loadInterpolator(context, interpolatorZ);
+ } else {
+ mInterpolatorZ = TransitionConstants.FAST_OUT_SLOW_IN;
+ }
+
+ a.recycle();
+ }
+
+ @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 bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
+ values.values.put(PROPNAME_BOUNDS, bounds);
+ values.values.put(PROPNAME_TRANSLATE_X, view.getTranslationX());
+ values.values.put(PROPNAME_TRANSLATE_Y, view.getTranslationY());
+ values.values.put(PROPNAME_TRANSLATE_Z, view.getTranslationZ());
+ values.values.put(PROPNAME_Z, view.getZ());
+
+ final Rect clip = view.getClipBounds();
+ values.values.put(PROPNAME_CLIP, clip);
+ }
+
+ @Override
+ public Animator onAppear(ViewGroup sceneRoot, View view,
+ TransitionValues startValues, TransitionValues endValues) {
+ if (endValues == null) {
+ return null;
+ }
+
+ final Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
+ final Rect startBounds = getEpicenterOrCenter(endBounds);
+ final float startX = startBounds.centerX() - endBounds.centerX();
+ final float startY = startBounds.centerY() - endBounds.centerY();
+ final float startZ = 0 - (float) endValues.values.get(PROPNAME_Z);
+
+ // Translate the view to be centered on the epicenter.
+ view.setTranslationX(startX);
+ view.setTranslationY(startY);
+ view.setTranslationZ(startZ);
+
+ final float endX = (float) endValues.values.get(PROPNAME_TRANSLATE_X);
+ final float endY = (float) endValues.values.get(PROPNAME_TRANSLATE_Y);
+ final float endZ = (float) endValues.values.get(PROPNAME_TRANSLATE_Z);
+
+ final Rect endClip = getBestRect(endValues);
+ final Rect startClip = getEpicenterOrCenter(endClip);
+
+ // Prepare the view.
+ view.setClipBounds(startClip);
+
+ final State startStateX = new State(startClip.left, startClip.right, startX);
+ final State endStateX = new State(endClip.left, endClip.right, endX);
+ final State startStateY = new State(startClip.top, startClip.bottom, startY);
+ final State endStateY = new State(endClip.top, endClip.bottom, endY);
+
+ return createRectAnimator(view, startStateX, startStateY, startZ, endStateX, endStateY,
+ endZ, endValues, mInterpolatorX, mInterpolatorY, mInterpolatorZ);
+ }
+
+ @Override
+ public Animator onDisappear(ViewGroup sceneRoot, View view,
+ TransitionValues startValues, TransitionValues endValues) {
+ if (startValues == null) {
+ return null;
+ }
+
+ final Rect startBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
+ final Rect endBounds = getEpicenterOrCenter(startBounds);
+ final float endX = endBounds.centerX() - startBounds.centerX();
+ final float endY = endBounds.centerY() - startBounds.centerY();
+ final float endZ = 0 - (float) startValues.values.get(PROPNAME_Z);
+
+ final float startX = (float) endValues.values.get(PROPNAME_TRANSLATE_X);
+ final float startY = (float) endValues.values.get(PROPNAME_TRANSLATE_Y);
+ final float startZ = (float) endValues.values.get(PROPNAME_TRANSLATE_Z);
+
+ final Rect startClip = getBestRect(startValues);
+ final Rect endClip = getEpicenterOrCenter(startClip);
+
+ // Prepare the view.
+ view.setClipBounds(startClip);
+
+ final State startStateX = new State(startClip.left, startClip.right, startX);
+ final State endStateX = new State(endClip.left, endClip.right, endX);
+ final State startStateY = new State(startClip.top, startClip.bottom, startY);
+ final State endStateY = new State(endClip.top, endClip.bottom, endY);
+
+ return createRectAnimator(view, startStateX, startStateY, startZ, endStateX, endStateY,
+ endZ, endValues, mInterpolatorX, mInterpolatorY, mInterpolatorZ);
+ }
+
+ private Rect getEpicenterOrCenter(Rect bestRect) {
+ final Rect epicenter = getEpicenter();
+ if (epicenter != null) {
+ return epicenter;
+ }
+
+ final int centerX = bestRect.centerX();
+ final int centerY = bestRect.centerY();
+ return new Rect(centerX, centerY, centerX, centerY);
+ }
+
+ 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 static Animator createRectAnimator(final View view, State startX, State startY,
+ float startZ, State endX, State endY, float endZ, TransitionValues endValues,
+ TimeInterpolator interpolatorX, TimeInterpolator interpolatorY,
+ TimeInterpolator interpolatorZ) {
+ final StateEvaluator evaluator = new StateEvaluator();
+
+ final ObjectAnimator animZ = ObjectAnimator.ofFloat(view, View.TRANSLATION_Z, startZ, endZ);
+ if (interpolatorZ != null) {
+ animZ.setInterpolator(interpolatorZ);
+ }
+
+ final StateProperty propX = new StateProperty(StateProperty.TARGET_X);
+ final ObjectAnimator animX = ObjectAnimator.ofObject(view, propX, evaluator, startX, endX);
+ if (interpolatorX != null) {
+ animX.setInterpolator(interpolatorX);
+ }
+
+ final StateProperty propY = new StateProperty(StateProperty.TARGET_Y);
+ final ObjectAnimator animY = ObjectAnimator.ofObject(view, propY, evaluator, startY, endY);
+ if (interpolatorY != null) {
+ animY.setInterpolator(interpolatorY);
+ }
+
+ final Rect terminalClip = (Rect) endValues.values.get(PROPNAME_CLIP);
+ final AnimatorListenerAdapter animatorListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setClipBounds(terminalClip);
+ }
+ };
+
+ final AnimatorSet animSet = new AnimatorSet();
+ animSet.playTogether(animX, animY, animZ);
+ animSet.addListener(animatorListener);
+ return animSet;
+ }
+
+ private static class State {
+ int lower;
+ int upper;
+ float trans;
+
+ public State() {}
+
+ public State(int lower, int upper, float trans) {
+ this.lower = lower;
+ this.upper = upper;
+ this.trans = trans;
+ }
+ }
+
+ private static class StateEvaluator implements TypeEvaluator<State> {
+ private final State mTemp = new State();
+
+ @Override
+ public State evaluate(float fraction, State startValue, State endValue) {
+ mTemp.upper = startValue.upper + (int) ((endValue.upper - startValue.upper) * fraction);
+ mTemp.lower = startValue.lower + (int) ((endValue.lower - startValue.lower) * fraction);
+ mTemp.trans = startValue.trans + (int) ((endValue.trans - startValue.trans) * fraction);
+ return mTemp;
+ }
+ }
+
+ private static class StateProperty extends Property<View, State> {
+ public static final char TARGET_X = 'x';
+ public static final char TARGET_Y = 'y';
+
+ private final Rect mTempRect = new Rect();
+ private final State mTempState = new State();
+
+ private final int mTargetDimension;
+
+ public StateProperty(char targetDimension) {
+ super(State.class, "state_" + targetDimension);
+
+ mTargetDimension = targetDimension;
+ }
+
+ @Override
+ public State get(View object) {
+ final Rect tempRect = mTempRect;
+ if (!object.getClipBounds(tempRect)) {
+ tempRect.setEmpty();
+ }
+ final State tempState = mTempState;
+ if (mTargetDimension == TARGET_X) {
+ tempState.trans = object.getTranslationX();
+ tempState.lower = tempRect.left + (int) tempState.trans;
+ tempState.upper = tempRect.right + (int) tempState.trans;
+ } else {
+ tempState.trans = object.getTranslationY();
+ tempState.lower = tempRect.top + (int) tempState.trans;
+ tempState.upper = tempRect.bottom + (int) tempState.trans;
+ }
+ return tempState;
+ }
+
+ @Override
+ public void set(View object, State value) {
+ final Rect tempRect = mTempRect;
+ if (object.getClipBounds(tempRect)) {
+ if (mTargetDimension == TARGET_X) {
+ tempRect.left = value.lower - (int) value.trans;
+ tempRect.right = value.upper - (int) value.trans;
+ } else {
+ tempRect.top = value.lower - (int) value.trans;
+ tempRect.bottom = value.upper - (int) value.trans;
+ }
+ object.setClipBounds(tempRect);
+ }
+
+ if (mTargetDimension == TARGET_X) {
+ object.setTranslationX(value.trans);
+ } else {
+ object.setTranslationY(value.trans);
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
index 34f62ba..bd0e6ce 100644
--- a/core/java/com/android/internal/util/AsyncChannel.java
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -462,10 +462,8 @@
} catch(Exception e) {
}
// Tell source we're disconnected.
- if (mSrcHandler != null) {
- replyDisconnected(STATUS_SUCCESSFUL);
- mSrcHandler = null;
- }
+ replyDisconnected(STATUS_SUCCESSFUL);
+ mSrcHandler = null;
// Unlink only when bindService isn't used
if (mConnection == null && mDstMessenger != null && mDeathMonitor!= null) {
mDstMessenger.getBinder().unlinkToDeath(mDeathMonitor, 0);
@@ -871,6 +869,8 @@
* @param status to be stored in msg.arg1
*/
private void replyDisconnected(int status) {
+ // Can't reply if already disconnected. Avoid NullPointerException.
+ if (mSrcHandler == null) return;
Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_DISCONNECTED);
msg.arg1 = status;
msg.obj = this;
diff --git a/core/java/com/android/internal/util/CallbackRegistry.java b/core/java/com/android/internal/util/CallbackRegistry.java
new file mode 100644
index 0000000..0f228d4
--- /dev/null
+++ b/core/java/com/android/internal/util/CallbackRegistry.java
@@ -0,0 +1,395 @@
+/*
+ * 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.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tracks callbacks for the event. This class supports reentrant modification
+ * of the callbacks during notification without adversely disrupting notifications.
+ * A common pattern for callbacks is to receive a notification and then remove
+ * themselves. This class handles this behavior with constant memory under
+ * most circumstances.
+ *
+ * <p>A subclass of {@link CallbackRegistry.NotifierCallback} must be passed to
+ * the constructor to define how notifications should be called. That implementation
+ * does the actual notification on the listener.</p>
+ *
+ * <p>This class supports only callbacks with at most two parameters.
+ * Typically, these are the notification originator and a parameter, but these may
+ * be used as required. If more than two parameters are required or primitive types
+ * must be used, <code>A</code> should be some kind of containing structure that
+ * the subclass may reuse between notifications.</p>
+ *
+ * @param <C> The callback type.
+ * @param <T> The notification sender type. Typically this is the containing class.
+ * @param <A> Opaque argument used to pass additional data beyond an int.
+ */
+public class CallbackRegistry<C, T, A> implements Cloneable {
+ private static final String TAG = "CallbackRegistry";
+
+ /** An ordered collection of listeners waiting to be notified. */
+ private List<C> mCallbacks = new ArrayList<C>();
+
+ /**
+ * A bit flag for the first 64 listeners that are removed during notification.
+ * The lowest significant bit corresponds to the 0th index into mCallbacks.
+ * For a small number of callbacks, no additional array of objects needs to
+ * be allocated.
+ */
+ private long mFirst64Removed = 0x0;
+
+ /**
+ * Bit flags for the remaining callbacks that are removed during notification.
+ * When there are more than 64 callbacks and one is marked for removal, a dynamic
+ * array of bits are allocated for the callbacks.
+ */
+ private long[] mRemainderRemoved;
+
+ /**
+ * The reentrancy level of the notification. When we notify a callback, it may cause
+ * further notifications. The reentrancy level must be tracked to let us clean up
+ * the callback state when all notifications have been processed.
+ */
+ private int mNotificationLevel;
+
+ /** The notification mechanism for notifying an event. */
+ private final NotifierCallback<C, T, A> mNotifier;
+
+ /**
+ * Creates an EventRegistry that notifies the event with notifier.
+ * @param notifier The class to use to notify events.
+ */
+ public CallbackRegistry(NotifierCallback<C, T, A> notifier) {
+ mNotifier = notifier;
+ }
+
+ /**
+ * Notify all callbacks.
+ *
+ * @param sender The originator. This is an opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ * @param arg An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ * @param arg2 An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ */
+ public synchronized void notifyCallbacks(T sender, int arg, A arg2) {
+ mNotificationLevel++;
+ notifyRecurseLocked(sender, arg, arg2);
+ mNotificationLevel--;
+ if (mNotificationLevel == 0) {
+ if (mRemainderRemoved != null) {
+ for (int i = mRemainderRemoved.length - 1; i >= 0; i--) {
+ final long removedBits = mRemainderRemoved[i];
+ if (removedBits != 0) {
+ removeRemovedCallbacks((i + 1) * Long.SIZE, removedBits);
+ mRemainderRemoved[i] = 0;
+ }
+ }
+ }
+ if (mFirst64Removed != 0) {
+ removeRemovedCallbacks(0, mFirst64Removed);
+ mFirst64Removed = 0;
+ }
+ }
+ }
+
+ /**
+ * Notify up to the first Long.SIZE callbacks that don't have a bit set in <code>removed</code>.
+ *
+ * @param sender The originator. This is an opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ * @param arg An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ * @param arg2 An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ */
+ private void notifyFirst64Locked(T sender, int arg, A arg2) {
+ final int maxNotified = Math.min(Long.SIZE, mCallbacks.size());
+ notifyCallbacksLocked(sender, arg, arg2, 0, maxNotified, mFirst64Removed);
+ }
+
+ /**
+ * Notify all callbacks using a recursive algorithm to avoid allocating on the heap.
+ * This part captures the callbacks beyond Long.SIZE that have no bits allocated for
+ * removal before it recurses into {@link #notifyRemainderLocked(Object, int, A, int)}.
+ * <p>
+ * Recursion is used to avoid allocating temporary state on the heap. Each stack has one
+ * long (64 callbacks) worth of information of which has been removed.
+ *
+ * @param sender The originator. This is an opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ * @param arg An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ * @param arg2 An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ */
+ private void notifyRecurseLocked(T sender, int arg, A arg2) {
+ final int callbackCount = mCallbacks.size();
+ final int remainderIndex = mRemainderRemoved == null ? -1 : mRemainderRemoved.length - 1;
+
+ // Now we've got all callbacks that have no mRemainderRemoved value, so notify the
+ // others.
+ notifyRemainderLocked(sender, arg, arg2, remainderIndex);
+
+ // notifyRemainderLocked notifies all at maxIndex, so we'd normally start at maxIndex + 1
+ // However, we must also keep track of those in mFirst64Removed, so we add 2 instead:
+ final int startCallbackIndex = (remainderIndex + 2) * Long.SIZE;
+
+ // The remaining have no bit set
+ notifyCallbacksLocked(sender, arg, arg2, startCallbackIndex, callbackCount, 0);
+ }
+
+ /**
+ * Notify callbacks that have mRemainderRemoved bits set for remainderIndex. If
+ * remainderIndex is -1, the first 64 will be notified instead.
+ *
+ * @param sender The originator. This is an opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ * @param arg An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ * @param arg2 An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ * @param remainderIndex The index into mRemainderRemoved that should be notified.
+ */
+ private void notifyRemainderLocked(T sender, int arg, A arg2, int remainderIndex) {
+ if (remainderIndex < 0) {
+ notifyFirst64Locked(sender, arg, arg2);
+ } else {
+ final long bits = mRemainderRemoved[remainderIndex];
+ final int startIndex = (remainderIndex + 1) * Long.SIZE;
+ final int endIndex = Math.min(mCallbacks.size(), startIndex + Long.SIZE);
+ notifyRemainderLocked(sender, arg, arg2, remainderIndex - 1);
+ notifyCallbacksLocked(sender, arg, arg2, startIndex, endIndex, bits);
+ }
+ }
+
+ /**
+ * Notify callbacks from startIndex to endIndex, using bits as the bit status
+ * for whether they have been removed or not. bits should be from mRemainderRemoved or
+ * mFirst64Removed. bits set to 0 indicates that all callbacks from startIndex to
+ * endIndex should be notified.
+ *
+ * @param sender The originator. This is an opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ * @param arg An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ * @param arg2 An opaque parameter passed to
+ * {@link CallbackRegistry.NotifierCallback#onNotifyCallback(Object, Object, int, A)}
+ * @param startIndex The index into the mCallbacks to start notifying.
+ * @param endIndex One past the last index into mCallbacks to notify.
+ * @param bits A bit field indicating which callbacks have been removed and shouldn't
+ * be notified.
+ */
+ private void notifyCallbacksLocked(T sender, int arg, A arg2, final int startIndex,
+ final int endIndex, final long bits) {
+ long bitMask = 1;
+ for (int i = startIndex; i < endIndex; i++) {
+ if ((bits & bitMask) == 0) {
+ mNotifier.onNotifyCallback(mCallbacks.get(i), sender, arg, arg2);
+ }
+ bitMask <<= 1;
+ }
+ }
+
+ /**
+ * Add a callback to be notified. If the callback is already in the list, another won't
+ * be added. This does not affect current notifications.
+ * @param callback The callback to add.
+ */
+ public synchronized void add(C callback) {
+ int index = mCallbacks.lastIndexOf(callback);
+ if (index < 0 || isRemovedLocked(index)) {
+ mCallbacks.add(callback);
+ }
+ }
+
+ /**
+ * Returns true if the callback at index has been marked for removal.
+ *
+ * @param index The index into mCallbacks to check.
+ * @return true if the callback at index has been marked for removal.
+ */
+ private boolean isRemovedLocked(int index) {
+ if (index < Long.SIZE) {
+ // It is in the first 64 callbacks, just check the bit.
+ final long bitMask = 1L << index;
+ return (mFirst64Removed & bitMask) != 0;
+ } else if (mRemainderRemoved == null) {
+ // It is after the first 64 callbacks, but nothing else was marked for removal.
+ return false;
+ } else {
+ final int maskIndex = (index / Long.SIZE) - 1;
+ if (maskIndex >= mRemainderRemoved.length) {
+ // There are some items in mRemainderRemoved, but nothing at the given index.
+ return false;
+ } else {
+ // There is something marked for removal, so we have to check the bit.
+ final long bits = mRemainderRemoved[maskIndex];
+ final long bitMask = 1L << (index % Long.SIZE);
+ return (bits & bitMask) != 0;
+ }
+ }
+ }
+
+ /**
+ * Removes callbacks from startIndex to startIndex + Long.SIZE, based
+ * on the bits set in removed.
+ * @param startIndex The index into the mCallbacks to start removing callbacks.
+ * @param removed The bits indicating removal, where each bit is set for one callback
+ * to be removed.
+ */
+ private void removeRemovedCallbacks(int startIndex, long removed) {
+ // The naive approach should be fine. There may be a better bit-twiddling approach.
+ final int endIndex = startIndex + Long.SIZE;
+
+ long bitMask = 1L << (Long.SIZE - 1);
+ for (int i = endIndex - 1; i >= startIndex; i--) {
+ if ((removed & bitMask) != 0) {
+ mCallbacks.remove(i);
+ }
+ bitMask >>>= 1;
+ }
+ }
+
+ /**
+ * Remove a callback. This callback won't be notified after this call completes.
+ * @param callback The callback to remove.
+ */
+ public synchronized void remove(C callback) {
+ if (mNotificationLevel == 0) {
+ mCallbacks.remove(callback);
+ } else {
+ int index = mCallbacks.lastIndexOf(callback);
+ if (index >= 0) {
+ setRemovalBitLocked(index);
+ }
+ }
+ }
+
+ private void setRemovalBitLocked(int index) {
+ if (index < Long.SIZE) {
+ // It is in the first 64 callbacks, just check the bit.
+ final long bitMask = 1L << index;
+ mFirst64Removed |= bitMask;
+ } else {
+ final int remainderIndex = (index / Long.SIZE) - 1;
+ if (mRemainderRemoved == null) {
+ mRemainderRemoved = new long[mCallbacks.size() / Long.SIZE];
+ } else if (mRemainderRemoved.length < remainderIndex) {
+ // need to make it bigger
+ long[] newRemainders = new long[mCallbacks.size() / Long.SIZE];
+ System.arraycopy(mRemainderRemoved, 0, newRemainders, 0, mRemainderRemoved.length);
+ mRemainderRemoved = newRemainders;
+ }
+ final long bitMask = 1L << (index % Long.SIZE);
+ mRemainderRemoved[remainderIndex] |= bitMask;
+ }
+ }
+
+ /**
+ * Makes a copy of the registered callbacks and returns it.
+ *
+ * @return a copy of the registered callbacks.
+ */
+ public synchronized ArrayList<C> copyListeners() {
+ ArrayList<C> callbacks = new ArrayList<C>(mCallbacks.size());
+ int numListeners = mCallbacks.size();
+ for (int i = 0; i < numListeners; i++) {
+ if (!isRemovedLocked(i)) {
+ callbacks.add(mCallbacks.get(i));
+ }
+ }
+ return callbacks;
+ }
+
+ /**
+ * Returns true if there are no registered callbacks or false otherwise.
+ *
+ * @return true if there are no registered callbacks or false otherwise.
+ */
+ public synchronized boolean isEmpty() {
+ if (mCallbacks.isEmpty()) {
+ return true;
+ } else if (mNotificationLevel == 0) {
+ return false;
+ } else {
+ int numListeners = mCallbacks.size();
+ for (int i = 0; i < numListeners; i++) {
+ if (!isRemovedLocked(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Removes all callbacks from the list.
+ */
+ public synchronized void clear() {
+ if (mNotificationLevel == 0) {
+ mCallbacks.clear();
+ } else if (!mCallbacks.isEmpty()) {
+ for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+ setRemovalBitLocked(i);
+ }
+ }
+ }
+
+ public synchronized CallbackRegistry<C, T, A> clone() {
+ CallbackRegistry<C, T, A> clone = null;
+ try {
+ clone = (CallbackRegistry<C, T, A>) super.clone();
+ clone.mFirst64Removed = 0;
+ clone.mRemainderRemoved = null;
+ clone.mNotificationLevel = 0;
+ clone.mCallbacks = new ArrayList<C>();
+ final int numListeners = mCallbacks.size();
+ for (int i = 0; i < numListeners; i++) {
+ if (!isRemovedLocked(i)) {
+ clone.mCallbacks.add(mCallbacks.get(i));
+ }
+ }
+ } catch (CloneNotSupportedException e) {
+ e.printStackTrace();
+ }
+ return clone;
+ }
+
+ /**
+ * Class used to notify events from CallbackRegistry.
+ *
+ * @param <C> The callback type.
+ * @param <T> The notification sender type. Typically this is the containing class.
+ * @param <A> An opaque argument to pass to the notifier
+ */
+ public abstract static class NotifierCallback<C, T, A> {
+ /**
+ * Used to notify the callback.
+ *
+ * @param callback The callback to notify.
+ * @param sender The opaque sender object.
+ * @param arg The opaque notification parameter.
+ * @param arg2 An opaque argument passed in
+ * {@link CallbackRegistry#notifyCallbacks}
+ * @see CallbackRegistry#CallbackRegistry(CallbackRegistry.NotifierCallback)
+ */
+ public abstract void onNotifyCallback(C callback, T sender, int arg, A arg2);
+ }
+}
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 8ae2e3b..2785c48 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -188,18 +188,43 @@
return mPixelRef->rowBytes();
}
-SkPixelRef* Bitmap::pixelRef() const {
+SkPixelRef* Bitmap::peekAtPixelRef() const {
assertValid();
return mPixelRef.get();
}
+SkPixelRef* Bitmap::refPixelRef() {
+ assertValid();
+ android::AutoMutex _lock(mLock);
+ return refPixelRefLocked();
+}
+
+SkPixelRef* Bitmap::refPixelRefLocked() {
+ mPixelRef->ref();
+ if (mPixelRef->unique()) {
+ // We just restored this from 0, pin the pixels and inc the strong count
+ // Note that there *might be* an incoming onStrongRefDestroyed from whatever
+ // last unref'd
+ pinPixelsLocked();
+ mPinnedRefCount++;
+ }
+ return mPixelRef.get();
+}
+
void Bitmap::reconfigure(const SkImageInfo& info, size_t rowBytes,
SkColorTable* ctable) {
+ {
+ android::AutoMutex _lock(mLock);
+ if (mPinnedRefCount) {
+ ALOGW("Called reconfigure on a bitmap that is in use! This may"
+ " cause graphical corruption!");
+ }
+ }
mPixelRef->reconfigure(info, rowBytes, ctable);
}
void Bitmap::reconfigure(const SkImageInfo& info) {
- mPixelRef->reconfigure(info, mPixelRef->rowBytes(), mPixelRef->colorTable());
+ reconfigure(info, info.minRowBytes(), nullptr);
}
void Bitmap::detachFromJava() {
@@ -287,18 +312,10 @@
void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
assertValid();
android::AutoMutex _lock(mLock);
- mPixelRef->ref();
- if (mPixelRef->unique()) {
- // We just restored this from 0, pin the pixels and inc the strong count
- // Note that there *might be* an incoming onStrongRefDestroyed from whatever
- // last unref'd
- pinPixelsLocked();
- mPinnedRefCount++;
- }
// Safe because mPixelRef is a WrappedPixelRef type, otherwise rowBytes()
// would require locking the pixels first.
outBitmap->setInfo(mPixelRef->info(), mPixelRef->rowBytes());
- outBitmap->setPixelRef(mPixelRef.get())->unref();
+ outBitmap->setPixelRef(refPixelRefLocked())->unref();
outBitmap->setHasHardwareMipMap(hasHardwareMipMap());
}
@@ -323,7 +340,7 @@
}
void* pixels() {
- return mBitmap->pixelRef()->pixels();
+ return mBitmap->peekAtPixelRef()->pixels();
}
bool valid() {
@@ -780,7 +797,7 @@
static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) {
LocalScopedBitmap bitmap(bitmapHandle);
- return static_cast<jint>(bitmap->pixelRef()->getGenerationID());
+ return static_cast<jint>(bitmap->peekAtPixelRef()->getGenerationID());
}
static jboolean Bitmap_isPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle) {
@@ -800,10 +817,10 @@
jboolean hasAlpha, jboolean requestPremul) {
LocalScopedBitmap bitmap(bitmapHandle);
if (hasAlpha) {
- bitmap->pixelRef()->changeAlphaType(
+ bitmap->peekAtPixelRef()->changeAlphaType(
requestPremul ? kPremul_SkAlphaType : kUnpremul_SkAlphaType);
} else {
- bitmap->pixelRef()->changeAlphaType(kOpaque_SkAlphaType);
+ bitmap->peekAtPixelRef()->changeAlphaType(kOpaque_SkAlphaType);
}
}
@@ -812,9 +829,9 @@
LocalScopedBitmap bitmap(bitmapHandle);
if (!bitmap->info().isOpaque()) {
if (isPremul) {
- bitmap->pixelRef()->changeAlphaType(kPremul_SkAlphaType);
+ bitmap->peekAtPixelRef()->changeAlphaType(kPremul_SkAlphaType);
} else {
- bitmap->pixelRef()->changeAlphaType(kUnpremul_SkAlphaType);
+ bitmap->peekAtPixelRef()->changeAlphaType(kUnpremul_SkAlphaType);
}
}
}
@@ -1164,7 +1181,7 @@
static jlong Bitmap_refPixelRef(JNIEnv* env, jobject, jlong bitmapHandle) {
LocalScopedBitmap bitmap(bitmapHandle);
- SkPixelRef* pixelRef = bitmap.valid() ? bitmap->pixelRef() : nullptr;
+ SkPixelRef* pixelRef = bitmap.valid() ? bitmap->peekAtPixelRef() : nullptr;
SkSafeRef(pixelRef);
return reinterpret_cast<jlong>(pixelRef);
}
diff --git a/core/jni/android/graphics/Bitmap.h b/core/jni/android/graphics/Bitmap.h
index d6e5c61..efeb898 100644
--- a/core/jni/android/graphics/Bitmap.h
+++ b/core/jni/android/graphics/Bitmap.h
@@ -62,7 +62,8 @@
int width() const { return info().width(); }
int height() const { return info().height(); }
size_t rowBytes() const;
- SkPixelRef* pixelRef() const;
+ SkPixelRef* peekAtPixelRef() const;
+ SkPixelRef* refPixelRef();
bool valid() const { return mPixelStorageType != PixelStorageType::Invalid; }
void reconfigure(const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
@@ -88,6 +89,7 @@
JNIEnv* jniEnv();
bool shouldDisposeSelfLocked();
void assertValid() const;
+ SkPixelRef* refPixelRefLocked();
android::Mutex mLock;
int mPinnedRefCount = 0;
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index cdd397d..3ca4e72 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -184,7 +184,7 @@
}
mBitmap->reconfigure(info, bitmap->rowBytes(), ctable);
- bitmap->setPixelRef(mBitmap->pixelRef());
+ bitmap->setPixelRef(mBitmap->refPixelRef())->unref();
// since we're already allocated, we lockPixels right away
// HeapAllocator/JavaPixelAllocator behaves this way too
@@ -258,7 +258,7 @@
unsigned int existingBufferSize = 0;
if (javaBitmap != NULL) {
reuseBitmap = GraphicsJNI::getBitmap(env, javaBitmap);
- if (reuseBitmap->pixelRef()->isImmutable()) {
+ if (reuseBitmap->peekAtPixelRef()->isImmutable()) {
ALOGW("Unable to reuse an immutable bitmap as an image decoder target.");
javaBitmap = NULL;
reuseBitmap = nullptr;
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 0deb8cc..1c6f7de 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -352,8 +352,8 @@
getBitmap(env, bitmap)->getSkBitmap(outBitmap);
}
-SkPixelRef* GraphicsJNI::getSkPixelRef(JNIEnv* env, jobject bitmap) {
- return getBitmap(env, bitmap)->pixelRef();
+SkPixelRef* GraphicsJNI::refSkPixelRef(JNIEnv* env, jobject bitmap) {
+ return getBitmap(env, bitmap)->refPixelRef();
}
SkColorType GraphicsJNI::getNativeBitmapColorType(JNIEnv* env, jobject jconfig) {
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index e748bac..ef9c2a9 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -52,7 +52,7 @@
static android::Canvas* getNativeCanvas(JNIEnv*, jobject canvas);
static android::Bitmap* getBitmap(JNIEnv*, jobject bitmap);
static void getSkBitmap(JNIEnv*, jobject bitmap, SkBitmap* outBitmap);
- static SkPixelRef* getSkPixelRef(JNIEnv*, jobject bitmap);
+ static SkPixelRef* refSkPixelRef(JNIEnv*, jobject bitmap);
static SkRegion* getNativeRegion(JNIEnv*, jobject region);
// Given the 'native' long held by the Rasterizer.java object, return a
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index db495dd..74a9e4e 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -976,6 +976,12 @@
dest->setTo(*src);
}
+static void android_content_AssetManager_clearTheme(JNIEnv* env, jobject clazz, jlong themeHandle)
+{
+ ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeHandle);
+ theme->clear();
+}
+
static jint android_content_AssetManager_loadThemeAttributeValue(
JNIEnv* env, jobject clazz, jlong themeHandle, jint ident, jobject outValue, jboolean resolve)
{
@@ -2108,6 +2114,8 @@
(void*) android_content_AssetManager_applyThemeStyle },
{ "copyTheme", "(JJ)V",
(void*) android_content_AssetManager_copyTheme },
+ { "clearTheme", "(J)V",
+ (void*) android_content_AssetManager_clearTheme },
{ "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I",
(void*) android_content_AssetManager_loadThemeAttributeValue },
{ "getThemeChangingConfigurations", "(J)I",
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 1965cd3..77af341 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -180,7 +180,7 @@
(void*) screenshot->getPixels(), (void*) screenshot.get(), DeleteScreenshot,
screenshotInfo, rowBytes, nullptr);
screenshot.detach();
- bitmap->pixelRef()->setImmutable();
+ bitmap->peekAtPixelRef()->setImmutable();
return GraphicsJNI::createBitmap(env, bitmap,
GraphicsJNI::kBitmapCreateFlag_Premultiplied, NULL);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e3930cd..5669b91 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2352,11 +2352,6 @@
<permission android:name="android.permission.BIND_CONDITION_PROVIDER_SERVICE"
android:protectionLevel="signature" />
- <!-- Must be required by a {@link android.media.routing.MediaRouteService},
- to ensure that only the system can bind to it. -->
- <permission android:name="android.permission.BIND_MEDIA_ROUTE_SERVICE"
- android:protectionLevel="signature" />
-
<!-- Must be required by an {@link android.service.dreams.DreamService},
to ensure that only the system can bind to it. -->
<permission android:name="android.permission.BIND_DREAM_SERVICE"
diff --git a/core/res/res/drawable-hdpi/text_cursor_mtrl_alpha.9.png b/core/res/res/drawable-hdpi/text_cursor_mtrl_alpha.9.png
deleted file mode 100644
index 0179433..0000000
--- a/core/res/res/drawable-hdpi/text_cursor_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_cursor_mtrl_alpha.9.png b/core/res/res/drawable-mdpi/text_cursor_mtrl_alpha.9.png
deleted file mode 100644
index e5760be..0000000
--- a/core/res/res/drawable-mdpi/text_cursor_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/text_cursor_mtrl_alpha.9.png b/core/res/res/drawable-xhdpi/text_cursor_mtrl_alpha.9.png
deleted file mode 100644
index 3939214..0000000
--- a/core/res/res/drawable-xhdpi/text_cursor_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/text_cursor_mtrl_alpha.9.png b/core/res/res/drawable-xxhdpi/text_cursor_mtrl_alpha.9.png
deleted file mode 100644
index 432c385..0000000
--- a/core/res/res/drawable-xxhdpi/text_cursor_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/text_cursor_material.xml b/core/res/res/drawable/text_cursor_material.xml
index a350c47..0bedaa9 100644
--- a/core/res/res/drawable/text_cursor_material.xml
+++ b/core/res/res/drawable/text_cursor_material.xml
@@ -14,6 +14,15 @@
limitations under the License.
-->
-<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/text_cursor_mtrl_alpha"
- android:tint="?attr/colorControlActivated" />
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:inset="2dp">
+ <shape
+ android:tint="?attr/colorControlActivated"
+ android:shape="rectangle">
+ <size
+ android:height="2dp"
+ android:width="2dp" />
+ <solid
+ android:color="@color/white" />
+ </shape>
+</inset>
diff --git a/core/res/res/layout-land/time_picker_material.xml b/core/res/res/layout-land/time_picker_material.xml
index 89c3749..4b544d2 100644
--- a/core/res/res/layout-land/time_picker_material.xml
+++ b/core/res/res/layout-land/time_picker_material.xml
@@ -16,7 +16,6 @@
-->
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -27,8 +26,7 @@
android:layout_column="0"
android:layout_row="0"
android:layout_rowSpan="3"
- android:layout_gravity="center|fill"
- tools:background="@color/accent_material_light" />
+ android:layout_gravity="center|fill" />
<RelativeLayout
android:layout_width="wrap_content"
@@ -56,20 +54,14 @@
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
android:singleLine="true"
android:ellipsize="none"
- android:gravity="right"
- tools:text="23"
- tools:textSize="@dimen/timepicker_time_label_size"
- tools:textColor="@color/white" />
+ android:gravity="right" />
<TextView
android:id="@+id/separator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
- android:importantForAccessibility="no"
- tools:text=":"
- tools:textSize="@dimen/timepicker_time_label_size"
- tools:textColor="@color/white" />
+ android:importantForAccessibility="no" />
<!-- The minutes should always be to the right of the separator,
regardless of the current locale's layout direction. -->
@@ -80,10 +72,7 @@
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
android:singleLine="true"
android:ellipsize="none"
- android:gravity="left"
- tools:text="59"
- tools:textSize="@dimen/timepicker_time_label_size"
- tools:textColor="@color/white" />
+ android:gravity="left" />
</LinearLayout>
<!-- The layout alignment of this view will switch between toRightOf
@@ -106,10 +95,7 @@
android:paddingTop="@dimen/timepicker_am_top_padding"
android:lines="1"
android:ellipsize="none"
- android:includeFontPadding="false"
- tools:text="AM"
- tools:textSize="@dimen/timepicker_ampm_label_size"
- tools:textColor="@color/white" />
+ android:includeFontPadding="false" />
<CheckedTextView
android:id="@+id/pm_label"
@@ -121,10 +107,7 @@
android:paddingTop="@dimen/timepicker_pm_top_padding"
android:lines="1"
android:ellipsize="none"
- android:includeFontPadding="false"
- tools:text="PM"
- tools:textSize="@dimen/timepicker_ampm_label_size"
- tools:textColor="@color/white" />
+ android:includeFontPadding="false" />
</LinearLayout>
</RelativeLayout>
diff --git a/core/res/res/layout/date_picker_header_material.xml b/core/res/res/layout/date_picker_header_material.xml
index 2150341..a4388f6 100644
--- a/core/res/res/layout/date_picker_header_material.xml
+++ b/core/res/res/layout/date_picker_header_material.xml
@@ -16,17 +16,13 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/date_picker_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="18dp"
android:paddingStart="?attr/dialogPreferredPadding"
android:paddingEnd="?attr/dialogPreferredPadding"
- android:orientation="vertical"
- tools:background="@color/accent_material_light"
- tools:paddingStart="24dp"
- tools:paddingEnd="24dp">
+ android:orientation="vertical">
<!-- Top padding should stay on this view so that
the touch target is a bit larger. -->
@@ -35,10 +31,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="16dp"
- android:textAppearance="@style/TextAppearance.Material.DatePicker.YearLabel"
- tools:text="2015"
- tools:textSize="@dimen/date_picker_year_label_size"
- tools:textColor="@color/white" />
+ android:textAppearance="@style/TextAppearance.Material.DatePicker.YearLabel" />
<TextView
android:id="@+id/date_picker_header_date"
@@ -47,9 +40,6 @@
android:textAppearance="@style/TextAppearance.Material.DatePicker.DateLabel"
android:gravity="start"
android:maxLines="2"
- android:ellipsize="none"
- tools:text="Thu, Sep 30"
- tools:textSize="@dimen/date_picker_date_label_size"
- tools:textColor="@color/white" />
+ android:ellipsize="none" />
</LinearLayout>
diff --git a/core/res/res/layout/time_picker_header_material.xml b/core/res/res/layout/time_picker_header_material.xml
index be9e443..3f5e300 100644
--- a/core/res/res/layout/time_picker_header_material.xml
+++ b/core/res/res/layout/time_picker_header_material.xml
@@ -18,13 +18,11 @@
<!-- This layout is duplicated in land/time_picker_material.xml, so any
changes made here need to be manually copied over. -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/time_header"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
- android:padding="@dimen/timepicker_separator_padding"
- tools:background="@color/accent_material_light">
+ android:padding="@dimen/timepicker_separator_padding">
<!-- The hour should always be to the left of the separator,
regardless of the current locale's layout direction. -->
@@ -37,10 +35,7 @@
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
android:singleLine="true"
android:ellipsize="none"
- android:gravity="right"
- tools:text="23"
- tools:textSize="@dimen/timepicker_time_label_size"
- tools:textColor="@color/white" />
+ android:gravity="right" />
<TextView
android:id="@+id/separator"
@@ -50,10 +45,7 @@
android:layout_marginRight="@dimen/timepicker_separator_padding"
android:layout_centerInParent="true"
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
- android:importantForAccessibility="no"
- tools:text=":"
- tools:textSize="@dimen/timepicker_time_label_size"
- tools:textColor="@color/white" />
+ android:importantForAccessibility="no" />
<!-- The minutes should always be to the left of the separator,
regardless of the current locale's layout direction. -->
@@ -66,10 +58,7 @@
android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
android:singleLine="true"
android:ellipsize="none"
- android:gravity="left"
- tools:text="59"
- tools:textSize="@dimen/timepicker_time_label_size"
- tools:textColor="@color/white" />
+ android:gravity="left" />
<!-- The layout alignment of this view will switch between toRightOf
@id/minutes and toLeftOf @id/hours depending on the locale. -->
@@ -90,10 +79,7 @@
android:paddingTop="@dimen/timepicker_am_top_padding"
android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
android:lines="1"
- android:ellipsize="none"
- tools:text="AM"
- tools:textSize="@dimen/timepicker_ampm_label_size"
- tools:textColor="@color/white" />
+ android:ellipsize="none" />
<CheckedTextView
android:id="@+id/pm_label"
android:layout_width="wrap_content"
@@ -103,9 +89,6 @@
android:paddingTop="@dimen/timepicker_pm_top_padding"
android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
android:lines="1"
- android:ellipsize="none"
- tools:text="PM"
- tools:textSize="@dimen/timepicker_ampm_label_size"
- tools:textColor="@color/white" />
+ android:ellipsize="none" />
</LinearLayout>
</RelativeLayout>
diff --git a/core/res/res/transition/popup_window_enter.xml b/core/res/res/transition/popup_window_enter.xml
index 38c41f0..c4c8dac 100644
--- a/core/res/res/transition/popup_window_enter.xml
+++ b/core/res/res/transition/popup_window_enter.xml
@@ -16,17 +16,14 @@
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
android:transitionOrdering="together">
- <!-- Start from location of epicenter, move to popup location. -->
- <transition
- class="com.android.internal.transition.EpicenterTranslate"
- android:duration="300" />
-
<!-- Start from size of epicenter, expand to full width/height. -->
<transition
- class="com.android.internal.transition.EpicenterClipReveal"
- android:centerClipBounds="true"
- android:duration="300" />
+ class="com.android.internal.transition.EpicenterTranslateClipReveal"
+ android:duration="250" />
<!-- Quickly fade in. -->
- <fade android:duration="100" />
+ <fade
+ android:duration="100"
+ android:fromAlpha="0.1"
+ android:toAlpha="1.0" />
</transitionSet>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index bb3ec65..0b96d22 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5872,16 +5872,9 @@
</declare-styleable>
<!-- @hide For internal use only. Use only as directed. -->
- <declare-styleable name="EpicenterClipReveal">
- <attr name="centerClipBounds" format="boolean" />
+ <declare-styleable name="EpicenterTranslateClipReveal">
<attr name="interpolatorX" format="reference" />
<attr name="interpolatorY" format="reference" />
- </declare-styleable>
-
- <!-- @hide For internal use only. Use only as directed. -->
- <declare-styleable name="EpicenterTranslate">
- <attr name="interpolatorX" />
- <attr name="interpolatorY" />
<attr name="interpolatorZ" format="reference" />
</declare-styleable>
diff --git a/core/tests/coretests/src/android/util/FloatMathTest.java b/core/tests/coretests/src/android/util/FloatMathTest.java
deleted file mode 100644
index f479e2b..0000000
--- a/core/tests/coretests/src/android/util/FloatMathTest.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.
- */
-
-package android.util;
-
-import junit.framework.TestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-public class FloatMathTest extends TestCase {
-
- @SmallTest
- public void testSqrt() {
- assertEquals(7, FloatMath.sqrt(49), 0);
- assertEquals(10, FloatMath.sqrt(100), 0);
- assertEquals(0, FloatMath.sqrt(0), 0);
- assertEquals(1, FloatMath.sqrt(1), 0);
- }
-
- @SmallTest
- public void testFloor() {
- assertEquals(78, FloatMath.floor(78.89f), 0);
- assertEquals(-79, FloatMath.floor(-78.89f), 0);
- }
-
- @SmallTest
- public void testCeil() {
- assertEquals(79, FloatMath.ceil(78.89f), 0);
- assertEquals(-78, FloatMath.ceil(-78.89f), 0);
- }
-
- @SmallTest
- public void testSin() {
- assertEquals(0.0, FloatMath.sin(0), 0);
- assertEquals(0.8414709848078965f, FloatMath.sin(1), 0);
- }
-
- @SmallTest
- public void testCos() {
- assertEquals(1.0f, FloatMath.cos(0), 0);
- assertEquals(0.5403023058681398f, FloatMath.cos(1), 0);
- }
-}
diff --git a/core/tests/coretests/src/com/android/internal/util/CallbackRegistryTest.java b/core/tests/coretests/src/com/android/internal/util/CallbackRegistryTest.java
new file mode 100644
index 0000000..c53f4cc
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/CallbackRegistryTest.java
@@ -0,0 +1,305 @@
+/*
+ * 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.util;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class CallbackRegistryTest extends TestCase {
+
+ final Integer callback1 = 1;
+ final Integer callback2 = 2;
+ final Integer callback3 = 3;
+ CallbackRegistry<Integer, CallbackRegistryTest, Integer> registry;
+ int notify1;
+ int notify2;
+ int notify3;
+ int[] deepNotifyCount = new int[300];
+ Integer argValue;
+
+ private void addNotifyCount(Integer callback) {
+ if (callback == callback1) {
+ notify1++;
+ } else if (callback == callback2) {
+ notify2++;
+ } else if (callback == callback3) {
+ notify3++;
+ }
+ deepNotifyCount[callback]++;
+ }
+
+ public void testAddListener() {
+ CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+ new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+ @Override
+ public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+ int arg, Integer arg2) {
+ }
+ };
+ registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+ Integer callback = 0;
+
+ assertNotNull(registry.copyListeners());
+ assertEquals(0, registry.copyListeners().size());
+
+ registry.add(callback);
+ ArrayList<Integer> callbacks = registry.copyListeners();
+ assertEquals(1, callbacks.size());
+ assertEquals(callback, callbacks.get(0));
+
+ registry.add(callback);
+ callbacks = registry.copyListeners();
+ assertEquals(1, callbacks.size());
+ assertEquals(callback, callbacks.get(0));
+
+ Integer otherListener = 1;
+ registry.add(otherListener);
+ callbacks = registry.copyListeners();
+ assertEquals(2, callbacks.size());
+ assertEquals(callback, callbacks.get(0));
+ assertEquals(otherListener, callbacks.get(1));
+
+ registry.remove(callback);
+ registry.add(callback);
+ callbacks = registry.copyListeners();
+ assertEquals(2, callbacks.size());
+ assertEquals(callback, callbacks.get(1));
+ assertEquals(otherListener, callbacks.get(0));
+ }
+
+ public void testSimpleNotify() {
+ CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+ new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+ @Override
+ public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+ int arg1, Integer arg) {
+ assertEquals(arg1, (int) arg);
+ addNotifyCount(callback);
+ argValue = arg;
+ }
+ };
+ registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+ registry.add(callback2);
+ Integer arg = 1;
+ registry.notifyCallbacks(this, arg, arg);
+ assertEquals(arg, argValue);
+ assertEquals(1, notify2);
+ }
+
+ public void testRemoveWhileNotifying() {
+ CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+ new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+ @Override
+ public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+ int arg1, Integer arg) {
+ addNotifyCount(callback);
+ if (callback == callback1) {
+ registry.remove(callback1);
+ registry.remove(callback2);
+ }
+ }
+ };
+ registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+ registry.add(callback1);
+ registry.add(callback2);
+ registry.add(callback3);
+ registry.notifyCallbacks(this, 0, null);
+ assertEquals(1, notify1);
+ assertEquals(1, notify2);
+ assertEquals(1, notify3);
+
+ ArrayList<Integer> callbacks = registry.copyListeners();
+ assertEquals(1, callbacks.size());
+ assertEquals(callback3, callbacks.get(0));
+ }
+
+ public void testDeepRemoveWhileNotifying() {
+ CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+ new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+ @Override
+ public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+ int arg1, Integer arg) {
+ addNotifyCount(callback);
+ registry.remove(callback);
+ registry.notifyCallbacks(CallbackRegistryTest.this, arg1, null);
+ }
+ };
+ registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+ registry.add(callback1);
+ registry.add(callback2);
+ registry.add(callback3);
+ registry.notifyCallbacks(this, 0, null);
+ assertEquals(1, notify1);
+ assertEquals(2, notify2);
+ assertEquals(3, notify3);
+
+ ArrayList<Integer> callbacks = registry.copyListeners();
+ assertEquals(0, callbacks.size());
+ }
+
+ public void testAddRemovedListener() {
+
+ CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+ new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+ @Override
+ public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+ int arg1, Integer arg) {
+ addNotifyCount(callback);
+ if (callback == callback1) {
+ registry.remove(callback2);
+ } else if (callback == callback3) {
+ registry.add(callback2);
+ }
+ }
+ };
+ registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+
+ registry.add(callback1);
+ registry.add(callback2);
+ registry.add(callback3);
+ registry.notifyCallbacks(this, 0, null);
+
+ ArrayList<Integer> callbacks = registry.copyListeners();
+ assertEquals(3, callbacks.size());
+ assertEquals(callback1, callbacks.get(0));
+ assertEquals(callback3, callbacks.get(1));
+ assertEquals(callback2, callbacks.get(2));
+ assertEquals(1, notify1);
+ assertEquals(1, notify2);
+ assertEquals(1, notify3);
+ }
+
+ public void testVeryDeepRemoveWhileNotifying() {
+ final Integer[] callbacks = new Integer[deepNotifyCount.length];
+ for (int i = 0; i < callbacks.length; i++) {
+ callbacks[i] = i;
+ }
+ CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+ new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+ @Override
+ public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+ int arg1, Integer arg) {
+ addNotifyCount(callback);
+ registry.remove(callback);
+ registry.remove(callbacks[callbacks.length - callback - 1]);
+ registry.notifyCallbacks(CallbackRegistryTest.this, arg1, null);
+ }
+ };
+ registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+ for (int i = 0; i < callbacks.length; i++) {
+ registry.add(callbacks[i]);
+ }
+ registry.notifyCallbacks(this, 0, null);
+ for (int i = 0; i < deepNotifyCount.length; i++) {
+ int expectedCount = Math.min(i + 1, deepNotifyCount.length - i);
+ assertEquals(expectedCount, deepNotifyCount[i]);
+ }
+
+ ArrayList<Integer> callbackList = registry.copyListeners();
+ assertEquals(0, callbackList.size());
+ }
+
+ public void testClear() {
+ CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+ new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+ @Override
+ public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+ int arg1, Integer arg) {
+ addNotifyCount(callback);
+ }
+ };
+ registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+ for (int i = 0; i < deepNotifyCount.length; i++) {
+ registry.add(i);
+ }
+ registry.clear();
+
+ ArrayList<Integer> callbackList = registry.copyListeners();
+ assertEquals(0, callbackList.size());
+
+ registry.notifyCallbacks(this, 0, null);
+ for (int i = 0; i < deepNotifyCount.length; i++) {
+ assertEquals(0, deepNotifyCount[i]);
+ }
+ }
+
+ public void testNestedClear() {
+ CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+ new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+ @Override
+ public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+ int arg1, Integer arg) {
+ addNotifyCount(callback);
+ registry.clear();
+ }
+ };
+ registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+ for (int i = 0; i < deepNotifyCount.length; i++) {
+ registry.add(i);
+ }
+ registry.notifyCallbacks(this, 0, null);
+ for (int i = 0; i < deepNotifyCount.length; i++) {
+ assertEquals(1, deepNotifyCount[i]);
+ }
+
+ ArrayList<Integer> callbackList = registry.copyListeners();
+ assertEquals(0, callbackList.size());
+ }
+
+ public void testIsEmpty() throws Exception {
+ CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+ new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+ @Override
+ public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+ int arg, Integer arg2) {
+ }
+ };
+ registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+ Integer callback = 0;
+
+ assertTrue(registry.isEmpty());
+ registry.add(callback);
+ assertFalse(registry.isEmpty());
+ }
+
+ public void testClone() throws Exception {
+ CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer> notifier =
+ new CallbackRegistry.NotifierCallback<Integer, CallbackRegistryTest, Integer>() {
+ @Override
+ public void onNotifyCallback(Integer callback, CallbackRegistryTest sender,
+ int arg, Integer arg2) {
+ }
+ };
+ registry = new CallbackRegistry<Integer, CallbackRegistryTest, Integer>(notifier);
+
+ assertTrue(registry.isEmpty());
+ CallbackRegistry<Integer, CallbackRegistryTest, Integer> registry2 = registry.clone();
+ Integer callback = 0;
+ registry.add(callback);
+ assertFalse(registry.isEmpty());
+ assertTrue(registry2.isEmpty());
+ registry2 = registry.clone();
+ assertFalse(registry2.isEmpty());
+ }
+}
diff --git a/docs/html/design/building-blocks/buttons.jd b/docs/html/design/building-blocks/buttons.jd
deleted file mode 100644
index 713574a..0000000
--- a/docs/html/design/building-blocks/buttons.jd
+++ /dev/null
@@ -1,99 +0,0 @@
-page.title=Buttons
-page.tags=button,input
-@jd:body
-
-<a class="notice-designers-material"
- href="http://www.google.com/design/spec/components/buttons.html">
- <div>
- <h3>Material Design</h3>
- <p>Buttons<p>
- </div>
-</a>
-
-<a class="notice-developers" href="{@docRoot}guide/topics/ui/controls/button.html">
- <div>
- <h3>Developer Docs</h3>
- <p>Buttons</p>
- </div>
-</a>
-
-<p itemprop="description">A button consists of text and/or an image that clearly communicates what action
- will occur when the user touches it. A button can have an image, text, or both.
-</p>
-
-<div class="cols" style="margin-top:22px">
- <div class="col-3">
- <img src="{@docRoot}design/media/icon_magnifying_glass.png" style="height:64px;padding:20px 0 0 40px;">
- </div>
- <div class="col-3">
- <img src="{@docRoot}design/media/buttons_text.png" style="height:94px;">
- </div>
- <div class="col-7">
- <img src="{@docRoot}design/media/buttons_image_and_text.png" style="height:94px;">
- </div>
-</div>
-
-<div class="cols" style="margin-top:0;">
- <div class="col-3">
- <p>An image alone works best when the action can be represented by a symbol that's well understood.</p>
- </div>
- <div class="col-3">
- <p>Text alone is most appropriate for actions that would be difficult to
- represent visually, or are critical to convey in words to avoid any ambiguity.</p>
- </div>
- <div class="col-7">
- <p>
- Both an icon and text is most appropriate when they complement each other:
- each carrying its own bit of information, but together making a larger whole.
- </p>
-
- <p>
- For example, in a birthday reminder card in Google Now, the button's text
- describes the action while its image indicates that the action will be done
- in Google+.
- </p>
- </div>
-</div>
-
-<h3>What about button backgrounds?</h3>
-
-<div class="cols">
- <div class="col-6">
- <p>For <strong>image-only</strong> buttons, a background isn't necessary because
- users are accustomed to interacting with objects.</p>
-
- <div class="cols" style="margin-left:72px">
- <div class="col-2">
- <div class="do-dont-label bad emulate-content-left-padding" style="width:30px">Don't</div>
- <img src="{@docRoot}design/media/buttons_image_bg_dont.png" style="padding-left:14px;">
- </div>
- <div class="col-2" style="width:29px;margin-left:10px;">
- <div class="do-dont-label good"><strong>Do</strong></div>
- <img src="{@docRoot}design/media/icon_alarm.png" style="width:31px;padding-top:7px;">
- </div>
- </div>
- </div>
-
-<div class="col-7">
-<p>
- For buttons <strong>with text</strong>, a background is also usually
- unnecessary. To invite users to touch, phrase it as a clear action (e.g.
- "Start", "Sign in") and use different color and formatting than the screen's
- usual body text.
-</p>
-
-<p>
- Use buttons with backgrounds sparingly. Because they have a heavy appearance,
- they work best when there's only one or two of them on the screen. They're
- most appropriate for:
-</p>
-
-<ul>
- <li>A call to action you really want users to pursue (e.g. "Sign up")</li>
- <li>A key decision point (e.g. "Accept" / "Decline")</li>
- <li>When the user is about to commit a significant action (e.g. "Erase
- everything", "Buy now")</li>
-</ul>
-</div>
-</div>
-
diff --git a/docs/html/design/building-blocks/dialogs.jd b/docs/html/design/building-blocks/dialogs.jd
deleted file mode 100644
index 70460ba..0000000
--- a/docs/html/design/building-blocks/dialogs.jd
+++ /dev/null
@@ -1,186 +0,0 @@
-page.title=Dialogs
-page.tags=dialog,alert,popup,toast
-@jd:body
-
-<a class="notice-designers-material"
- href="http://www.google.com/design/spec/components/dialogs.html">
- <div>
- <h3>Material Design</h3>
- <p>Dialogs<p>
- </div>
-</a>
-
-<a class="notice-developers" href="{@docRoot}guide/topics/ui/dialogs.html">
- <div>
- <h3>Developer Docs</h3>
- <p>Dialogs</p>
- </div>
-</a>
-
-<p itemprop="description">Dialogs prompt the user for decisions or additional information required by the app to continue a
-task. Such requests can range from simple Cancel/OK decisions to more complex layouts asking the
-user to adjust settings or enter text.</p>
-
-<img src="{@docRoot}design/media/dialogs_main.png">
-
-<div class="with-callouts">
-
-<ol>
- <li>
- <h4>Optional title region</h4>
- <p>The title introduces the content of your dialog. It can, for example, identify the name of a
- setting that the user is about to change, or request a decision.</p>
- </li>
- <li>
- <h4>Content area</h4>
- <p>Dialog content varies widely. For settings dialogs, a dialog may contain UI elements such as
- sliders, text fields, checkboxes, or radio buttons that allow the user to change app or system
- settings. In other cases, such as alerts, the content may consist solely of text that provides
- further context for a user decision.</p>
- </li>
-
- <li>
- <h4>Action buttons</h4>
- <p>Action buttons are typically Cancel and/or OK, with OK indicating the preferred or most likely action. However, if the options consist of specific actions such as Close or Wait rather than a confirmation or cancellation of the action described in the content, then all the buttons should be active verbs. Order actions following these rules:</p>
- <ul>
-
- <li>The dismissive action of a dialog is always on the left. Dismissive actions return to the user to the previous state.</li>
- <li>The affirmative actions are on the right. Affirmative actions continue progress toward the user goal that triggered the dialog.</li>
- </ul>
- </li>
-</ol>
-</div>
-
-<img src="{@docRoot}design/media/dialogs_examples.png">
-<div class="figure-caption">
- Samples of typical dialog use in Android.
-</div>
-
-<h2 id="alerts">Alerts</h2>
-
-<p>Alerts inform the user about a situation that requires their confirmation or acknowledgement before
-proceeding. They differ slightly in appearance based upon the severity and impact of the message
-conveyed.</p>
-
-<div class="cols">
- <div class="col-8">
-
- <img src="{@docRoot}design/media/dialogs_w_no_title.png">
-
- </div>
- <div class="col-5">
-
-<h4>Alerts without title bars</h4>
-<p>Most alerts don't need titles. Usually the decision doesn't have a severe impact and can be summed
-up succinctly in a sentence or two. The content area should either ask a question (such as "Delete
-this conversation?") or make a clear statement whose relationship to the action buttons is obvious.</p>
-
- </div>
-</div>
-
-
-<div class="cols">
- <div class="col-8">
-
- <img src="{@docRoot}design/media/dialogs_w_title.png">
-
- </div>
- <div class="col-5">
-
-<h4>Alerts with title bars</h4>
-<p>Use alerts with title bars sparingly. They are appropriate only when a high-risk operation involving
-potential loss of data, connectivity, extra charges, and so on requires a clear question or
-statement (the title) and some additional explanation (in the content area).</p>
-<p>Keep the question or statement short: for example, "Erase USB storage?" Avoid apologies. A user
-should be able to skip the content completely and still have a clear idea of what choices are
-available based on the title and the text of the action buttons.</p>
-
- </div>
-</div>
-<p>When crafting a confirmation dialog, make the title meaningful by echoing the requested action.</p>
-
-<div class="cols">
- <div class="col-4">
- <div class="do-dont-label bad">Don't</div>
- <table class="ui-table bad">
- <thead>
- <tr>
- <th class="label">
- Are you sure?
- </th>
- </tr>
- </thead>
- </table>
- </div>
- <div class="col-4">
- <div class="do-dont-label bad">Don't</div>
- <table class="ui-table bad">
- <thead>
- <tr>
- <th class="label">
- Warning!
- </th>
- </tr>
- </thead>
- </table>
- </div>
- <div class="col-5">
- <div class="do-dont-label good">Do</div>
- <table class="ui-table good">
- <thead>
- <tr>
- <th class="label">
- Erase USB storage?
- </th>
- </tr>
- </thead>
- </table>
- </div>
-</div>
-
-
-<h2 id="popups">Popups</h2>
-
-<p>Popups are lightweight version of dialogs that require a single selection from the user. Popups
-don't have have explicit buttons that accept or cancel the operation. Instead, making a selection
-advances the workflow, and simply touching outside the popup dismisses it.</p>
-
-<img src="{@docRoot}design/media/dialogs_popups_example.png">
-
-
-<h2 id="toasts">Toasts</h2>
-
-
-<div class="cols">
- <div class="col-6">
-
- <div class="vspace size-3"></div>
-
-<p>Toasts provide lightweight feedback about an operation in a small popup. For example, navigating
-away from an email before you send it triggers a "Draft saved" toast to let you know that you can
-continue editing later. Toasts automatically disappear after a timeout.</p>
-
-<a class="notice-designers-material left"
- href="http://www.google.com/design/spec/components/snackbars-toasts.html">
- <div>
- <h3>Material Design</h3>
- <p>Toasts<p>
- </div>
-</a>
-
-
-<a class="notice-developers left" href="{@docRoot}guide/topics/ui/notifiers/toasts.html">
- <div>
- <h3>Developer Docs</h3>
- <p>Toasts</p>
- </div>
-</a>
-
- </div>
- <div class="col-7">
-
- <img src="{@docRoot}design/media/dialogs_toasts.png">
-
- </div>
-</div>
-
diff --git a/docs/html/design/building-blocks/grid-lists.jd b/docs/html/design/building-blocks/grid-lists.jd
deleted file mode 100644
index 7a1c652..0000000
--- a/docs/html/design/building-blocks/grid-lists.jd
+++ /dev/null
@@ -1,96 +0,0 @@
-page.title=Grid Lists
-page.tags=gridview,layout,listview
-@jd:body
-
-<img src="{@docRoot}design/media/gridview_overview.png">
-
-
-<a class="notice-designers-material"
- href="http://www.google.com/design/spec/components/grid-lists.html">
- <div>
- <h3>Material Design</h3>
- <p>Grid lists<p>
- </div>
-</a>
-
-<a class="notice-developers" href="{@docRoot}guide/topics/ui/layout/gridview.html">
- <div>
- <h3>Developer Docs</h3>
- <p>Grid View</p>
- </div>
-</a>
-
-<p itemprop="description">Grid lists are an alternative to standard list views. They are best suited for showing data sets
-that represent themselves through images. In contrast to simple lists, grid lists may scroll either
-vertically or horizontally.</p>
-
-
-
-<h2 id="generic_grid">Generic Grids</h2>
-
-
-<p>The items in a grid list are arranged in two dimensions, one of which is fixed when scrolling
-content. The scrolling direction dictates the ordering of the items within the grid list. Since the
-scrolling direction is not deterministic, make it easy for the user to determine the orientation by
-cutting off grid items to communicate where the overflow is located.</p>
-<p>Avoid creating grid lists that scroll in two dimensions.</p>
-
-
-<div class="cols">
- <div class="col-7">
-
- <img src="{@docRoot}design/media/gridview_vertical.png">
-
- </div>
- <div class="col-6">
-
-<h4>Vertical scrolling</h4>
-<p>Vertically scrolling grid list items are sorted in traditional western reading direction:
-left-to-right and top-down. When displaying the list, cut off the items in the bottom row to
-communicate that the user can scroll the list down to show additional items. Be sure to retain this
-scheme when the user rotates the screen.</p>
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-7">
-
- <img src="{@docRoot}design/media/gridview_horizontal.png">
-
- </div>
- <div class="col-6">
-
-<h4>Horizontal scrolling</h4>
-<p>Horizontally scrolling lists fix the vertical axis of the item grid. Compared to vertically
-scrolling lists, the sorting changes slightly to a top-down and left-to-right arrangement. Employ
-the same technique of cutting off the items in the rightmost column to indicate the scrolling
-direction.</p>
-<p>Don't use scrolling tabs as a means to switch views in conjunction with horizontally scrolling grid
-lists, because the horizontal gesture for view and content navigation will conflict. If you show
-scrolling tabs for view navigation together with a grid list, use vertical grid scrolling for list
-navigation.</p>
-
- </div>
-</div>
-
-
-<h2 id="with-labels">Grid List with Labels</h2>
-
-<p>Use labels to display additional contextual information for your grid list items.</p>
-
-
-<div class="cols">
- <div class="col-7">
-
- <img src="{@docRoot}design/media/gridview_style.png">
-
- </div>
- <div class="col-6">
-
-<h4>Style</h4>
-<p>Use semi-transparent panels on top of the grid list items to display your labels. This allows you to
-control the contrast and ensures legibility of the labels while letting the content "shine through".</p>
-
- </div>
-</div>
diff --git a/docs/html/design/building-blocks/index.jd b/docs/html/design/building-blocks/index.jd
deleted file mode 100644
index 7fb0e55..0000000
--- a/docs/html/design/building-blocks/index.jd
+++ /dev/null
@@ -1,30 +0,0 @@
-page.title=Building Blocks
-header.justLinks=1
-footer.hide=1
-@jd:body
-
-<style>
-#landing-graphic-container {
- position: relative;
-}
-
-#text-overlay {
- position: absolute;
- left: 0;
- top: 520px;
- width: 450px;
-}
-</style>
-
-<div id="landing-graphic-container">
- <div id="text-overlay">
- <span itemprop="description">Your inventory of ready-to-use elements for creating
- outstanding apps.</span>
- <br><br>
- <a href="{@docRoot}design/building-blocks/tabs.html" class="landing-page-link">Tabs</a>
- </div>
-
- <a href="{@docRoot}design/building-blocks/tabs.html">
- <img src="{@docRoot}design/media/building_blocks_landing.png">
- </a>
-</div>
diff --git a/docs/html/design/building-blocks/lists.jd b/docs/html/design/building-blocks/lists.jd
deleted file mode 100644
index 85753c8..0000000
--- a/docs/html/design/building-blocks/lists.jd
+++ /dev/null
@@ -1,56 +0,0 @@
-page.title=Lists
-page.tags=listview,layout
-@jd:body
-
-<a class="notice-designers-material"
- href="http://www.google.com/design/spec/components/lists.html">
- <div>
- <h3>Material Design</h3>
- <p>Lists<p>
- </div>
-</a>
-
-<p itemprop="description">Lists present multiple line items in a vertical arrangement. They can be used for data selection as
-well as drilldown navigation.</p>
-
-<div class="vspace size-1"> </div>
-
-<div class="cols clearfix">
- <div class="col-9">
-
- <img src="{@docRoot}design/media/lists_main.png">
-
- </div>
- <div class="col-4 with-callouts">
-
-<ol style="margin-bottom: 60px;">
-<li>
-<h4>Section Divider</h4>
-<p>Use section dividers to organize the content of your list into groups and facilitate scanning.</p>
-</li>
-<li>
-<h4>Line Items</h4>
-<p>List items can accommodate a wide range of data types in different arrangements, including
- simple single-line items, multi-line items, and custom items with icons, checkboxes, and action
- buttons.</p>
-</li>
-</ol>
-
-<a class="notice-developers" href="{@docRoot}guide/topics/ui/layout/listview.html">
- <div>
- <h3>Developer Docs</h3>
- <p>List View</p>
- </div>
-</a>
-
-<a class="notice-designers-material"
- href="http://www.google.com/design/spec/components/lists-controls.html">
- <div>
- <h3>Material Design</h3>
- <p>Lists: Controls<p>
- </div>
-</a>
-
-
- </div>
-</div>
diff --git a/docs/html/design/building-blocks/pickers.jd b/docs/html/design/building-blocks/pickers.jd
deleted file mode 100644
index 72da0f7..0000000
--- a/docs/html/design/building-blocks/pickers.jd
+++ /dev/null
@@ -1,40 +0,0 @@
-page.title=Pickers
-page.tags=datepicker,timepicker
-@jd:body
-
-<a class="notice-developers" href="{@docRoot}guide/topics/ui/controls/pickers.html">
- <div>
- <h3>Developer Docs</h3>
- <p>Pickers</p>
- </div>
-</a>
-
-<p itemprop="description">Pickers provide a simple way to select a single value from a set. In addition to touching the
-up/down arrow buttons, it's possible to set the desired value from the keyboard or via a swipe
-gesture.</p>
-
-<div class="cols">
- <div class="col-6">
-
- <img src="{@docRoot}design/media/picker_space.png">
-
- </div>
- <div class="col-6">
-
-<h4>Space considerations</h4>
-<p>Pickers can be used inline on a form, but their relatively large footprint is best suited for
-display in a dialog. For inline display, consider using more compact controls such as text fields or
-spinners.</p>
-
- </div>
-</div>
-
-<h2 id="date-time">Date and time pickers</h2>
-
-<p>Android provides these as ready-to-use dialogs. Each picker is a dialog with a set of controls for
-entering the parts of the date (month, day, year) or time (hour, minute, AM/PM). Using these in your
-app helps ensure that a user's specification of a data or time input is valid and formatted
-correctly. The format of a time and date picker adjusts automatically to the locale.</p>
-
-<img src="{@docRoot}design/media/picker_datetime.png">
-
diff --git a/docs/html/design/building-blocks/progress.jd b/docs/html/design/building-blocks/progress.jd
deleted file mode 100644
index ae81440..0000000
--- a/docs/html/design/building-blocks/progress.jd
+++ /dev/null
@@ -1,105 +0,0 @@
-page.title=Progress & Activity
-page.tags=progressbar,download,network
-@jd:body
-
-<a class="notice-designers-material"
- href="http://www.google.com/design/spec/components/progress-activity.html">
- <div>
- <h3>Material Design</h3>
- <p>Progress and Activity<p>
- </div>
-</a>
-
-<p>Progress bars and activity indicators signal to users that something is happening that will take a moment.</p>
-<h2 id="progress">Progress bars</h2>
-
-<p>Progress bars are for situations where the percentage completed can be determined. They give users a quick sense of how much longer an operation will take.</p>
-
-<img src="{@docRoot}design/media/progress_download.png">
-
-<p>A progress bar should always fill from 0% to 100% and never move backwards to a lower value. If multiple operations are happening in sequence, use the progress bar to represent the delay as a whole, so that when the bar reaches 100%, it doesn't return back to 0%.</p>
-
-<div class="vspace size-2"> </div>
-
-<img src="{@docRoot}design/media/progress_themes.png">
-<div class="figure-caption">
- Progress bar in Holo Dark and Holo Light.
-</div>
-
-<h2 id="activity">Activity indicators</h2>
-
-<p>Activity indicators are for operations of an indeterminate length. They ask users to wait a moment while something finishes up, without getting into specifics about what's happening behind the scenes.</p>
-
-<p>Two styles are available: a bar and a circle. Each is offered in a variety of sizes, in both Holo Light and Holo Dark themes. Choose the appropriate style and size for the surrounding context. For example, the largest activity circle works well when displayed in a blank content area, but not in a smaller dialog box. Each operation should only be represented by one activity indicator.</p>
-
-<div class="cols">
- <div class="col-6">
-
- <img src="{@docRoot}design/media/progress_activity.png">
-
- </div>
- <div class="col-7 with-callouts">
-
- <ol>
- <li class="value-1"><h4>Activity bar</h4>
- <p>In this example, an activity bar (in Holo Dark) appears when a user first requests a download. There's an unknown period of time when the download has not yet started. As soon as the download starts, this activity bar transforms into a progress bar.</p>
- </li>
- </ol>
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-6">
-
- <img src="{@docRoot}design/media/progress_activity2.png">
-
- </div>
- <div class="col-7 with-callouts">
-
- <ol>
- <li class="value-2"><h4>Activity circle</h4>
- <p>In this example, an activity circle (in Holo Light) is used in the Gmail application when a message is being loaded because it's not possible to determine how long it will take to download the email.</p>
- <p>When displaying an activity circle, do not include text to communicate what the app is doing. The moving circle alone provides sufficient feedback about the delay, and does so in an understated way that minimizes the impact.</p>
- <p>
- <div style="margin-left:0;margin-top:1em;">
- <img src="{@docRoot}design/media/progress_activity_do_dont.png">
- </div>
- </p>
- </li>
- </ol>
-
- </div>
-</div>
-
-<h2 id="custom-indicators">Custom indicators</h2>
-<p>The standard progress bar and activity indicators work well for most situations and should be used whenever possible to provide a consistent experience across Android. However, some situations may call for something more custom.</p>
-
-<p>Here's an example:<br>
-In all of the Google Play apps (Music, Books, Movies, Magazines), we wanted the current download state of each item to be visible at all times at the top-level screen. These states are:
- <ul>
- <li>Not downloaded</li>
- <li>Temporarily downloaded (automatically cached by the app)</li>
- <li>Permanently downloaded on the device at the user's request</li>
- </ul>
-</p>
-<p>We also needed to indicate progress from one download state to another, because downloading is not instantaneous.</p>
-<p>This presented a challenge, because the Google Play apps use a variety of different layouts, and some of them are highly space-constrained. We didn't want this information to clutter the top-level screens, or compete too much with the cover art.</p>
-<p>So we designed a custom indicator that could show all of the information in a tiny footprint, with the flexibility to appear on top of content if necessary.</p>
-
-<img src="{@docRoot}design/media/progress_activity_custom.png">
-
-<p>The color indicates whether it's downloaded (blue) or not (gray). The appearance of the pin indicates whether the download is permanent (white, upright) or temporary (gray, diagonal). And when state is in the process of changing, progress is indicated by a moving pie chart.</p>
-
-<div class="cols">
- <div class="col-9">
- <img src="{@docRoot}design/media/progress_activity_custom_app.png">
- </div>
- <div class="col-4">
- <div class="figure-caption">
- Across Google Play apps with different layouts, the same custom indicator appears with each item. It communicates download state as well as progress, in a compact package that can be incorporated into any screen design.
- </div>
- </div>
-</div>
-
-<p>If you find that the standard indicators aren't meeting your needs (due to space constraints, state complexities), by all means design your own. Make it feel like part of the Android family by injecting some of the visual characteristics of the standard indicators. In this example, we carried over the circular shape, the same shade of blue, and the flat and simple style.</p>
diff --git a/docs/html/design/building-blocks/scrolling.jd b/docs/html/design/building-blocks/scrolling.jd
deleted file mode 100644
index 04b1e4a..0000000
--- a/docs/html/design/building-blocks/scrolling.jd
+++ /dev/null
@@ -1,39 +0,0 @@
-page.title=Scrolling
-page.tags=scrollview,listview
-@jd:body
-
-
-<p>Scrolling allows the user to navigate to content in the overflow using a swipe gesture. The
-scrolling speed is proportional to the speed of the gesture.</p>
-<h2 id="indicator">Scroll Indicator</h2>
-
-<p>Appears during scrolling to indicate what portion of the content is currently in view.</p>
-
-<div class="framed-nexus5-land-span-13">
- <video class="play-on-hover" autoplay>
- <source src="{@docRoot}design/media/scroll_indicator.mp4" type="video/mp4">
- <source src="{@docRoot}design/media/scroll_indicator.webm" type="video/webm">
- <source src="{@docRoot}design/media/scroll_indicator.ogv" type="video/ogg">
- </video>
-</div>
-<div class="figure-caption">
- <div class="video-instructions"> </div>
-</div>
-
-<h2 id="index-scrolling">Index Scrolling</h2>
-
-<p>In addition to traditional scrolling, a long alphabetical list can also offer index scrolling: a way
-to quickly navigate to the items that begin with a particular letter. With index scrolling, a scroll
-indicator appears even when the user isn't scrolling. Touching or dragging it causes the current
-letter to pop up in a prominent way.</p>
-
-<div class="framed-nexus5-land-span-13">
- <video class="play-on-hover" autoplay>
- <source src="{@docRoot}design/media/scroll_index.mp4" type="video/mp4">
- <source src="{@docRoot}design/media/scroll_index.webm" type="video/webm">
- <source src="{@docRoot}design/media/scroll_index.ogv" type="video/ogg">
- </video>
-</div>
-<div class="figure-caption">
- <div class="video-instructions"> </div>
-</div>
diff --git a/docs/html/design/building-blocks/seek-bars.jd b/docs/html/design/building-blocks/seek-bars.jd
deleted file mode 100644
index 04446d2..0000000
--- a/docs/html/design/building-blocks/seek-bars.jd
+++ /dev/null
@@ -1,45 +0,0 @@
-page.title=Seek Bars and Sliders
-page.tags=seekbar,progressbar
-@jd:body
-
-<a class="notice-designers-material"
- href="http://www.google.com/design/spec/components/sliders.html">
- <div>
- <h3>Material Design</h3>
- <p>Sliders<p>
- </div>
-</a>
-
-<p>Interactive sliders make it possible to select a value from a continuous or discrete range of values
-by moving the slider thumb. The smallest value is to the left, the largest to the right. The
-interactive nature of the slider makes it a great choice for settings that reflect intensity levels,
-such as volume, brightness, or color saturation.</p>
-
-<div class="cols">
- <div class="col-9">
-
- <img src="{@docRoot}design/media/seekbar_example.png">
-
- </div>
- <div class="col-4">
-
-<div class="vspace size-2"> </div>
-
-<h4>Example</h4>
-<p>Interactive slider to set the ringer volume. The value can either be set through the hardware volume controls or interactively via a gesture.</p>
-
- </div>
-</div>
-
-
-<div class="cols">
- <div class="col-9">
-
- <img src="{@docRoot}design/media/seekbar_style.png">
- <div class="figure-caption">
- Seek bars in Holo Light & Dark
- </div>
-
- </div>
- <div class="col-4"> </div>
-</div>
diff --git a/docs/html/design/building-blocks/spinners.jd b/docs/html/design/building-blocks/spinners.jd
deleted file mode 100644
index 31c5558..0000000
--- a/docs/html/design/building-blocks/spinners.jd
+++ /dev/null
@@ -1,54 +0,0 @@
-page.title=Spinners
-page.tags=spinner,dropdown
-@jd:body
-
-<a class="notice-designers-material"
- href="http://www.google.com/design/spec/components/menus.html">
- <div>
- <h3>Material Design</h3>
- <p>Menus<p>
- </div>
-</a>
-
-<a class="notice-developers" href="{@docRoot}guide/topics/ui/controls/spinner.html">
- <div>
- <h3>Developer Docs</h3>
- <p>Spinners</p>
- </div>
-</a>
-
-<p itemprop="description">Spinners provide a quick way to select one value from a set. In the default state, a spinner shows
-its currently selected value. Touching the spinner displays a dropdown menu with all other available
-values, from which the user can select a new one.</p>
-
-
-<div class="cols">
- <div class="col-6">
-
- <img src="{@docRoot}design/media/spinners_form.png">
-
-<h4>Spinners in forms</h4>
-<p>Spinners are useful for data picking in forms. They are compact and integrate nicely with other
-components. Use spinners in forms for both simple data input and in combination with other input
-fields. For example, a text field might let you edit an email address for a contact, while its
-associated spinner allows you to select whether it's a Home or Work address.</p>
-
- </div>
- <div class="col-7">
-
- <img src="{@docRoot}design/media/spinners_actionbar.png">
-
-<h4>Spinners in action bars</h4>
-<p>Use spinners in action bars to switch views. For example, Gmail uses a spinner to permit switching
-between accounts or commonly used labels. Spinners are useful when changing the view is important to
-your app, but not necessarily a frequent occurrence. In cases where view switching is frequent, use
-tabs.</p>
-
- </div>
-</div>
-
-<img src="{@docRoot}design/media/spinners_hololightanddark.png">
-<div class="figure-caption">
- Spinners in the Holo Dark and Holo Light themes, in various states.
-</div>
-
diff --git a/docs/html/design/building-blocks/switches.jd b/docs/html/design/building-blocks/switches.jd
deleted file mode 100644
index 9dd09ca..0000000
--- a/docs/html/design/building-blocks/switches.jd
+++ /dev/null
@@ -1,64 +0,0 @@
-page.title=Switches
-page.tags=switch,checkbox,radiobutton,button
-@jd:body
-
-<a class="notice-designers-material"
- href="http://www.google.com/design/spec/components/selection-controls.html">
- <div>
- <h3>Material Design</h3>
- <p>Selection Controls<p>
- </div>
-</a>
-
-<p>Switches allow the user to select options. There are three kinds of switches: checkboxes, radio
-buttons, and on/off switches.</p>
-
-
-
-<h2 id="checkboxes">Checkboxes</h2>
-
-<a class="notice-developers" href="{@docRoot}guide/topics/ui/controls/checkbox.html">
- <div>
- <h3>Developer Docs</h3>
- <p>Checkboxes</p>
- </div>
-</a>
-
-<p itemprop="description">Checkboxes allow the user to select multiple options from a set. Avoid using a single checkbox to
-turn an option off or on. Instead, use an on/off switch.</p>
-
- <img src="{@docRoot}design/media/switches_checkboxes.png">
-
-
-
-<h2 id="radio-buttons">Radio Buttons</h2>
-
-<a class="notice-developers" href="{@docRoot}guide/topics/ui/controls/radiobutton.html">
- <div>
- <h3>Developer Docs</h3>
- <p>Radio Buttons</p>
- </div>
-</a>
-
-<p>Radio buttons allow the user to select one option from a set. Use radio buttons for exclusive
-selection if you think that the user needs to see all available options side-by-side. Otherwise,
-consider a spinner, which uses less space.</p>
-
- <img src="{@docRoot}design/media/switches_radios.png">
-
-
-
-<h2 id="switches">On/off Switches</h2>
-
-<a class="notice-developers" href="{@docRoot}guide/topics/ui/controls/togglebutton.html">
- <div>
- <h3>Developer Docs</h3>
- <p>Toggle Buttons</p>
- </div>
-</a>
-
-<p>On/off switches toggle the state of a single settings option.</p>
-
- <img src="{@docRoot}design/media/switches_switches.png">
-
-
diff --git a/docs/html/design/building-blocks/tabs.jd b/docs/html/design/building-blocks/tabs.jd
deleted file mode 100644
index 1315a2f..0000000
--- a/docs/html/design/building-blocks/tabs.jd
+++ /dev/null
@@ -1,65 +0,0 @@
-page.title=Tabs
-page.tags=tabs,actionbar,navigation,viewpager
-@jd:body
-
-<img src="{@docRoot}design/media/tabs_overview.png">
-
-<a class="notice-designers-material"
- href="http://www.google.com/design/spec/components/tabs.html">
- <div>
- <h3>Material Design</h3>
- <p>Tabs<p>
- </div>
-</a>
-
-<a class="notice-developers" href="{@docRoot}training/implementing-navigation/lateral.html">
- <div>
- <h3>Developer Docs</h3>
- <p>Creating Swipe Views<br>with Tabs</p>
- </div>
-</a>
-
-<p itemprop="description">Tabs in the action bar make it easy to explore and switch between different views or functional
-aspects of your app, or to browse categorized data sets.</p>
-
-<p>For details on using gestures to move between tabs, see the <a href="{@docRoot}design/patterns/swipe-views.html">Swipe Views</a> pattern.</p>
-
-<h2 id="scrollable">Scrollable Tabs</h2>
-
-
-<div class="cols">
- <div class="col-6">
-
-<p>Scrolling tab controls can contain a larger number of items than a standard tab control. To navigate
-to the next/previous view, swipe left or right.</p>
-
- </div>
- <div class="col-7">
-
- <video width="400" class="with-shadow play-on-hover" autoplay>
- <source src="{@docRoot}design/media/tabs_scrolly.mp4" type="video/mp4">
- <source src="{@docRoot}design/media/tabs_scrolly.webm" type="video/webm">
- <source src="{@docRoot}design/media/tabs_scrolly.ogv" type="video/ogg">
- </video>
- <div class="figure-caption">
- Scrolling tabs in the Play Store app.
- <div class="video-instructions-image"> </div>
- </div>
-
- </div>
-</div>
-
-
-<h2 id="fixed">Fixed Tabs</h2>
-<p>Fixed tabs display all items concurrently. To navigate to a different view, touch the tab, or swipe left or right.</p>
-<p>Fixed tabs are displayed with equal width, based on the width of the widest tab label. If there is insufficient room to display all tabs, the tab labels themselves will be scrollable. For this reason, fixed tabs are best suited for displaying 3 or fewer tabs.</p>
-
-<img src="{@docRoot}design/media/tabs_standard.png">
-<div class="figure-caption">
- Tabs in Holo Dark & Light.
-</div>
-
-<img src="{@docRoot}design/media/tabs_youtube.png">
-<div class="figure-caption">
- Tabs in the Google Play Movies app.
-</div>
diff --git a/docs/html/design/building-blocks/text-fields.jd b/docs/html/design/building-blocks/text-fields.jd
deleted file mode 100644
index 9403679..0000000
--- a/docs/html/design/building-blocks/text-fields.jd
+++ /dev/null
@@ -1,88 +0,0 @@
-page.title=Text Fields
-page.tags=text,edittext,input
-@jd:body
-
-
-<a class="notice-designers-material"
- href="http://www.google.com/design/spec/components/text-fields.html">
- <div>
- <h3>Material Design</h3>
- <p>Text Fields<p>
- </div>
-</a>
-
-<a class="notice-developers" href="{@docRoot}guide/topics/ui/controls/text.html">
- <div>
- <h3>Developer Docs</h3>
- <p>Text Fields</p>
- </div>
-</a>
-
-<p itemprop="description">Text fields allow the user to type text into your app. They can be either single line or multi-line.
-Touching a text field places the cursor and automatically displays the keyboard. In addition to
-typing, text fields allow for a variety of other activities, such as text selection (cut, copy,
-paste) and data lookup via auto-completion.</p>
-
-
-<div class="cols">
- <div class="col-12">
-
- <img src="{@docRoot}design/media/text_input_singlevsmultiline.png">
-
- </div>
-</div>
-
-<h4>Single line and multi line</h4>
-<p>Single-line fields automatically scroll their content to the left as the text input cursor reaches
-the right edge of the input field. Multi-line text fields automatically break to a new line for
-overflow text and scroll vertically when the cursor reaches the lower edge.</p>
-
-<img src="{@docRoot}design/media/text_input_typesandtypedown.png">
-
-<div class="cols">
- <div class="col-6">
-
-<h4>Text field types</h4>
-<p>Text fields can have different types, such as number, message, or email address. The type determines
-what kind of characters are allowed inside the field, and may prompt the virtual keyboard to
-optimize its layout for frequently used characters.</p>
-
- </div>
- <div class="col-6">
-
-<h4>Auto-complete text fields</h4>
-<p>Use auto-complete text fields to present real-time completions or search results in popups, so users
-can enter information more accurately and efficiently.</p>
-
- </div>
-</div>
-
-<h2 id="text-selection">Text Selection</h2>
-
-<p>Users can select any word in a text field with a long press. This action triggers a text selection
-mode that facilitates extending the selection or choosing an action to perform on the selected text.
-Selection mode includes:</p>
-
-<div class="cols">
- <div class="col-9">
-
- <img src="{@docRoot}design/media/text_input_textselection.png">
-
- </div>
- <div class="col-4 with-callouts">
-
-<ol>
-<li>
-<h4>Contextual action bar</h4>
-<p>A contextual action bar (CAB) displays the actions available to perform on the selection:
- typically cut, copy, and paste, but apps can insert additional commands as needed.</p>
-</li>
-<li>
-<h4>Selection handles</h4>
-<p>Selection handles can be dragged to select more or less text while remaining in selection mode.</p>
-</li>
-</ol>
-
- </div>
-</div>
-
diff --git a/docs/html/design/design_toc.cs b/docs/html/design/design_toc.cs
index 63f5cad..85569ef 100644
--- a/docs/html/design/design_toc.cs
+++ b/docs/html/design/design_toc.cs
@@ -1,15 +1,15 @@
<ul id="nav">
<li class="nav-section">
- <div class="nav-section-header"><a href="<?cs var:toroot ?>design/index.html">Get Started</a></div>
+ <div class="nav-section-header"><a href="<?cs var:toroot ?>design/get-started/creative-vision.html">Creative Vision</a></div>
<ul>
- <li><a href="<?cs var:toroot ?>design/get-started/creative-vision.html">Creative Vision</a></li>
<li><a href="<?cs var:toroot ?>design/get-started/principles.html">Design Principles</a></li>
+ <li><a href="<?cs var:toroot ?>design/patterns/new.html">New in Android</a></li>
</ul>
</li>
<li class="nav-section">
- <div class="nav-section-header empty"><a href="<?cs var:toroot ?>design/material/index.html">Material Design</a></div>
+ <div class="nav-section-header empty"><a href="<?cs var:toroot ?>design/material/index.html">Material for Android</a></div>
</li>
<li class="nav-section">
@@ -43,59 +43,20 @@
</li>
<li class="nav-section">
- <div class="nav-section-header"><a href="<?cs var:toroot ?>design/style/index.html">Style</a></div>
+ <div class="nav-section-header"><a href="<?cs var:toroot ?>design/style/index.html">Pure Android</a></div>
<ul>
<li><a href="<?cs var:toroot ?>design/style/devices-displays.html">Devices and Displays</a></li>
- <li><a href="<?cs var:toroot ?>design/style/themes.html">Themes</a></li>
- <li><a href="<?cs var:toroot ?>design/style/touch-feedback.html">Touch Feedback</a></li>
- <li><a href="<?cs var:toroot ?>design/style/metrics-grids.html">Metrics and Grids</a></li>
- <li><a href="<?cs var:toroot ?>design/style/typography.html">Typography</a></li>
- <li><a href="<?cs var:toroot ?>design/style/color.html">Color</a></li>
- <li><a href="<?cs var:toroot ?>design/style/iconography.html">Iconography</a></li>
- <li><a href="<?cs var:toroot ?>design/style/branding.html">Your Branding</a></li>
- <li><a href="<?cs var:toroot ?>design/style/writing.html" zh-cn-lang="å†™ä½œé£Žæ ¼">Writing Style</a></li>
- </ul>
- </li>
-
- <li class="nav-section">
- <div class="nav-section-header"><a href="<?cs var:toroot ?>design/patterns/index.html">Patterns</a></div>
- <ul>
- <li><a href="<?cs var:toroot ?>design/patterns/new.html">New in Android</a></li>
- <li><a href="<?cs var:toroot ?>design/patterns/gestures.html">Gestures</a></li>
<li><a href="<?cs var:toroot ?>design/patterns/app-structure.html">App Structure</a></li>
<li><a href="<?cs var:toroot ?>design/patterns/navigation.html">Navigation</a></li>
- <li><a href="<?cs var:toroot ?>design/patterns/actionbar.html">Action Bar</a></li>
- <li><a href="<?cs var:toroot ?>design/patterns/navigation-drawer.html">Navigation Drawer</a></li>
- <li><a href="<?cs var:toroot ?>design/patterns/multi-pane-layouts.html">Multi-pane Layouts</a></li>
- <li><a href="<?cs var:toroot ?>design/patterns/swipe-views.html">Swipe Views</a></li>
- <li><a href="<?cs var:toroot ?>design/patterns/fullscreen.html">Full Screen</a></li>
- <li><a href="<?cs var:toroot ?>design/patterns/selection.html">Selection</a></li>
- <li><a href="<?cs var:toroot ?>design/patterns/confirming-acknowledging.html">Confirming & Acknowledging</a></li>
<li><a href="<?cs var:toroot ?>design/patterns/notifications.html">Notifications</a></li>
<li><a href="<?cs var:toroot ?>design/patterns/widgets.html">Widgets</a></li>
- <li><a href="<?cs var:toroot ?>design/patterns/settings.html">Settings</a></li>
- <li><a href="<?cs var:toroot ?>design/patterns/help.html">Help</a></li>
+ <li><a href="<?cs var:toroot ?>design/patterns/swipe-views.html">Swipe Views</a></li>
+ <li><a href="<?cs var:toroot ?>design/patterns/fullscreen.html">Full Screen</a></li>
+ <li><a href="<?cs var:toroot ?>design/patterns/confirming-acknowledging.html">Confirming & Acknowledging</a></li>
+ <li><a href="<?cs var:toroot ?>design/patterns/pure-android.html">Pure Android</a></li>
<li><a href="<?cs var:toroot ?>design/patterns/compatibility.html">Compatibility</a></li>
<li><a href="<?cs var:toroot ?>design/patterns/accessibility.html">Accessibility</a></li>
- <li><a href="<?cs var:toroot ?>design/patterns/pure-android.html">Pure Android</a></li>
- </ul>
- </li>
-
- <li class="nav-section">
- <div class="nav-section-header"><a href="<?cs var:toroot ?>design/building-blocks/index.html">Building Blocks</a></div>
- <ul>
- <li><a href="<?cs var:toroot ?>design/building-blocks/tabs.html">Tabs</a></li>
- <li><a href="<?cs var:toroot ?>design/building-blocks/lists.html">Lists</a></li>
- <li><a href="<?cs var:toroot ?>design/building-blocks/grid-lists.html">Grid Lists</a></li>
- <li><a href="<?cs var:toroot ?>design/building-blocks/scrolling.html">Scrolling</a></li>
- <li><a href="<?cs var:toroot ?>design/building-blocks/spinners.html">Spinners</a></li>
- <li><a href="<?cs var:toroot ?>design/building-blocks/buttons.html">Buttons</a></li>
- <li><a href="<?cs var:toroot ?>design/building-blocks/text-fields.html">Text Fields</a></li>
- <li><a href="<?cs var:toroot ?>design/building-blocks/seek-bars.html">Seek Bars</a></li>
- <li><a href="<?cs var:toroot ?>design/building-blocks/progress.html">Progress & Activity</a></li>
- <li><a href="<?cs var:toroot ?>design/building-blocks/switches.html">Switches</a></li>
- <li><a href="<?cs var:toroot ?>design/building-blocks/dialogs.html">Dialogs</a></li>
- <li><a href="<?cs var:toroot ?>design/building-blocks/pickers.html">Pickers</a></li>
+ <li><a href="<?cs var:toroot ?>design/patterns/help.html">Help</a></li>
</ul>
</li>
@@ -103,8 +64,8 @@
<div class="nav-section-header empty"><a href="<?cs var:toroot ?>design/downloads/index.html">Downloads</a></div>
</li>
- <li class="nav-section">
+<!-- <li class="nav-section">
<div class="nav-section-header empty"><a href="<?cs var:toroot ?>design/videos/index.html">Videos</a></div>
- </li>
+ </li> -->
</ul>
diff --git a/docs/html/design/downloads/index.jd b/docs/html/design/downloads/index.jd
index 0b9f59f..089a4af 100644
--- a/docs/html/design/downloads/index.jd
+++ b/docs/html/design/downloads/index.jd
@@ -3,107 +3,16 @@
@jd:body
-<p>You may use these materials without restriction to facilitate your app design
-and implementation.</p>
+<p>For icons, sticker sheets, and other downloadable resources, visit the
+<a href="http://www.google.com/design">Design site</a> or use the links below. </p>
-
-<h2 id="stencils">Phone & Tablet Stencils</h2>
-
-<div class="cols">
- <div class="col-5">
-
-
-<p>Drag and drop your way to beautifully designed Android apps. The stencils feature the
-rich typography, colors, interactive controls, and icons found throughout Android, along with
-phone and tablet outlines to frame your creations. Source files for icons and controls are also
-available.</p>
-
- </div>
- <div class="col-4">
-
- <img src="{@docRoot}design/media/downloads_stencils.png">
-
- </div>
-
- <div class="col-4">
- <a class="notice-designers-material"
- style="width:218px;"
- href="http://www.google.com/design/spec/resources/layout-templates.html">
- <div>
- <h3>Material Design</h3>
- <p>Layout Templates<p>
- </div>
- </a>
- </div>
-
- <div class="col-4">
- <a class="notice-designers-material"
- style="width:218px;"
- href="http://www.google.com/design/spec/resources/sticker-sheets-icons.html">
- <div>
- <h3>Material Design</h3>
- <p>Sticker Sheets<p>
- </div>
- </a>
- </div>
-
- <div class="col-4">
-<p>
- <a class="download-button" onClick="ga('send', 'event', 'Design', 'Download', 'Photoshop Sources');"
- href="{@docRoot}downloads/design/Android_Design_Stencils_Sources_20131106.zip">Adobe® Photoshop® Stencils and Sources</a>
-</p>
-
- </div>
+ <div class="resource-widget resource-flow-layout col-16"
+ data-query="collection:design/landing/resources"
+ data-cardSizes="6x2"
+ data-maxResults="6"></div>
</div>
-
-
-
-
-<h2 id="action-bar-icon-pack">Action Bar Icon Pack</h2>
-
-<div class="cols">
- <div class="col-5">
-
-<p>Action bar icons are graphic buttons that represent the most important actions people can take
-within your app. <a href="{@docRoot}design/style/iconography.html">More on Action Bar Iconography</a></p>
-<p>The download package includes icons that are scaled for various screen densities and suitable for
-use with the Holo Light and Holo Dark themes. The package also includes unstyled icons that you can
-modify to match your theme, plus source files.</p>
-
- </div>
- <div class="col-4">
-
- <img src="{@docRoot}design/media/iconography_actionbar_style.png">
-
- </div>
-
- <div class="col-4">
- <a class="notice-designers-material"
- style="width:218px;"
- href="http://www.google.com/design/spec/resources/sticker-sheets-icons.html">
- <div>
- <h3>Material Design</h3>
- <p>Sticker Sheets<p>
- </div>
- </a>
- </div>
-
- <div class="col-4">
-
-<p>
- <a class="download-button" onClick="ga('send', 'event', 'Design', 'Download', 'Action Bar Icons');"
- href="{@docRoot}downloads/design/Android_Design_Icons_20131106.zip">Action Bar Icon Pack</a>
-</p>
-
- </div>
-</div>
-
-
-
-
-
<h2 id="Wear">Android Wear Materials</h2>
<div class="cols">
@@ -243,86 +152,4 @@
<a class="download-button" onClick="ga('send', 'event', 'Design', 'Download', 'Wear Watch Face Example Specifications');"
href="{@docRoot}downloads/design/Slide_IconExample.psd">Adobe® Photoshop® Icon</a>
</div>
-</div>
-
-
-
-
-<h2 id="style">Style</h2>
-
-<div class="cols">
- <div class="col-5">
-
-<h4 id="roboto">Roboto</h4>
-<p>Ice Cream Sandwich introduced a new type family named Roboto, created specifically for the
-requirements of UI and high-resolution screens.</p>
-<p>For Android Wear, Roboto Condensed is the system font and the Regular and Light variants should be used by all Wear apps.</p>
-<p><a href="{@docRoot}design/style/typography.html">More on Roboto</a></p>
-<p><a href="http://www.google.com/fonts/specimen/Roboto" class="external-link">Roboto on Google Fonts</a></p>
-<p><a href="http://www.google.com/fonts/specimen/Roboto+Condensed" class="external-link">Roboto Condensed on Google Fonts</a></p>
-
- </div>
- <div class="col-4">
-
- <img src="{@docRoot}design/media/downloads_roboto_specimen_preview.png">
-
- </div>
-
- <div class="col-4">
- <a class="notice-designers-material"
- style="width:218px;"
- href="http://www.google.com/design/spec/resources/roboto-noto-fonts.html">
- <div>
- <h3>Material Design</h3>
- <p>Roboto Font<p>
- </div>
- </a>
- </div>
-
- <div class="col-4">
-
-<p>
- <a class="download-button" onClick="ga('send', 'event', 'Design', 'Download', 'Roboto ZIP');"
- href="{@docRoot}downloads/design/roboto-1.2.zip">Roboto</a>
- <a class="download-button" onClick="ga('send', 'event', 'Design', 'Download', 'Roboto Specemin Book');"
- href="{@docRoot}downloads/design/Roboto_Specimen_Book_20131031.pdf">Specimen Book</a>
-</p>
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-5">
-
-<h4>Color</h4>
-<p>In Android's color palette, each color has a corresponding darker
-shade that can be used as a complement when needed.</p>
-<p><a href="{@docRoot}design/style/color.html">More on Color</a></p>
-
- </div>
- <div class="col-4">
-
- <img src="{@docRoot}design/media/downloads_color_swatches.png">
-
- </div>
-
- <div class="col-4">
- <a class="notice-designers-material"
- style="width:218px;"
- href="http://www.google.com/design/spec/resources/color-palettes.html">
- <div>
- <h3>Material Design</h3>
- <p>Color Palettes<p>
- </div>
- </a>
- </div>
-
- <div class="col-4">
-
-<p>
- <a class="download-button" onClick="ga('send', 'event', 'Design', 'Download', 'Color Swatches');"
- href="{@docRoot}downloads/design/Android_Design_Color_Swatches_20120229.zip">Color Swatches</a>
-</p>
-
- </div>
-</div>
+</div>
\ No newline at end of file
diff --git a/docs/html/design/get-started/creative-vision.jd b/docs/html/design/get-started/creative-vision.jd
index 3955494..974d5d0 100644
--- a/docs/html/design/get-started/creative-vision.jd
+++ b/docs/html/design/get-started/creative-vision.jd
@@ -7,10 +7,9 @@
<div class="vspace size-1"> </div>
<p itemprop="description">
- Starting with Ice Cream Sandwich, we focused the design of
- Android around these three overarching goals, which apply
- to our core apps as well as the system at large.
- As you work with Android, consider these goals.
+ Android design is shaped by three overarching goals for users that apply
+ to apps as well as the system at large. As you work with Android,
+ keep these goals in mind.
</p>
<div class="vspace size-1"> </div>
diff --git a/docs/html/design/index.jd b/docs/html/design/index.jd
index 74af6e7..638f35b 100644
--- a/docs/html/design/index.jd
+++ b/docs/html/design/index.jd
@@ -14,17 +14,14 @@
<img class="dac-hero-image" src="/design/media/hero-material-design.png">
</div>
<div class="col-1of2 col-pull-1of2">
- <h1 class="dac-hero-title">Up and running with Material Design</h1>
+ <h1 class="dac-hero-title">Up and running with material design</h1>
<p class="dac-hero-description">
- Android 5.0 introduces a design metaphor inspired by paper and ink that provides a reassuring sense of tactility.
+ Android uses a new design metaphor inspired by paper and ink that provides a reassuring
+ sense of tactility. Visit the <a href="http://www.google.com/design/spec/material-design/introduction">material design</a> site for more resources.
</p>
<a class="dac-hero-cta" href="https://www.google.com/design/spec/material-design/introduction.html">
<span class="dac-sprite dac-auto-chevron"></span>
- Introducing Material Design
- </a><br>
- <a class="dac-hero-cta" href="/design/material/index.html">
- <span class="dac-sprite dac-auto-chevron"></span>
- Material Design for Android
+ Introducing material design
</a><br>
<a class="dac-hero-cta" href="https://www.google.com/design/spec/resources/color-palettes.html">
<span class="dac-sprite dac-auto-chevron"></span>
@@ -56,7 +53,7 @@
<section class="dac-section dac-light"><div class="wrap">
<h1 class="dac-section-title">Pure Android</h1>
<div class="dac-section-subtitle">
- This is a place holder paragraph. Some text here would povide some context.
+ Design around Android's capabilities and conventions to give users the best experience.
</div>
<div class="resource-widget resource-flow-layout col-16"
data-query="collection:design/landing/pureandroid"
@@ -67,7 +64,7 @@
<section class="dac-section dac-gray"><div class="wrap">
<h1 class="dac-section-title">Resources</h1>
<div class="dac-section-subtitle">
- This is a place holder paragraph. Some text here would povide some context.
+ Essential downloads, stencils, and tools to help you create your design.
</div>
<div class="resource-widget resource-flow-layout col-16"
data-query="collection:design/landing/resources"
diff --git a/docs/html/design/patterns/gestures.jd b/docs/html/design/patterns/gestures.jd
deleted file mode 100644
index b471ee3..0000000
--- a/docs/html/design/patterns/gestures.jd
+++ /dev/null
@@ -1,135 +0,0 @@
-page.title=Gestures
-page.tags=gesture,input,touch
-@jd:body
-
-<a class="notice-designers-material"
- href="http://www.google.com/design/spec/patterns/gestures.html">
- <div>
- <h3>Material Design</h3>
- <p>Gestures<p>
- </div>
-</a>
-
-<p>Gestures allow users to interact with your app by manipulating the screen objects you provide. The
-following table shows the core gesture set that is supported in Android.</p>
-
-<div class="vspace size-2"> </div>
-
-<div class="cols">
-
- <div class="col-4">
- <img src="{@docRoot}design/media/gesture_touch.png">
- <h4>Touch</h4>
- <p>Triggers the default functionality for a given item.</p>
- <ul>
- <li class="no-bullet with-icon action">
- <h4>Action</h4>
- <p>Press, lift</p></li>
- </ul>
- </div>
-
- <div class="col-4">
- <img src="{@docRoot}design/media/gesture_longtouch.png">
- <h4>Long press</h4>
- <p>Enters data selection mode. Allows you to select one or more items in a view and act upon
- the data using a contextual action bar. Avoid using long press for showing contextual menus.</p>
- <ul>
- <li class="no-bullet with-icon action">
- <h4>Action</h4>
- <p>Press, wait, lift</p></li>
- </ul>
- </div>
-
- <div class="col-4">
- <img src="{@docRoot}design/media/gesture_swipe.png">
- <h4>Swipe or drag</h4>
- <p>Scrolls overflowing content, or navigates between views in the same hierarchy. Swipes are
- quick and affect the screen even after the finger is picked up. Drags are slower and more precise,
- and the screen stops responding when the finger is picked up.</p>
- <ul>
- <li class="no-bullet with-icon action">
- <h4>Action</h4>
- <p>Press, move, lift</p></li>
- </ul>
- </div>
-
-</div>
-
-<div class="vspace size-2"> </div>
-
-<div class="cols">
-
- <div class="col-4">
- <img src="{@docRoot}design/media/gesture_drag.png">
- <h4>Long press drag</h4>
- <p>Rearranges data within a view, or moves data into a container (e.g. folders on Home Screen).</p>
- <ul>
- <li class="no-bullet with-icon action">
- <h4>Action</h4>
- <p>Long press, move, lift</p></li>
- </ul>
- </div>
-
- <div class="col-4">
- <img src="{@docRoot}design/media/gesture_doubletouch.png">
- <h4>Double touch </h4>
- <p> Scales up a standard amount around the target with each repeated gesture until reaching
- maximum scale. For nested views, scales up the smallest targetable view, or returns it to
- its original scale. Also used as a secondary gesture for text selection.</p>
- <ul>
- <li class="no-bullet with-icon action">
- <h4>Action</h4>
- <p>Two touches in quick succession</p>
- </li>
- </ul>
- </div>
-
- <div class="col-4">
- <img src="{@docRoot}design/media/gesture_doubletouchdrag.png">
- <h4>Double touch drag</h4>
- <p>Scales content by pushing away or pulling closer, centered around gesture.</p>
- <ul>
- <li class="no-bullet with-icon action">
- <h4>Action</h4>
- <p>A single touch followed in quick succession by a drag up or down:</p>
- <ul style="padding-left:1.5em;list-style-type:disc;">
- <li>Dragging up decreases content scale</li>
- <li>Dragging down increases content scale</li>
- <li>Reversing drag direction reverses scaling.</li>
- </ul>
- </li>
- </ul>
- </div>
-
-</div>
-
-
-<div class="vspace size-2"> </div>
-
-<div class="cols">
-
- <div class="col-4">
- <img src="{@docRoot}design/media/gesture_pinchopen.png" style="margin-left:-4px">
- <h4>Pinch open</h4>
- <p>Zooms into content.</p>
- <ul>
- <li class="no-bullet with-icon action">
- <h4>Action</h4>
- <p>2-finger press, move outwards, lift</p></li>
- </ul>
- </div>
-
- <div class="col-4">
- <img src="{@docRoot}design/media/gesture_pinchclose.png">
- <h4>Pinch close</h4>
- <p>Zooms out of content.</p>
- <ul>
- <li class="no-bullet with-icon action">
- <h4>Action</h4>
- <p>2-finger press, move inwards, lift</p>
- </li>
- </ul>
- </div>
-
-</div>
-
diff --git a/docs/html/design/patterns/index.jd b/docs/html/design/patterns/index.jd
deleted file mode 100644
index e091a29..0000000
--- a/docs/html/design/patterns/index.jd
+++ /dev/null
@@ -1,30 +0,0 @@
-page.title=Patterns
-header.justLinks=1
-footer.hide=1
-@jd:body
-
-<style>
-#landing-graphic-container {
- position: relative;
-}
-
-#text-overlay {
- position: absolute;
- left: 0;
- top: 492px;
- width: 200px;
-}
-</style>
-
-<div id="landing-graphic-container">
- <div id="text-overlay">
- <span itemprop="description">Design apps that behave in a consistent, predictable
- fashion.</span>
- <br><br>
- <a href="{@docRoot}design/patterns/new.html" class="landing-page-link">New in Android</a>
- </div>
-
- <a href="{@docRoot}design/patterns/new.html">
- <img src="{@docRoot}design/media/patterns_landing.png">
- </a>
-</div>
diff --git a/docs/html/design/patterns/multi-pane-layouts.jd b/docs/html/design/patterns/multi-pane-layouts.jd
deleted file mode 100644
index c9d3d84..0000000
--- a/docs/html/design/patterns/multi-pane-layouts.jd
+++ /dev/null
@@ -1,130 +0,0 @@
-page.title=Multi-pane Layouts
-page.tags="tablet","navigation","layout","fragment"
-page.metaDescription=Design guide with examples of how to flatten navigation and provide improved layout across the range of Android devices.
-
-@jd:body
-
-
-<a class="notice-designers-material"
- href="http://www.google.com/design/spec/layout/structure.html#structure-ui-regions">
- <div>
- <h3>Material Design</h3>
- <p>UI Regions and Guidance<p>
- </div>
-</a>
-
-<a class="notice-developers" href="{@docRoot}training/basics/fragments/index.html">
- <div>
- <h3>Developer Docs</h3>
- <p>Building a Dynamic UI with Fragments</p>
- </div>
-</a>
-
-<p>When writing an app for Android, keep in mind that Android devices
-come in many different screen sizes and types. Make sure that your app consistently provides a
-balanced and aesthetically pleasing layout by adjusting its content to varying screen sizes and
-orientations.</p>
-
-<p><em>Panels</em> are a great way for your app to achieve this. They allow you to combine multiple views into
-one compound view when a lot of horizontal screen real estate is available and by splitting them up
-when less space is available.</p>
-<h2 id="combining-views">Combining Multiple Views Into One</h2>
-
-<p>On smaller devices your content is typically divided into a master grid or list view and a detail
-view. Touching an item in the master view opens a different screen showing that item's detail
-information.</p>
-
-<img src="{@docRoot}design/media/multipane_views.png">
-
-<p>Because tablets have more screen real estate than phones, you can use panels to combine the related
-list and detail views into a single compound view. This uses the additional space more efficiently
-and makes navigating the app easier. </p>
-
-<img src="{@docRoot}design/media/multipane_view_tablet.png">
-
-<p>In general, use the pane on the right to present more information about the item you selected in the
-left pane. Make sure to keep the item in the left pane selected in order to establish the
-relationship between the panels.</p>
-<h2 id="orientation">Compound Views and Orientation Changes</h2>
-
-<p>Screens should strive to have the same functionality regardless of orientation. If you use a compound view in
-one orientation, try not to split it up when the user rotates the screen. There are several techniques
-you can use to adjust the layout after orientation change while keeping functional parity intact.</p>
-
-<div class="cols">
- <div class="col-8">
-
- <img src="{@docRoot}design/media/multipane_stretch.png">
-
- </div>
- <div class="col-5">
-
-<h4>Stretch/compress</h4>
-<p>Adjust the column width of your left pane to achieve a balanced layout in both orientations.</p>
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-8">
-
- <img src="{@docRoot}design/media/multipane_stack.png">
-
- </div>
- <div class="col-5">
-
-<h4>Stack</h4>
-<p>Rearrange the panels on your screen to match the orientation.</p>
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-8">
-
- <img src="{@docRoot}design/media/multipane_expand.png">
-
- </div>
- <div class="col-5">
-
-<h4>Expand/collapse</h4>
-<p>When the device rotates, collapse the left pane view to only show the most important information.</p>
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-8">
-
- <img src="{@docRoot}design/media/multipane_show.png">
-
- </div>
- <div class="col-5">
-
-<h4>Show/hide</h4>
-<p>If your screen cannot accommodate the compound view on rotation show the right pane in full screen view on rotation to portrait. Use the Up icon in action bar to show the parent screen.</p>
-
- </div>
-</div>
-
-
-
-
-<h2 id="checklist">Checklist</h2>
-
-<ul>
-<li>
-<p>Plan in advance on how your app scales to different screen sizes and screen orientations.</p>
-</li>
-<li>
-<p>Identify the most appropriate method for the panels in your compound views to reorganize
- themselves when screen orientation changes.</p>
-</li>
-<li>
-<p>Look for opportunities to consolidate your views into multi-panel compound views.</p>
-</li>
-<li>
-<p>Make sure that your screens try to provide functional parity after the screen orientation
- changes.</p>
-</li>
-</ul>
diff --git a/docs/html/design/patterns/navigation-drawer.jd b/docs/html/design/patterns/navigation-drawer.jd
deleted file mode 100644
index b359470..0000000
--- a/docs/html/design/patterns/navigation-drawer.jd
+++ /dev/null
@@ -1,346 +0,0 @@
-page.title=Navigation Drawer
-page.tags=DrawerLayout,SlidingPaneLayout
-@jd:body
-
-
-<a class="notice-designers-material"
- href="http://www.google.com/design/spec/layout/structure.html#structure-side-nav">
- <div>
- <h3>Material Design</h3>
- <p>Side Nav<p>
- </div>
-</a>
-
-<a class="notice-developers" href="{@docRoot}training/implementing-navigation/nav-drawer.html">
- <div>
- <h3>Developer Docs</h3>
- <p>Creating a Navigation Drawer</p>
- </div>
-</a>
-
-
-<p>The navigation drawer is a panel that transitions in from the left edge of the screen and
-displays the app’s main navigation options.</p>
-
-
-<h4>Displaying the navigation drawer</h4>
-
-<p>The user can bring the navigation drawer onto the screen by swiping from the left edge of the
-screen or by touching the application icon on the action bar.</p>
-
-<p>As the navigation drawer expands, it overlays the content but not the action bar. When the
-drawer is fully extended, the action bar adjusts its content by replacing the current action
-bar title with the app name and removing all actions that are contextual to the view underneath
-the navigation drawer. The overflow menu with the standard action items for Settings and Help
-remains visible.</p>
-
-<img src="{@docRoot}design/media/navigation_drawer_overview.png">
-<div class="figure-caption">
- The user can open the drawer panel by touching the navigation drawer indicator.
-</div>
-
-<p>Because they are transient, navigation drawers make views less cluttered. You can also use
-them at deeper levels in the navigation hierarchy, allowing users to switch to your app's most
-important screens from anywhere in the app.</p>
-
-<img src="{@docRoot}design/media/navigation_drawer_open_from_lower.png">
-<div class="figure-caption">
- Open the drawer from anywhere in your app by swiping from the left edge of the screen.
-</div>
-
-
-<h4>Dismissing the navigation drawer</h4>
-
-<p> When the navigation drawer is expanded, the user can dismiss it in one of four ways: </p>
-<ul>
- <li>Touching the content outside the navigation drawer</li>
- <li>Swiping to the left anywhere on the screen (including edge swipe from right)</li>
- <li>Touching the app icon/title in the action bar</li>
- <li>Pressing Back</li>
-</ul>
-
-
-<h2 id="WhenToUse"> When to Use the Navigation Drawer </h2>
-
-<p> The navigation drawer is not a general replacement for top-level navigation via spinners
-or tabs. The structure of your app should guide your choice of which pattern to use for
-top-level switching. For more information on top-level switching mechanisms, see the
-<a href="{@docRoot}design/patterns/app-structure.html">Application Structure</a> design pattern.</p>
-<p> Here are some examples of where navigation drawers work best:</p>
-
-<h4>More than 3 top-level views</h4>
-<p> Navigation drawers are great for displaying a large number of navigation targets
-concurrently. Use the navigation drawer if you have more than 3 unique top-level views.
-If not, use fixed tabs for top-level organization to ease discovery and interaction.</p>
-
-<h4>Cross-navigation from lower levels</h4>
-<p> If your app requires cross-navigating between lower-level screens, consider using the
-navigation drawer. Because it is accessible from anywhere in the app, the drawer enables
-efficient navigation from lower-level screens to other important places in your app.</p>
-
-<img src="{@docRoot}design/media/navigation_drawer_cross_nav.png">
-<div class="figure-caption">
- The navigation drawer makes cross-navigation at lower levels possible.
-</div>
-
-
-<h4>Deep navigation branches</h4>
-<p> If you have particularly deep branches, navigating to the top-level of your app can become
-repetitive and cumbersome with Up and Back alone. Since navigation drawers are accessible from
-anywhere in the app, navigation up to the top level is faster and more efficient.</p>
-
-<img src="{@docRoot}design/media/navigation_drawer_quick_to_top.png">
-<div class="figure-caption">
- The navigation drawer allows for quick jumps to the top-level of your app, removing the need
- for repetitive Back or Up sequences.
-</div>
-
-
-<h2 id="Hubs">Navigation Hubs</h2>
-
-<p>The navigation drawer is a reflection of your app’s structure and displays its major
-navigation hubs. Think of navigation hubs as those places in your app that a user will want
-to visit frequently or use as a jumping-off point to other parts of the app.
-At a minimum, the navigation hubs are the top-level views, since they correspond to your app’s
-major functional areas.</p>
-<p> If your app’s structure is deep, you can add screens from lower levels that your users will
-likely visit often and make those navigation hubs as well.</p>
-
-<img src="{@docRoot}design/media/navigation_drawer_navigation_hubs.png">
-<div class="figure-caption">
- The navigation drawer contains all of your app's navigation hubs. Include your top level
- screens as well as important lower-level screens.
-</div>
-
-<div class="cols">
- <div class="col-8">
- <p> To facilitate access to the navigation drawer on navigation hubs, all screens that
- correspond to an entry in your navigation drawer should show the navigation drawer indicator
- next to the application icon in the action bar. Touching the app icon causes the navigation
- drawer to slide in from the left. </p>
- <p> All other lower-level screens show the traditional Up indicator next to the application
- icon. The drawer is still accessible with an edge-swipe, but is not featured in the action bar.</p>
- </div>
- <div class="col-5">
- <img src="{@docRoot}design/media/navigation_drawer_indicator_big.png">
- <div class="figure-caption">
- App icon with navigation drawer indicator.
- </div>
- </div>
-</div>
-
-
-<h2 id="Content">Content of the Navigation Drawer</h2>
-
-<p> Keep the content of the navigation drawer focused on app navigation. Expose the navigation
-hubs of your app as list items inside the navigation drawer - one item per row.
-
-<div class="cols">
- <div class="col-8">
- <h4>Titles, icons, and counters</h4>
- <p> You can structure navigation targets by adding titles. The titles are not interactive,
- but just organize navigation targets into functional topics. If you have many navigation
- targets, use titles to orient the user within the drawer.</p>
- <p> Navigation targets can have optional leading icons as well as trailing counters. Use
- the counters to inform users about a changed state of data in the corresponding view.</p>
- </div>
- <div class="col-5">
- <img src="{@docRoot}design/media/navigation_drawer_titles_icons.png">
- <div class="figure-caption">
- Use titles and icons to organize your drawer.
- </div>
- </div>
-</div>
-
-<div class="cols">
- <div class="col-8">
- <img src="{@docRoot}design/media/navigation_drawer_collapse.png">
- <div class="figure-caption">
- Collapsible navigation items are split. Use the left side for navigation and the right
- to collapse and expand items.
- </div>
- </div>
- <div class="col-5">
- <h4>Collapsible navigation items</h4>
- <p>If you have many views with some subordinate to others, consider collapsing them into one
- expandable item to conserve space.
- The parent in the navigation drawer then turns into a split item. The left side allows
- navigation to the parent item’s view, and the right side collapses or expands the list of
- child items. </p>
- <p> At launch, the initial state of the collapsible items is up to you. As a rule, all
- top-level view entries of the navigation drawer should be visible. If you have many collapsible
- items, consider collapsing all items to allow the user to see the top-level views in their
- entirety.</p>
- <p> When the user opens the drawer from a lower-level screen, expand the associated branch
- of the top-level view to give a stronger sense of place and highlight navigation opportunities
- close to the user’s current
- location in the app.</p>
- </div>
-</div>
-
-
-<h2 id="ActionBar">Navigation Drawers and Action Bars</h2>
-
-<p> When the user expands the navigation drawer, the task focus switches to selecting an item
-from the drawer. Because the drawer does not overlay the action bar, users may not realize that
-the items in the action bar do not pertain to the navigation drawer. </p>
-<p> To reduce confusion, adjust the content of the action bar to the following, once the drawer
-is fully expanded:</p>
-<ul>
- <li>App icon</li>
- <li>App name</li>
- <li>Remove actions from the action bar that are contextual to the underlying view (such as
- Create new, Refresh). You may retain actions with global scope, such as “Search”.</li>
- <li>Overflow menu with expected navigation targets, such as Settings and Help.</li>
-</ul>
-
-<img src="{@docRoot}design/media/navigation_drawer_open_overflow.png">
-<div class="figure-caption">
- Clean up the action bar when the drawer is fully expanded. Remove actions that are not needed
- and display your app's name in the title area.
-</div>
-
-<h4>Actions</h4>
-<div class="cols">
- <div class="col-6">
- <img src="{@docRoot}design/media/navigation_drawer_nav_and_actions.png">
- <div class="figure-caption">
- Keep actions on the right side of the action bar and in the overflow
- </div>
- </div>
- <div class="col-6">
- <p> Don’t place actions in the navigation drawer. Actions belong in the action bar, and the
- user expects to see them there. Keep in mind that not all applications use the navigation
- drawer pattern. It may be tempting to expose all your app’s capabilities in a single place,
- but keep the bigger picture in mind. Place your actions where all apps display them.</p>
- </div>
-</div>
-<div class="cols">
- <div class="col-6">
- <p> This also applies to common navigation targets, such as access to Help or the app’s
- Settings. As per style guide convention Help and Settings are always located in the action
- overflow.</p>
- </div>
- <div class="col-6">
- <img src="{@docRoot}design/media/navigation_drawer_settings_help.png">
- <div class="figure-caption">
- Keep Help and Settings in the overflow.
- </div>
- </div>
-</div>
-
-
-<h4>Contextual action bars</h4>
-<p> Sometimes the user will be in a state where a contextual action bar (CAB) appears instead
-of the app’s action bar. This typically happens when the user selects text or selects multiple
-items after a press-and-hold gesture. While the CAB is visible, you should still allow the
-user to open the navigation drawer using an edge swipe. However, replace the CAB with the
-standard action bar while the navigation drawer is open. When the user dismisses the drawer,
-re-display the CAB.</p>
-
-<img src="{@docRoot}design/media/navigation_drawer_CAB.png">
-<div class="figure-caption">
- Hide contextual action bars while the drawer is visible.
-</div>
-
-<p>If the user navigates away from a view with selected content, deselect the content before
-before navigating to the new view.</p>
-
-
-<h2 id="Interaction">Interaction Details</h2>
-
-<h4>Introduce the user to the drawer at first use</h4>
-<p> Upon first launch of your app, introduce the user to the navigation drawer by
-automatically opening it. This ensures that users know about the navigation drawer and prompts
-them to learn about the structure of your app by exploring its content. Continue showing the
-drawer upon subsequent launches until the user actively expands the navigation drawer manually.
-Once you know that the user understands how to open the drawer, launch the app with the
-navigation drawer closed. </p>
-
-<img src="{@docRoot}design/media/navigation_drawer_first_run.png">
-<div class="figure-caption">
- At first use, show the navigation drawer automatically to help the user learn the
- functionality and structure of your app.
-</div>
-
-<h4>Give the user a quick peek</h4>
-<p> If the user touches the very left edge of the screen (within 20 dp from the left), have the
-drawer peek out as soon as the finger makes contact with the display. This promotes accidental
-discovery and provides richer feedback. </p>
-
-<img src="{@docRoot}design/media/navigation_drawer_peek.png">
-<div class="figure-caption">
- The navigation drawer peeks out when the user touches the very left edge of the screen.
-</div>
-
-<h4>Highlights</h4>
-<p> When you open the navigation drawer from a screen that is represented inside the drawer,
-highlight its entry in the drawer. Vice versa, if you open the drawer from a screen that is
-not listed in the drawer, none of the items of the drawer should be highlighted.</p>
-
-
-<h2 id="ImpactOnNav">Impact of Drawer on Overall App Navigation</h2>
-
-<p>The navigation drawer is an alternative to other top-level navigation patterns. To make apps
-with navigation drawers work consistently with apps that use a tab or spinner pattern, remember
-that all navigation requirements for system Back and Up apply.</p>
-<p>Pay special attention to the following situations:</p>
-
-<h4>System Back at the top level of the app</h4>
-<p>Touching System Back at the app’s top level never opens the navigation drawer. Instead,
-System Back behaves according to the navigation rules for the top level, such as navigating
-to the previous app within the task or navigating to the Home screen.</p>
-
-<img src="{@docRoot}design/media/navigation_drawer_top_out.png">
-<div class="figure-caption">
- System Back does not show the drawer, but behaves according to the navigation rules for
- the top level.
-</div>
-
-<h4>System Back after cross navigation to lower hierarchy levels</h4>
-<p>If the user navigates to a lower hierarchy screen from the navigation drawer and the screen
-has a direct parent, then the Back stack is reset and Back points to the target screen’s parent.
-This Back behavior is the same as when a user navigates into an app from a notification.</p>
-
-<img src="{@docRoot}design/media/navigation_drawer_reset_backstack.png">
-<div class="figure-caption">
- Reset the Back stack if your lower-level navigation target has direct parents.
-</div>
-
-
-<h2 id="Style">Style</h2>
-
-<p>The width of the navigation drawer depends on the content you want to display, but should be
-between a minimum of 240 dp and a maximum of 320 dp. The height of the individual line items
-should not fall below 48 dp. See the layout guideline below for recommendations on padding and
-spacing.</p>
-
-<img src="{@docRoot}design/media/navigation_drawer_layout.png">
-<div class="figure-caption">
- Layout guidelines for the navigation drawer.
-</div>
-
-
-<p>Pick the drawer background to best match your app’s theme. See the following examples
-for a Holo light and a Holo dark themed drawer.</p>
-
-<img src="{@docRoot}design/media/navigation_drawer_holo_dark_light.png">
-<div class="figure-caption">
- Navigation drawers in Holo light and Holo dark themed apps.
-</div>
-
-
-<h2 id="Checklist">Navigation Drawer Checklist</h2>
-
-<p>Even if you already support a similar navigation drawer, update your drawer to this
-pattern to make sure that:</p>
-<ul>
- <li>The action bar remains in place and adjusts its content.</li>
- <li>Your navigation drawer overlays the content.</li>
- <li>Any view represented in the drawer has a navigation drawer indicator in its action bar
- that allows the drawer to be opened by touching the app icon.</li>
- <li>You take advantage of the new visual drawer transition.</li>
- <li>Any view not represented in the drawer maintains the traditional Up indicator in its action bar.</li>
- <li>You stay in sync with the general navigation patterns for Up and Back.</li>
-</ul>
-
diff --git a/docs/html/design/patterns/selection.jd b/docs/html/design/patterns/selection.jd
deleted file mode 100644
index e00f726..0000000
--- a/docs/html/design/patterns/selection.jd
+++ /dev/null
@@ -1,123 +0,0 @@
-page.title=Selection
-page.tags=actionmode,navigation,contextual
-@jd:body
-
-<a class="notice-designers-material"
- href="http://www.google.com/design/spec/patterns/selection.html">
- <div>
- <h3>Material Design</h3>
- <p>Selection<p>
- </div>
-</a>
-
-<a class="notice-developers" href="{@docRoot}guide/topics/ui/menus.html#context-menu">
- <div>
- <h3>Developer Docs</h3>
- <p>Menus: Creating Contextual Menus</p>
- </div>
-</a>
-
-
-<p>Android 3.0 changed the <em>long press</em> gesture—that is, a touch that's held in the same position for a moment—to be the global gesture to select data.. This affects the way you should
-handle multi-select and contextual actions in your apps.</p>
-
-<div class="vspace size-1"> </div>
-
-<div class="cols">
- <div class="col-6">
-
-<h4>What has changed?</h4>
-<p>In previous versions of Android, the long press gesture was universally used to display contextual
-actions for a given data item in a contextual menu.</p>
-<p>This pattern changed with Android 3.0. The long press gesture is now used to select data, combining
-contextual actions and selection management functions for selected data into a new element called
-the contextual action bar (CAB).</p>
-
- </div>
- <div class="col-7">
-
- <img src="{@docRoot}design/media/selection_context_menu.png">
- <div class="figure-caption">
- Traditional use of the long press gesture to show contextual menus.
- </div>
-
- </div>
-</div>
-
-<h4>Using the contextual action bar</h4>
-<p itemprop="description">The contextual action bar (CAB) is a temporary action bar that overlays your app's current action bar while data
-is selected. It appears after the user long-presses on a selectable data item.</p>
-
-<img src="{@docRoot}design/media/selection_cab_big.png">
-
-<div class="vspace size-1"> </div>
-
-<div class="cols">
- <div class="col-6">
-
-<p>From here the user can:</p>
-<ul>
-<li>Select additional data items by touching them.</li>
-<li>Trigger an action from the CAB that applies to all highlighted data items. The CAB then
- automatically dismisses itself.</li>
-<li>Dismiss the CAB via the navigation bar's Back button or the CAB's checkmark button. This removes
- the CAB along with all selection highlights.</li>
-</ul>
-
- </div>
- <div class="col-7">
-
- <img src="{@docRoot}design/media/selection_cab_example.png">
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-6">
-
-<h4>Selecting CAB actions</h4>
-<p>You can decide which actions and elements appear in the CAB. Use the guidelines in the <a href="actionbar.html">Action Bar
-pattern</a> to decide which items to surface at the top level and which to move to the
-action overflow.</p>
-<h4>Dynamically adjust CAB actions</h4>
-<p>In most cases you need to adjust the actions in the CAB dynamically as the user adds more items to
-the selection. Actions that apply to a single selected data item don't necessarily apply to multiple
-selected data items of the same kind.</p>
-
- </div>
- <div class="col-7">
-
- <img src="{@docRoot}design/media/selection_adjusting_actions.png">
- <div class="figure-caption">
- Adjusting actions in the CAB as additional items are selected.
- </div>
-
- </div>
-</div>
-
-<div class="note develop">
-<p><strong>Developer Guide</strong></p>
- <p>For information about how to create a contextual action bar, read
- <a href="{@docRoot}guide/topics/ui/menus.html#CAB">Using the contextual action mode</a>.</p>
-</div>
-
-
-<h2 id="checklist">Checklist</h2>
-
-<ul>
-<li>
-<p>Whenever your app supports the selection of multiple data items, make use of the contextual action
- bar (CAB).</p>
-</li>
-<li>
-<p>Reserve the long press gesture for selection exclusively. Don't use it to display traditional
- contextual menus.</p>
-</li>
-<li>
-<p>If you don't support multi-selection within a list, long press should do nothing.</p>
-</li>
-<li>
-<p>Plan the actions you want to display inside of a CAB in the same way you would plan the actions
- inside your app's action bar.</p>
-</li>
-</ul>
diff --git a/docs/html/design/patterns/settings.jd b/docs/html/design/patterns/settings.jd
deleted file mode 100644
index 9ba837a..0000000
--- a/docs/html/design/patterns/settings.jd
+++ /dev/null
@@ -1,708 +0,0 @@
-page.title=Settings
-page.tags=preferences,sharedpreferences
-@jd:body
-
-<a class="notice-designers-material"
- href="http://www.google.com/design/spec/patterns/settings.html">
- <div>
- <h3>Material Design</h3>
- <p>Settings<p>
- </div>
-</a>
-
-<a class="notice-developers" href="{@docRoot}guide/topics/ui/settings.html">
- <div>
- <h3>Developer Docs</h3>
- <p>Settings</p>
- </div>
-</a>
-
-
-<p itemprop="description">Settings is a place in your app where users indicate their preferences for how your app should
-behave. This benefits users because:</p>
-
-<ul>
-<li>You don't need to interrupt them with the same questions over and over when certain situations
-arise. The settings predetermine what will always happen in those situations (see design
-principle: <a href="{@docRoot}design/get-started/principles.html#decide-for-me">Decide for me but
-let me have the final say</a>).</li>
-<li>You help them feel at home and in control (see design principle:
-<a href="{@docRoot}design/get-started/principles.html#make-it-mine">Let me make it mine</a>).</li>
-</ul>
-
-<h2 id="flow-structure">Flow and Structure</h2>
-
-<h4 id="settings-access">Provide access to Settings in the action overflow</h4>
-
-<p>Settings is given low prominence in the UI because it's not frequently needed. Even if there's
-room in the <a href="{@docRoot}design/patterns/actionbar.html">action bar</a>, never make Settings
-an action button. Always keep it in the action overflow and label it "Settings". Place it below
-all other items except "Help".</p>
-
-<img src="{@docRoot}design/media/settings_overflow.png">
-
-<div class="vspace size-2"> </div>
-
-<h4 id="what-to-make-a-setting">Avoid the temptation to make everything a setting</h4>
-
-<p>Because Settings is a few navigational steps away, no matter how many items you have, they'll
-never clutter up the core part of your UI. This may seem like good news, but it also poses a
-challenge.</p>
-
-<p>Settings can be a tempting place to keep a lot of stuff—like a hall closet where things
-get stashed when you tidy up before company comes over. It's not a place where you spend lots of
-time, so it's easy to rationalize and ignore its cluttered condition. But when users visit
-Settings—however infrequently—they'll have the same expectations for the experience as
-they do everywhere else in your app. More settings means more choices to make, and too many are
-overwhelming.</p>
-
-<p>So don't punt on the difficult product decisions and debates that can bring on the urge to
-"just make it a setting". For each control you're considering adding to Settings, make sure it
-meets the bar:</p>
-
-<img src="{@docRoot}design/media/settings_flowchart.png">
-
-<div class="vspace size-3"> </div>
-
-<div class="cols">
- <div class="col-5 with-callouts">
-
-<h4 id="group-settings">If you still have lots of settings, group related settings together</h4>
-
-<p>The number of items an average human can hold in short-term memory is 7±2. If you
-present a list of 10 or more settings (even after applying the criteria above), users will have
-more difficulty scanning, comprehending, and processing them.</p>
-
-<p>You can remedy this by dividing some or all of the settings into groups, effectively turning
-one long list into multiple shorter lists. A group of related settings can be presented in one of
-two ways:</p>
-
-<ol>
-<li><h4>Under a section divider</h4></li>
-<li><h4>In a separate subscreen</h4></li>
-</ol>
-
-<p>You can use one or both these grouping techniques to organize your app's settings.</p>
-
-<p>For example, in the main screen of the Android Settings app, each item in the list navigates
-to a subscreen of related settings. In addition, the items themselves are grouped under section
-dividers.</p>
-
- </div>
- <div class="col-8">
-
- <img src="{@docRoot}design/media/settings_grouping.png">
-
- </div>
-</div>
-
-<p>Grouping settings is not an exact science, but here's some advice for how to approach it, based
-on the total number of settings in your app.</p>
-
-<div class="vspace size-1"> </div>
-
-<div class="cols">
- <div class="col-2">
-
-<h4>7 or fewer</h4>
-
- </div>
- <div class="col-11">
-
-<p>Don't group them at all. It won't benefit users and will seem like overkill.</p>
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-2">
-
-<h4>8 to 10</h4>
-
- </div>
- <div class="col-11">
-
-<p>Try grouping related settings under 1 or 2 section dividers. If you have any "singletons"
-(settings that don't relate to any other settings and can't be grouped under your section
-dividers), treat them as follows:</p>
-
-<ul>
-<li>If they include some of your most important settings, list them at the top without a section
-divider.</li>
-<li>Otherwise, list them at the bottom with a section divider called "OTHER", in order of
-importance.</li>
-</ul>
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-2">
-
-<h4>11 to 15</h4>
-
- </div>
- <div class="col-11">
-
-<p>Same advice as above, but try 2 to 4 section dividers.</p>
-
-<p>Also, try the following to reduce the list:</p>
-
-<ul>
-<li>If 2 or more of the settings are mainly for power users, move them out of your main Settings
-screen and into an "Advanced" subscreen. Place an item in the action overflow called "Advanced" to
-navigate to it.</li>
-<li>Look for "doubles": two settings that relate to one another, but not to any other settings.
-Try to combine them into one setting, using the design patterns described later in this section.
-For example, you might be able to redesign two related checkbox settings into one multiple choice
-setting.</li>
-</ul>
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-2">
-
-<h4>16 or more</h4>
-
- </div>
- <div class="col-11">
-
-<p>If you have any instances of 4 or more related settings, group them under a subscreen. Then use
-the advice suggested above for the reduced list size.</p>
-
- </div>
-</div>
-
-
-<h2 id="patterns">Design Patterns</h2>
-
-<div class="cols">
- <div class="col-3">
-
-<h4>Checkbox</h4>
-<p>Use this pattern for a setting that is either selected or not selected.</p>
-
- </div>
- <div class="col-10">
-
-<img src="{@docRoot}design/media/settings_checkbox.png">
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-3">
-
-<h4>Multiple choice</h4>
-<p>Use this pattern for a setting that needs to present a discrete set of options, from which the
-user can choose only one.</p>
-
- </div>
- <div class="col-10">
-
-<img src="{@docRoot}design/media/settings_multiple_choice.png">
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-3">
-
-<h4>Slider</h4>
-<p>Use this pattern for a setting where the range of values are not discrete and fall along a
-continuum.</p>
-
- </div>
- <div class="col-10">
-
-<img src="{@docRoot}design/media/settings_slider.png">
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-3">
-
-<h4>Date/time</h4>
-<p>Use this pattern for a setting that needs to collect a date and/or time from the user.</p>
-
- </div>
- <div class="col-10">
-
-<img src="{@docRoot}design/media/settings_date_time.png">
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-3">
-
-<h4>Subscreen navigation</h4>
-<p>Use this pattern for navigating to a subscreen or sequence of subscreens that guide the user
-through a more complex setup process.</p>
-<ul>
-<li>If navigating to a single subscreen, use the same title in both the subscreen and the label
-navigating to it.</li>
-<li>If navigating to a sequence of subscreens (as in this example), use a title that describes the
-first step in the sequence.</li>
-</ul>
-
- </div>
- <div class="col-10">
-
-<img src="{@docRoot}design/media/settings_subscreen_navigation.png">
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-3">
-
-<h4>List subscreen</h4>
-<p>Use this pattern for a setting or category of settings that contains a list of equivalent items.
-</p>
-<p>The label provides the name of the item, and secondary text may be used for status. (In this
-example, status is reinforced with an icon to the right of the label.) Any actions associated with
-the list appear in the action bar rather than the list itself.</p>
-
- </div>
- <div class="col-10">
-
-<img src="{@docRoot}design/media/settings_list_subscreen.png">
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-3">
-
-<h4>Master on/off switch</h4>
-<p>Use this pattern for a category of settings that need a mechanism for turning on or off as a
-whole.</p>
-<p>An on/off switch is placed as the first item in the action bar of a subscreen. When the switch
-is turned off, the items in the list disappear, replaced by text that describes why the list is
-empty. If any actions require the switch to be on, they become disabled.</p>
-
- </div>
- <div class="col-10">
-
-<img src="{@docRoot}design/media/settings_master_on_off.png">
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-3">
-
-<div class="vspace size-2"> </div>
-
-<p>You can also echo the master on/off switch in the menu item that leads to the subscreen.
-However, you should only do this in cases where users rarely need to access the subscreen once
-it's initially set up and more often just want to toggle the switch.</p>
-
- </div>
- <div class="col-10">
-
-<img src="{@docRoot}design/media/settings_master_on_off_2.png">
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-3">
-
-<h4>Individual on/off switch</h4>
-<p>Use this pattern for an individual setting that requires a more elaborate description than can
-be provided in checkbox form.</p>
-<p>The on/off switch only appears in the subscreen so that users aren't able to toggle it without
-also being exposed to the descriptive text. Secondary text appears below the setting label to
-reflect the current selection.</p>
-<p>In this example, Android Beam is on by default. Since users might not know what this setting
-does, we made the status more descriptive than just "On".</p>
-
- </div>
- <div class="col-10">
-
-<img src="{@docRoot}design/media/settings_individual_on_off.png">
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-3">
-
-<h4>Dependency</h4>
-<p>Use this pattern for a setting that changes availability based on the value of another setting.
-</p>
-<p>The disabled setting appears below its dependency, without any indentation. If the setting
-includes a status line, it says "Unavailable", and if the reason isn't obvious, a brief
-explanation is included in the status.</p>
-<p>If a given setting is a dependency to 3 or more settings, consider using a subscreen with a
-master on/off switch so that your main settings screen isn't cluttered by lots of disabled items.
-</p>
-
- </div>
- <div class="col-10">
-
-<img src="{@docRoot}design/media/settings_dependency.png">
-
- </div>
-</div>
-
-<h2 id="defaults">Defaults</h2>
-
-<p>Take great care in choosing default values for each of your settings. Because settings
-determine app behavior, your choices will contribute to users' first impressions of your app. Even
-though users can change settings, they'll expect the initial states to be sensible. The following
-questions (when applicable) may help inform your decisions:</p>
-
-<ul>
-<li>Which choice would most users be likely to choose on their own if there were no default?</li>
-<li>Which choice is the most neutral or middle-of-the-road?</li>
-<li>Which choice is the least risky, controversial, or over-the-top?</li>
-<li>Which choice uses the least amount of battery or mobile data?</li>
-<li>Which choice best supports the design principle
-<a href="{@docRoot}design/get-started/principles.html#never-lose-my-stuff">Never lose my stuff</a>?</li>
-<li>Which choice best supports the design principle
-<a href="{@docRoot}design/get-started/principles.html#interrupt-only-if-important">Only interrupt
-me if it's important</a>?
-</li>
-</ul>
-
-<h2 id="writing">Writing Guidelines</h2>
-
-<h4>Label clearly and concisely</h4>
-
-<p>Writing a good label for a setting can be challenging because space is very limited. You only
-get one line, and it's incredibly short on the smallest of devices. Follow these guidelines to
-make your labels brief, meaningful, and scannable:</p>
-
-<ul>
-<li>Write each label in sentence case (i.e. only the first word and proper nouns are capitalized).
-</li>
-<li>Don't start a label with an instructional verb like "Set", "Change", "Edit", "Modify",
-"Manage", "Use", "Select", or "Choose". Users already understand that they can do these things to
-settings.</li>
-<li>Likewise, don't end a label with a word like "setting" or "settings". It's already implied.
-</li>
-<li>If the setting is part of a grouping, don't repeat the word(s) used in the section divider or
-subscreen title.</li>
-<li>Avoid starting a label with a negative word like "Don't" or "Never". For example, "Don't
-allow" could be rephrased to "Block".</li>
-<li>Steer clear of technical jargon as much as possible, unless it's a term widely understood by
-your target users. Use common verbs and nouns to convey the setting's purpose rather than its
-underlying technology.</li>
-<li>Don't refer to the user. For example, for a setting allowing the user to turn notifications on
-or off, label it "Notifications" instead of "Notify me".</li>
-</ul>
-
-<p>Once you've decided on labels for your settings, be sure to preview them on an
-<a href="{@docRoot}design/style/metrics-grids.html">LDPI handset</a> in portrait to make sure
-they'll fit everywhere.</p>
-
-<h4>Secondary text below is for status, not description…</h4>
-
-<p>Before Ice Cream Sandwich, we often displayed secondary text below a label to further describe
-it or provide instructions. Starting in Ice Cream Sandwich, we're using secondary text for status.
-</p>
-
-<div class="cols">
- <div class="col-4">
-
- <div class="do-dont-label bad emulate-content-left-padding">Before</div>
-
- <table class="ui-table bad emulate-content-left-padding">
- <thead>
- <tr>
- <th class="label">
- Screen timeout
- </th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td class="secondary-text">
- Adjust the delay before the screen automatically turns off
- </td>
- </tr>
- </tbody>
- </table>
-
- </div>
- <div class="col-4">
-
- <div class="do-dont-label good">After</div>
-
- <table class="ui-table good">
- <thead>
- <tr>
- <th class="label">
- Sleep
- </th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td class="secondary-text">
- After 10 minutes of inactivity
- </td>
- </tr>
- </tbody>
- </table>
-
- </div>
-</div>
-
-<p>Status in secondary text has the following benefits:</p>
-<ul>
-<li>Users can see at a glance what the current value of a setting is without having to navigate
-any further.</li>
-<li>It applies the design principle
-<a href="{@docRoot}design/get-started/principles.html#keep-it-brief">Keep it brief</a>, which
-users greatly appreciate.</li>
-</ul>
-
-<h4>…unless it's a checkbox setting</h4>
-<p>There's one important exception to the using secondary text for status: checkbox settings.
-Here, use secondary text for description, not status. Status below a checkbox is unnecessary
-because the checkbox already indicates it. The reason why it's appropriate to have a description
-below a checkbox setting is because—unlike other controls—it doesn't display a dialog
-or navigate to another screen where additional information can be provided.</p>
-
-<p>That said, if a checkbox setting's label is clear enough on its own, there's no need to also
-provide a description. Only include one if necessary.</p>
-
-<p>Follow these guidelines to write checkbox setting descriptions:</p>
-<ul>
-<li>Keep it to one sentence and don't use ending punctuation.</li>
-<li>Convey what happens when the setting is checked, phrased in the form of a command. Example:
-"Allow data exchange", not "Allows data exchange".</li>
-<li>Avoid repetition by choosing words that don't already appear in the label.</li>
-<li>Don't refer to the user unless it's necessary for understanding the setting.</li>
-<li>If you must refer to the user, do so in the second person ("you") rather than the first person
-("I"). Android speaks to users, not on behalf of them.</li>
-</ul>
-
-<h4>Writing examples</h4>
-
-<p>The following are examples of changes we made to labels and secondary text in the Settings app
-in Ice Cream Sandwich.</p>
-
-<div class="cols">
- <div class="col-4">
-
- <div class="do-dont-label bad emulate-content-left-padding">Before</div>
-
- <table class="ui-table bad emulate-content-left-padding">
- <thead>
- <tr>
- <th class="label">
- Use tactile feedback
- </th>
- </tr>
- </thead>
- </table>
-
- </div>
- <div class="col-4">
-
- <div class="do-dont-label good">After</div>
-
- <table class="ui-table good">
- <thead>
- <tr>
- <th class="label">
- Vibrate on touch
- </th>
- </tr>
- </thead>
- </table>
-
- </div>
- <div class="col-5">
-
-<p>In this checkbox setting, we eliminated the throwaway word "Use" and rephrased the label to be
-more direct and understandable.</p>
-
- </div>
-
-</div>
-
-<div class="cols">
- <div class="col-4">
-
- <div class="do-dont-label bad emulate-content-left-padding">Before</div>
-
- <table class="ui-table bad emulate-content-left-padding">
- <thead>
- <tr>
- <th class="label">
- Screen timeout
- </th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td class="secondary-text">
- Adjust the delay before the screen automatically turns off
- </td>
- </tr>
- </tbody>
- </table>
-
- </div>
- <div class="col-4">
-
- <div class="do-dont-label good">After</div>
-
- <table class="ui-table good">
- <thead>
- <tr>
- <th class="label">
- Sleep
- </th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td class="secondary-text">
- After 10 minutes of inactivity
- </td>
- </tr>
- </tbody>
- </table>
-
- </div>
- <div class="col-5">
-
-<p>In this multiple choice setting, we changed the label to a friendlier term and also replaced
-the description with status. We put some descriptive words around the selected value, "10
-minutes", because on its own, the meaning could be misinterpreted as "sleep for 10 minutes".</p>
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-4">
-
- <div class="do-dont-label bad emulate-content-left-padding">Before</div>
-
- <table class="ui-table bad emulate-content-left-padding">
- <thead>
- <tr>
- <th class="label">
- Change screen lock
- </th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td class="secondary-text">
- Change or disable pattern, PIN, or password security
- </td>
- </tr>
- </tbody>
- </table>
-
- </div>
- <div class="col-4">
-
- <div class="do-dont-label good">After</div>
-
- <table class="ui-table good">
- <thead>
- <tr>
- <th class="label">
- Screen lock
- </th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td class="secondary-text">
- Pattern
- </td>
- </tr>
- </tbody>
- </table>
-
- </div>
- <div class="col-5">
-
-<p>This setting navigates to a a sequence of subscreens that allow users to choose a type of
-screen lock and then set it up. We eliminated the throwaway word "Change" in the label, and
-replaced the description with the current type of screen lock set up by the user. If the user
-hasn't set up a screen lock, the secondary text says "None".</p>
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-4">
-
- <div class="do-dont-label bad emulate-content-left-padding">Before</div>
-
- <table class="ui-table bad emulate-content-left-padding">
- <thead>
- <tr>
- <th class="label">
- NFC
- </th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td class="secondary-text">
- Use Near Field Communication to read and exchange tags
- </td>
- </tr>
- </tbody>
- </table>
-
- </div>
- <div class="col-4">
-
- <div class="do-dont-label good">After</div>
-
- <table class="ui-table good">
- <thead>
- <tr>
- <th class="label">
- NFC
- </th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td class="secondary-text">
- Allow data exchange when the phone touches another device
- </td>
- </tr>
- </tbody>
- </table>
-
- </div>
- <div class="col-5">
-
-<p>In this checkbox setting—although it's technical jargon—we kept the "NFC" label
-because: (1) we couldn't find a clear, concise alternative, and (2) user familiarity with the
-acronym is expected to increase dramatically in the next couple of years.</p>
-<p>We did, however, rewrite the description. It's far less technical than before and does a better
-job of conveying how and why you'd use NFC. We didn't include what the acronym stands for because
-it doesn't mean anything to most users and would have taken up a lot of space.</p>
-
- </div>
-</div>
-
-
-
-<h2 id="checklist">Checklist</h2>
-<ul>
-<li><p>Make sure each item in Settings meets the criteria for belonging there.</p></li>
-<li><p>If you have more than 7 items, explore ways to group related settings.</p></li>
-<li><p>Use design patterns wherever applicable so users don't face a learning curve.</p></li>
-<li><p>Choose defaults that are safe, neutral, and fit the majority of users.</p></li>
-<li><p>Give each setting a clear, concise label and use secondary text appropriately.</p></li>
-</ul>
diff --git a/docs/html/design/style/branding.jd b/docs/html/design/style/branding.jd
deleted file mode 100644
index 5995d03..0000000
--- a/docs/html/design/style/branding.jd
+++ /dev/null
@@ -1,128 +0,0 @@
-page.title=Your Branding
-page.tags=branding,logo
-@jd:body
-
-<p>Following Android design patterns doesn't mean that your app has to look the same as
-everyone else's. In Android, your app can shine as an extension of your brand. </p>
-
-<h2 id="color">Color</h2>
-
-<a class="notice-designers-material"
- href="http://www.google.com/design/spec/style/color.html#color-ui-color-application">
- <div>
- <h3>Material Design</h3>
- <p>UI Color Application<p>
- </div>
-</a>
-
-<p>Use your brand color for accent by overriding the Android framework's default blue in UI elements like checkboxes, progress bars, radio buttons, sliders, tabs, and scroll indicators.</p>
-
-<p>Look for opportunities to use high-contrast color for emphasis, for example, as the background color of the action bar or a primary button. But don't go overboard: not all actions are equal, so use it only for the one or two most important things.</p>
-<p>When customizing colors, <a href="{@docRoot}design/style/touch-feedback.html">touch feedback</a>
- should be subtle — just slightly lighter or darker than the untouched color.</p>
-
-<div class="vspace size-1"> </div>
-
-<div class="cols">
- <div class="col-6">
- <img src="{@docRoot}design/media/branding_wallet.png" style="width:94%">
- <div class="figure-caption">
- The four colors of the Google Wallet logo provide a playful accent to the four dots
- that appear as the user enters a PIN.
- </div>
- </div>
- <div class="col-6">
- <img src="{@docRoot}design/media/branding_googlemusic.png" style="width:94%">
- <div class="figure-caption">
- The Google Play Music app has an orange theme color, which is used for emphasis
- in the action bar and for accent in the selected tab, scroll indicator, and
- hyperlinks.
- </div>
- </div>
-</div>
-
-<h2 id="logo">Logo</h2>
-
-<p>Your app's <a href="{@docRoot}design/style/iconography.html#launcher">launcher icon</a> is
- a key place to incorporate your logo, because it's what
- users will look for and touch to begin using your app. You can carry the launcher
- icon through to all the screens in your app by showing it in the
- <a href="{@docRoot}design/patterns/actionbar.html">action bar</a> along
- with the name of the app.</p>
-
-<p>Another approach to consider is to have your logo take the place of the launcher icon
-and app name in the action bar.</p>
-
-
-<div class="vspace size-1"> </div>
-
-<div class="cols">
- <div class="col-6" style="padding-top:24px;">
- <img src="{@docRoot}design/media/branding_launcher_icon.png" style="width:60px;float:left;padding-right:1em;">
- <div class="figure-caption" style="width:290px;margin-left:20px;">
- Google+ reinforces its brand by carrying its launcher icon through to the action bar.
- </div>
- <img src="{@docRoot}design/media/branding_logo_icon_action_bar.png" style="width:320px;float:left;padding-right:1em;">
- </div>
- <div class="col-6">
- <img src="{@docRoot}design/media/yourbranding_app.png" style="width:320px;">
- <div class="figure-caption" style="width:320px;">
- Example of a the logo in the action bar. This works well in cases where the brand's logo matches the name of the app.
- </div>
- </div>
-</div>
-
-<h2 id="logo">Icons</h2>
-
-<a class="notice-designers-material" href="http://www.google.com/design/spec/style/icons.html">
- <div>
- <h3>Material Design</h3>
- <p>Icons<p>
- </div>
-</a>
-
-<p>If you have icons that you're already using for your app on other platforms
-and they have a distinctive look intended to fit your brand, use them on your
-Android app as well. <strong>If you take this approach, make sure your brand styling is
-applied to every single icon in your app.</strong></p>
-
-
-<div class="cols">
- <div class="col-6">
- <p>One exception: For any icon in your existing set where the symbol is different from
- Android's, use Android's symbol but give it your brand's styling. That way, users will
- understand what the purpose of the icon is based on what they've learned in other
- Android apps (Design principle:
- <a href="{@docRoot}design/get-started/principles.html#give-me-tricks">Give me tricks that
- work everywhere</a>). But the icon will still look like it belongs with all of
- your other icons as a part of your brand.</p>
-
- </div>
-
- <div class="col-6">
- <img src="{@docRoot}design/media/yourbranding_in-app-icons.png" style="width:300px;margin:12px 48px 0 16px;">
- </div>
- </div>
-</div>
-
-<div class="cols">
- <div class="col-6">
- <p><strong>Example</strong>:<br />
- </p>
- <p>The brand's normal icon for sharing on other platforms is a right arrow.
- </div>
-
- <div class="col-6 lasyout-with-list-item-margins">
-
- <div style="margin-bottom:1em;">
- <span class="do-dont-label bad" style="margin-left:12px">Don't</span>
- <span style="margin-left: 64px;" class="do-dont-label good"><strong>Do</strong></span>
- </div>
- <img src="{@docRoot}design/media/yourbranding_sharing.png" style="width:180px;">
- </div>
-</div>
-
-<p>What if you don't already have your own icons — for example, if you're creating a
-brand new app only for Android? In this case, use Android's standard icons and rely
-more on color and logo for branding. Get the Action Bar Icon Pack, available for free
-in <a href="{@docRoot}design/downloads/index.html">Downloads</a>.</p>
diff --git a/docs/html/design/style/color.jd b/docs/html/design/style/color.jd
deleted file mode 100644
index 4c5f5ab..0000000
--- a/docs/html/design/style/color.jd
+++ /dev/null
@@ -1,141 +0,0 @@
-page.title=Color
-@jd:body
-
-<style>
- .color-row {
- width: 760px;
- margin:0;
-
- display: -webkit-box;
- display: -moz-box;
- display: box;
-
- -webkit-box-orient: horizontal;
- -moz-box-orient: horizontal;
- box-orient: horizontal;
-
- cursor: pointer;
-
- -webkit-user-select: none;
- user-select: none;
- /* nested user-select in FF is broken as of Jan 2012, don't use it */
- }
-
- .color-row-container {
- line-height: 0; /* to remove more top space in FF for -moz-box elements */
- }
-
- .color-row-container + .color-row-container {
- margin-top: -10px !important;
- }
-
- .color-row li {
- margin-left: 0 !important;
- position: relative;
- list-style-type: none;
- height: 80px;
- display: block;
-
- -webkit-box-flex: 1;
- -moz-box-flex: 1;
- box-flex: 1;
- }
-
- .color-row li:before {
- display: none;
- }
-
- .color-row li.thin {
- height: 40px;
- }
-
- .color-row li span {
- display: none;
- position: absolute;
- top: -30px;
- left: 50%;
- margin-left: -2.5em;
- width: 5em;
- background-color: #fff;
- padding: 10px;
- font-weight: 600;
- line-height: 20px;
- text-align: center;
- box-shadow: 0 5px 5px rgba(0,0,0,0.1);
- cursor: text;
-
- -webkit-user-select: text;
- user-select: text;
- /* nested user-select in FF is broken as of Jan 2012, don't use it */
- }
-
- .color-row li:hover span {
- display: block;
- }
-
- /* triangle callout */
- .color-row li span:after {
- content: '';
- display: block;
- position: absolute;
- left: 50%;
- bottom: -16px;
- border: 8px solid transparent;
- border-top-color: #fff;
- width: 0;
- height: 0;
- margin-left: -8px;
- }
-</style>
-
-<a class="notice-designers-material" href="http://www.google.com/design/spec/style/color.html">
- <div>
- <h3>Material Design</h3>
- <p>Color<p>
- </div>
-</a>
-
-<p>Use color primarily for emphasis. Choose colors that fit with your brand and provide good contrast
-between visual components. Note that red and green may be indistinguishable to color-blind users.</p>
-
- <div class="color-row-container">
- <ul class="color-row">
- <li><span>#33b5e5</span></li>
- <li><span>#aa66cc</span></li>
- <li><span>#99cc00</span></li>
- <li><span>#ffbb33</span></li>
- <li><span>#ff4444</span></li>
- </ul>
- </div>
-
- <div class="color-row-container">
- <ul class="color-row">
- <li class="thin"><span>#0099cc</span></li>
- <li class="thin"><span>#9933cc</span></li>
- <li class="thin"><span>#669900</span></li>
- <li class="thin"><span>#ff8800</span></li>
- <li class="thin"><span>#cc0000</span></li>
- </ul>
- </div>
-
-<h2 id="palette">Palette</h2>
-
-<p>Blue is the standard accent color in Android's color palette. Each color has a corresponding darker
-shade that can be used as a complement when needed.</p>
-<p><a onClick="ga('send', 'event', 'Design', 'Download', 'Color Swatches (@color page)');"
- href="{@docRoot}downloads/design/Android_Design_Color_Swatches_20120229.zip">Download the swatches</a></p>
-
-<img src="{@docRoot}design/media/color_spectrum.png">
-
-<script>
- $(document).ready(function() {
- $('.color-row li').each(function() {
- var color = $(this).text();
- $(this).css('background-color', color);
- $(this).find('span')
- .css('color', color)
- .text(color.toUpperCase());
- });
-
- });
-</script>
diff --git a/docs/html/design/style/iconography.jd b/docs/html/design/style/iconography.jd
deleted file mode 100644
index 8b6f3ab..0000000
--- a/docs/html/design/style/iconography.jd
+++ /dev/null
@@ -1,601 +0,0 @@
-page.title=Iconography
-page.tags="icons"
-meta.tags="icons, googleplay, listing, branding"
-page.titleFriendly=Guidelines for creating your app's icons
-@jd:body
-
-<img src="{@docRoot}design/media/iconography_overview.png">
-
-
-<p>An icon is a graphic that takes up a small portion of screen real estate and provides a quick,
-intuitive representation of an action, a status, or an app.</p>
-
-<p>When you design icons for your app, it's important to keep in mind that your
-app may be installed on a variety of devices that offer a range of
-pixel densities, as mentioned in
-<a href="{@docRoot}design/style/devices-displays.html">Devices
-and Displays</a>. But you can make your icons look great on all devices
-by providing each icon in multiple sizes. When your app runs, Android checks the characteristics of
-the device screen and loads the appropriate density-specific assets for your app. </p>
-
-<p>Because you will deliver each icon in multiple sizes to support different densities,
-the design guidelines below
-refer to the icon dimensions in <acronym title="density-independent pixels">dp</acronym>
-units, which are based on the pixel dimensions of a medium-density (MDPI) screen.</p>
-
-<img src="{@docRoot}design/media/devices_displays_density@2x.png" alt="" height="160" />
-
-<p>So, to create an icon for different densities, you should follow the <strong>2:3:4:6:8
-scaling ratio</strong> between the five primary densities (medium, high, x-high, xx-high, and
-xxx-high respectively). For example, consider that the size for a launcher icon is specified to be
-48x48 dp. This means the baseline (MDPI) asset is 48x48 px, and the
-high-density(HDPI) asset should be 1.5x the baseline at 72x72 px, and the x-high
-density (XHDPI) asset should be 2x the baseline at 96x96 px, and so on.</p>
-
-<p class="note"><strong>Note:</strong> Android also supports low-density (LDPI) screens,
-but you normally don't need to create custom assets at this size because Android
-effectively down-scales your HDPI assets by 1/2 to match the expected size.</p>
-
-
-
-
-<h2 id="launcher">Launcher</h2>
-
-<a class="notice-designers-material"
- href="http://www.google.com/design/spec/style/icons.html#icons-product-icons">
- <div>
- <h3>Material Design</h3>
- <p>Product Icons<p>
- </div>
-</a>
-
-<p>The launcher icon is the visual representation of your app on the Home or All Apps screen. Since the
-user can change the Home screen's wallpaper, make sure that your launcher icon is clearly visible on
-any type of background.</p>
-
-<div class="cols">
- <div class="col-4">
-
- <img src="{@docRoot}design/media/iconography_launcher_size.png">
-
- </div>
- <div class="col-4">
-
- <img src="{@docRoot}design/media/iconography_launcher_focal.png">
-
- </div>
- <div class="col-4">
-
- <img src="{@docRoot}design/media/iconography_launcher_style.png">
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-4">
-
- <h4>Sizes & scale</h4>
-
- <ul>
- <li class="no-bullet with-icon tablet">
- <p>Launcher icons on a mobile device must be <strong>48x48 <acronym title="Density-independent pixels. One dp is one pixel on a 160 dpi screen.">dp</acronym></strong>.</p></li>
- <li class="no-bullet with-icon web">
- <p>Launcher icons for display on Google Play must be <strong>512x512 pixels</strong>.</p></li>
- </ul>
-
- </div>
- <div class="col-4">
-
- <h4>Proportions</h4>
-
- <ul>
- <li class="no-bullet with-icon tablet">
- <p>Full asset, <strong>48x48 dp</strong></p>
- </li>
- </ul>
-
- </div>
- <div class="col-4">
-
-<h4>Style</h4>
-<p>Use a distinct silhouette. Three-dimensional, front view, with a slight perspective as if viewed
-from above, so that users perceive some depth.</p>
-
- </div>
-</div>
-
-
-<div class="cols">
- <div class="col-4">
-
- </div>
- <div class="col-7">
-
- <img src="{@docRoot}design/media/iconography_launcher_example.png">
-
- </div>
- <!-- 2 free columns -->
-</div>
-
-<div class="cols">
- <div class="col-12">
-
- <img src="{@docRoot}design/media/iconography_launcher_example2.png">
-
- <div class="vspace size-2"> </div>
-
- </div>
- <!-- 1 free columns -->
-</div>
-
-
-<h2 id="action-bar">Action Bar</h2>
-
-<a class="notice-designers-material"
- href="http://www.google.com/design/spec/style/icons.html#icons-system-icons">
- <div>
- <h3>Material Design</h3>
- <p>System Icons<p>
- </div>
-</a>
-
-<p>
-
-Action bar icons are graphic buttons that represent the most important actions people can take
-within your app. Each one should employ a simple metaphor representing a single concept that most
-people can grasp at a glance.
-
-</p>
-<p>
-
-Pre-defined glyphs should be used for certain common actions such as "refresh" and "share." The
-download link below provides a package with icons that are scaled for various screen densities and
-are suitable for use with the Holo Light and Holo Dark themes. The package also includes unstyled
-icons that you can modify to match your theme, in addition to Adobe® Illustrator® source
-files for further customization.
-
-</p>
-<p>
-<a onClick="ga('send', 'event', 'Design', 'Download', 'Action Bar Icons (@iconography page)');"
- href="{@docRoot}downloads/design/Android_Design_Icons_20131106.zip">Download the Action Bar Icon Pack</a>
-</p>
-
-<div class="cols">
- <div class="col-4">
-
- <img src="{@docRoot}design/media/iconography_actionbar_size.png">
-
- </div>
- <div class="col-4">
-
- <img src="{@docRoot}design/media/iconography_actionbar_focal.png">
-
- </div>
- <div class="col-4">
-
- <img src="{@docRoot}design/media/iconography_actionbar_style.png">
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-4">
-
- <h4>Sizes & scale</h4>
-
- <ul>
- <li class="no-bullet with-icon tablet">
- <p>Action bar icons for phones should be <strong>32x32 <acronym title="Density-independent pixels. One dp is one pixel on a 160 dpi screen.">dp</acronym></strong>.</p></li>
- </ul>
-
- </div>
- <div class="col-4">
-
- <h4>Focal area & proportions</h4>
-
- <ul>
- <li class="no-bullet with-icon tablet">
- <p>Full asset, <strong>32x32 dp</strong></p>
- <p>Optical square, <strong>24x24 dp</strong></p>
- </li>
- </ul>
-
- </div>
- <div class="col-4">
-
-<h4>Style</h4>
-<p>Pictographic, flat, not too detailed, with smooth curves or sharp shapes. If the graphic is thin,
-rotate it 45° left or right to fill the focal space. The thickness of the strokes and negative
-spaces should be a minimum of 2 dp.</p>
-
- </div>
-</div>
-
-
-<div class="cols">
- <div class="col-3">
-
-<h4>Colors</h4>
-<p>Colors: <strong>#333333</strong><br />
-Enabled: <strong>60%</strong> opacity<br />
-Disabled: <strong>30%</strong> opacity</p>
-<div class="vspace size-1"> </div>
-
-<p>Colors: <strong>#FFFFFF</strong><br />
-Enabled: <strong>80%</strong> opacity<br />
-Disabled: <strong>30%</strong> opacity</p>
-
- </div>
- <div class="col-9">
-
- <img src="{@docRoot}design/media/iconography_actionbar_colors.png">
-
- </div>
-</div>
-
-
-<h2 id="small-contextual">Small / Contextual Icons</h2>
-
-<a class="notice-designers-material"
- href="http://www.google.com/design/spec/style/icons.html#icons-system-icons">
- <div>
- <h3>Material Design</h3>
- <p>System Icons<p>
- </div>
-</a>
-
-<p>Within the body of your app, use small icons to surface actions and/or provide status for specific
-items. For example, in the Gmail app, each message has a star icon that marks the message as
-important.</p>
-
-
-<div class="cols">
- <div class="col-4">
-
- <img src="{@docRoot}design/media/iconography_small_size.png">
-
- </div>
- <div class="col-4">
-
- <img src="{@docRoot}design/media/iconography_small_focal.png">
-
- </div>
- <div class="col-4">
-
- <img src="{@docRoot}design/media/iconography_small_style.png">
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-4">
-
- <h4>Sizes & scale</h4>
-
- <ul>
- <li class="no-bullet with-icon tablet">
- <p>Small icons should be <strong>16x16 <acronym title="Density-independent pixels. One dp is one pixel on a 160 dpi screen.">dp</acronym></strong>.</p></li>
- </ul>
-
- </div>
- <div class="col-4">
-
- <h4>Focal area & proportions</h4>
-
- <ul>
- <li class="no-bullet with-icon tablet">
- <p>Full asset, <strong>16x16 dp</strong></p>
- <p>Optical square, <strong>12x12 dp</strong></p>
- </li>
- </ul>
-
- </div>
- <div class="col-4">
-
-<h4>Style</h4>
-<p>Neutral, flat, and simple. Filled shapes are easier to see than thin strokes. Use a single visual
-metaphor so that a user can easily recognize and understand its purpose.</p>
-
- </div>
-</div>
-
-
-<div class="cols">
- <div class="col-4">
-
- <img src="{@docRoot}design/media/iconography_small_colors.png">
-
- <div class="vspace size-2"> </div>
-
-<h4>Colors</h4>
-<p>Use non-neutral colors sparingly and with purpose. For example, Gmail uses yellow in the star icon
-to indicate a bookmarked message. If an icon is actionable, choose a color that contrasts well with
-the background.</p>
-
- </div>
- <div class="col-7">
-
- <img src="{@docRoot}design/media/iconography_small_example.png">
-
- </div>
- <!-- 2 free columns -->
-</div>
-
-
-<h2 id="notification">Notification Icons</h2>
-
-<a class="notice-designers-material"
- href="http://www.google.com/design/spec/style/icons.html#icons-system-icons">
- <div>
- <h3>Material Design</h3>
- <p>System Icons<p>
- </div>
-</a>
-
-
-<p>If your app generates notifications, provide an icon that the system can display in the status bar
-whenever a new notification is available.</p>
-
-
-<div class="cols">
- <div class="col-4">
-
- <img src="{@docRoot}design/media/iconography_notification_size.png">
-
- </div>
- <div class="col-4">
-
- <img src="{@docRoot}design/media/iconography_notification_focal.png">
-
- </div>
- <div class="col-4">
-
- <img src="{@docRoot}design/media/iconography_notification_style.png">
-
- </div>
-</div>
-
-<div class="cols">
- <div class="col-4">
-
- <h4>Sizes & scale</h4>
-
- <ul>
- <li class="no-bullet with-icon tablet">
- <p>Notification icons must be <strong>24x24 <acronym title="Density-independent pixels. One dp is one pixel on a 160 dpi screen.">dp</acronym></strong>.</p></li>
- </ul>
-
- </div>
- <div class="col-4">
-
- <h4>Focal area & proportions</h4>
-
- <ul>
- <li class="no-bullet with-icon tablet">
- <p>Full asset, <strong>24x24 dp</strong></p>
- <p>Optical square, <strong>22x22 dp</strong></p>
- </li>
- </ul>
-
- </div>
- <div class="col-4">
-
-<h4>Style</h4>
-<p>Keep the style flat and simple, using the same single, visual metaphor as your launcher icon.</p>
-
- </div>
-</div>
-
-
-<div class="cols">
- <div class="col-4">
-
-<h4>Colors</h4>
-<p>Notification icons must be entirely white. Also, the system may scale down and/or darken the icons.</p>
-
- </div>
- <div class="col-7">
-
- <img src="{@docRoot}design/media/iconography_notification_example.png">
-
- </div>
- <!-- 2 free columns -->
-</div>
-
-
-
-
-
-
-
-
-
-
-<h2 id="DesignTips">Design Tips</h2>
-
-<p>Here are some tips you might find useful as you create icons or other
-drawable assets for your application. These tips assume you are using
-Adobe® Photoshop® or a similar raster and vector image-editing program.</p>
-
-
-
-
-<h3>Use vector shapes where possible</h3>
-
-<p>Many image-editing programs such as Adobe® Photoshop® allow you to use a
-combination of vector shapes and raster layers and effects. When possible,
-use vector shapes so that if the need arises, assets can be scaled up without
-loss of detail and edge crispness.</p>
-
-<p>Using vectors also makes it easy to align edges and corners to pixel
-boundaries at smaller resolutions.</li>
-
-
-
-<h3>Start with large artboards</h3>
-
-<p>Because you will need to create assets for different screen densities,
-it is best to start your icon
-designs on large artboards with dimensions that are multiples of the target icon
-sizes. For example, launcher icons are 48, 72, 96, or 144 pixels wide,
-depending on screen density (mdpi, hdpi, xhdpi, and xxhdpi, respectively). If you
-initially draw launcher icons on an 864x864 artboard, it will be easier and
-cleaner to adjust the icons when you scale the artboard down to the target
-sizes for final asset creation.</p>
-
-
-
-<h3>When scaling, redraw bitmap layers as needed</h3>
-
-<p>If you scaled an image up from a bitmap layer, rather than from a vector
-layer, those layers will need to be redrawn manually to appear crisp at higher
-densities. For example if a 60x60 circle was painted as a bitmap for
-mdpi it will need to be repainted as a 90x90 circle for hdpi.</p>
-
-
-
-<h3>Use common naming conventions for icon assets</h3>
-
-<p>Try to name files so that related assets will group together inside a
-directory when they are sorted alphabetically. In particular, it helps to use a
-common prefix for each icon type. For example:</p>
-
-<table>
-<tr>
-<th>Asset Type</th>
-<th>Prefix</th>
-<th>Example</th>
-</tr>
-<tr>
-<td>Icons</td>
-<td><code>ic_</code></td>
-<td><code>ic_star.png</code></td>
-</tr>
-<tr>
-<td>Launcher icons</td>
-<td><code>ic_launcher</code></td>
-<td><code>ic_launcher_calendar.png</code></td>
-</tr>
-<tr>
-<td>Menu icons and Action Bar icons</td>
-<td><code>ic_menu</code></td>
-<td><code>ic_menu_archive.png</code></td>
-</tr>
-<tr>
-<td>Status bar icons</td>
-<td><code>ic_stat_notify</code></td>
-<td><code>ic_stat_notify_msg.png</code></td>
-</tr>
-<tr>
-<td>Tab icons</td>
-<td><code>ic_tab</code></td>
-<td><code>ic_tab_recent.png</code></td>
-</tr>
-<tr>
-<td>Dialog icons</td>
-<td><code>ic_dialog</code></td>
-<td><code>ic_dialog_info.png</code></td>
-</tr>
-</table>
-
-<p>Note that you are not required to use a shared prefix of any
-type—doing so is for your convenience only.</p>
-
-
-<h3>Set up a working space that organizes files by density</h3>
-
-<p>Supporting multiple screen densities means you must create multiple versions
-of the same icon. To help keep the multiple copies of files safe and easier to
-find, we recommend creating a directory structure in your working space that
-organizes asset files based on the target density. For example:</p>
-
-<pre>
-art/...
- mdpi/...
- _pre_production/...
- <em>working_file</em>.psd
- <em>finished_asset</em>.png
- hdpi/...
- _pre_production/...
- <em>working_file</em>.psd
- <em>finished_asset</em>.png
- xhdpi/...
- _pre_production/...
- <em>working_file</em>.psd
- <em>finished_asset</em>.png
- xxhdpi/...
- _pre_production/...
- <em>working_file</em>.psd
- <em>finished_asset</em>.png
-</pre>
-
-<p>Because the structure in your working space is similar to that of the application, you
-can quickly determine which assets should be copied to each
-resources directory. Separating assets by density also helps you detect any
-variances in filenames across densities, which is important because
-corresponding assets for different densities must share the same filename.</p>
-
-<p>For comparison, here's the resources directory structure of a typical
-application: </p>
-
-<pre>res/...
- drawable-ldpi/...
- <em>finished_asset</em>.png
- drawable-mdpi/...
- <em>finished_asset</em>.png
- drawable-hdpi/...
- <em>finished_asset</em>.png
- drawable-xhdpi/...
- <em>finished_asset</em>.png
- drawable-xxhdpi/...
- <em>finished_asset</em>.png
-
- mipmap-ldpi/...
- <em>finished_launcher_asset</em>.png
- mipmap-mdpi/...
- <em>finished_launcher_asset</em>.png
- mipmap-hdpi/...
- <em>finished_launcher_asset</em>.png
- mipmap-xhdpi/...
- <em>finished_launcher_asset</em>.png
- mipmap-xxhdpi/...
- <em>finished_launcher_asset</em>.png
- mipmap-xxxhdpi/...
- <em>finished_launcher_asset</em>.png
-</pre>
-
-<p>For more information about how to save resources in the application project,
-see <a href="{@docRoot}guide/topics/resources/providing-resources.html">Providing Resources</a>.
-</p>
-
-<p> For more information about using the mipmap folders, see
-<a href="{@docRoot}tools/projects/index.html#mipmap">Managing Projects Overview</a>.</p>
-
-<h3 id="xxxhdpi-launcher">Provide an xxx-high-density launcher icon</h3>
-
-<p>Some devices scale-up the launcher icon by as much as 25%. For example, if your highest density
-launcher icon image is already extra-extra-high density, the scaling process will make it appear
-less crisp. So you should provide a higher density launcher icon in the <code>mipmap-xxxhdpi
-</code> directory, which the system uses instead of scaling up a smaller version of the icon.</p>
-
-<p class="note"><strong>Note:</strong> The <code>mipmap-xxxhdpi</code> qualifier is necessary
-only to provide a launcher icon that can appear larger than usual on an xxhdpi device. It is best
-practice to place all your launcher icons in the <code>res/mipmap-[density]/</code> folders. This
-enables your app to display launcher icons that have a higher density than the device, without
-scaling up a lower density version of the icon. You do not need to provide xxxhdpi assets for all
-your app's images.</p>
-
-<p>See <a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a> for
-more information.</p>
-
-
-<h3>Remove unnecessary metadata from final assets</h3>
-
-<p>Although the Android SDK tools will automatically compress PNGs when packaging
-application resources into the application binary, a good practice is to remove
-unnecessary headers and metadata from your PNG assets. Tools such as <a
-href="http://optipng.sourceforge.net/">OptiPNG</a> or <a
-href="http://pmt.sourceforge.net/pngcrush/">Pngcrush</a> can ensure that this
-metadata is removed and that your image asset file sizes are optimized.</p>
-
-
diff --git a/docs/html/design/style/metrics-grids.jd b/docs/html/design/style/metrics-grids.jd
deleted file mode 100644
index 97915b8..0000000
--- a/docs/html/design/style/metrics-grids.jd
+++ /dev/null
@@ -1,90 +0,0 @@
-page.title=Metrics and Grids
-page.metaDescription=Optimize your app's UI by designing layouts based on density-independent grids.
-page.tags="layout","screens"
-meta.tags="multiple screens, layout, tablets"
-page.image=/design/media/metrics_closeup.png
-@jd:body
-
-
-<a class="notice-designers-material"
- href="http://www.google.com/design/spec/layout/metrics-keylines.html">
- <div>
- <h3>Material Design</h3>
- <p>Metrics and Keylines<p>
- </div>
-</a>
-
-
-<p>Devices vary not only in physical size, but also in screen density (<acronym title="Dots per
-inch">DPI</acronym>). To simplify the way you design for multiple screens, think of each device as
-falling into a particular size bucket and density bucket:</p>
-<ul>
- <li>The size buckets are <em>handset</em> (smaller than
-600<acronym title="Density-independent pixels: One dp is one pixel on a 160 dpi (mdpi)
-screen.">dp</acronym>) and <em>tablet</em> (larger than or equal 600dp).</li>
- <li>The density buckets are <acronym
-title="Low density (120 dpi)">LDPI</acronym>, <acronym title="Medium density (160
-dpi)">MDPI</acronym>, <acronym title="High density (240 dpi)">HDPI</acronym>, <acronym title
-="Extra-high density (320 dpi)">XHDPI</acronym>, <acronym title
-="Extra-extra!-high density (480 dpi)">XXHDPI</acronym>, and <acronym title
-="Extra-extra-extra!-high density (640 dpi)">XXXHDPI</acronym>.</li>
-</ul>
-
-<p>Optimize your application's UI by designing
-alternative layouts for some of the different size buckets, and provide alternative bitmap images
-for different density buckets.</p>
-
-<p>Because it's important that you design and implement your layouts for multiple densities,
-the guidelines below and throught the documentation
-refer to layout dimensions with <acronym title="Density-independent pixels: One dp is one pixel
-on a 160 dpi (mdpi) screen.">dp</acronym> measurements instead of pixels.</p>
-
-
-<div class="cols">
- <div class="col-8">
-
- <img src="{@docRoot}design/media/metrics_diagram.png">
-
- </div>
- <div class="col-5">
-
-<h4>Space considerations</h4>
-<p>Devices vary in the amount of density-independent pixels (dp) they can display.</p>
-<p>To see more, visit the
-<a href="http://developer.android.com/resources/dashboard/screens.html" target="_blank">
-Screen Sizes and Densities Device Dashboard</a>.</p>
-
- </div>
-</div>
-
-
-<h2 id="48dp-rhythm">48dp Rhythm</h2>
-
-<p>Touchable UI components are generally laid out along 48dp units.</p>
-
-<img src="{@docRoot}design/media/metrics_48.png">
-
-<div class="vspace size-2"> </div>
-
-<h4>Why 48dp?</h4>
-<p>On average, 48dp translate to a physical size of about 9mm (with some variability). This is
-comfortably in the range of recommended target sizes (7-10 mm) for touchscreen objects and users
-will be able to reliably and accurately target them with their fingers.</p>
-<p>If you design your elements to be at least 48dp high and wide you can guarantee that:</p>
-<ul>
-<li>your targets will never be smaller than the minimum recommended target size of 7mm regardless of
- what screen they are displayed on.</li>
-<li>you strike a good compromise between overall information density on the one hand, and
- targetability of UI elements on the other.</li>
-</ul>
-
-<img src="{@docRoot}design/media/metrics_closeup.png">
-
-<div class="vspace size-2"> </div>
-
-<h4>Mind the gaps</h4>
-<p>Spacing between each UI element is 8dp.</p>
-
-<h2 id="examples">Examples</h2>
-
-<img src="{@docRoot}design/media/metrics_forms.png">
diff --git a/docs/html/design/style/themes.jd b/docs/html/design/style/themes.jd
deleted file mode 100644
index 6c8169b..0000000
--- a/docs/html/design/style/themes.jd
+++ /dev/null
@@ -1,53 +0,0 @@
-page.title=Themes
-@jd:body
-
-<div class="cols">
- <div class="col-5">
-
- <img src="{@docRoot}design/media/themes_holo_light.png">
- <div class="figure-caption">
- Gmail in Holo Light.
- </div>
-
- <img src="{@docRoot}design/media/themes_holo_dark.png">
- <div class="figure-caption">
- Settings in Holo Dark.
- </div>
-
- </div>
- <div class="col-7">
-
-
-<a class="notice-designers-material"
- href="http://www.google.com/design/spec/style/color.html#color-themes">
- <div>
- <h3>Material Design</h3>
- <p>Color Themes<p>
- </div>
-</a>
-
-
-<p>Themes are Android's mechanism for applying a consistent style to an app or activity.
-The style specifies the visual properties of the elements that make up your user interface,
-such as color, height, padding and font size. To promote greater cohesion between all apps
-on the platform, Android provides two system themes that you can choose from when building apps:</p>
-<ul>
-<li>Holo Light</li>
-<li>Holo Dark</li>
-</ul>
-<p>Applying these themes will go a long way in helping you to build apps that fit right into the
-general visual language of Android.</p>
-<p>Pick the system theme that best matches the needs and design aesthetics for your app. If your
-desire is to have a more distinct look for your app, using one of the system themes as a starting
-point for your customizations is a good idea. The system themes provide a solid foundation on top
-of which you can selectively implement your own visual stylings.</p>
-
-<div class="note develop">
-<p><strong>Developer Guide</strong></p>
- <p>For information about how to apply themes such as Holo Light and Dark, and
- how to build your own themes, see the
- <a href="{@docRoot}guide/topics/ui/themes.html">Styles and Themes</a> API guide.</p>
-</div>
-
- </div>
-</div>
diff --git a/docs/html/design/style/touch-feedback.jd b/docs/html/design/style/touch-feedback.jd
deleted file mode 100644
index e1fac2f..0000000
--- a/docs/html/design/style/touch-feedback.jd
+++ /dev/null
@@ -1,95 +0,0 @@
-page.title=Touch Feedback
-page.tags=input,button
-@jd:body
-
-<div class="cols" style="margin-bottom: -100px">
-<div class="col-7">
-
-<a class="notice-designers-material"
- href="http://www.google.com/design/spec/animation/responsive-interaction.html">
- <div>
- <h3>Material Design</h3>
- <p>Responsive Interaction<p>
- </div>
-</a>
-
-
-<p>Use illumination and dimming to respond to touches, reinforce the resulting behaviors
-of gestures, and indicate what actions are enabled and disabled.</p>
-
-<p><strong>Be responsive to touches in a gentle way</strong>. Whenever a user touches an
-actionable area in your app, let them know the app is "listening" by providing a visual
-response. Make it subtle —just slightly lighter or darker than the untouched color. This
-provides two benefits:</p>
-
-<ul>
-<li><a href="{@docRoot}design/get-started/principles.html#sprinkle-encouragement">Sprinkles
-of encouragement</a> are more pleasant than jolts.</li>
-<li>Incorporating <a href="{@docRoot}design/style/branding.html">your branding</a> is much
-easier because the default touch feedback works with whatever hue you choose.</li>
-</ul>
-
-</div>
-
-<div class="col-6" style="float:right;">
- <video class="play-on-hover" width="268" height="442" autoplay style="border:1px solid #ddd;background-color:#f9f9f9;" poster="">
- <source src="{@docRoot}design/media/touch_feedback.mp4" type="video/mp4">
- <source src="{@docRoot}design/media/touch_feedback.webm" type="video/webm">
- <source src="{@docRoot}design/media/touch_feedback.ogv" type="video/ogg">
- </video>
- <div class="figure-caption">
- <div style="color:#a3a3a3;margin-left:130px;"><em>Click image to replay...</em></div>
- </div>
-</div>
-
-<h4 style="clear:both;">States</h4>
-
-
-<div class="vspace size-1"> </div>
-
-<img src="{@docRoot}design/media/touch_feedback_states.png">
-<div class="figure-caption">
- Most of Android's UI elements have touch feedback built in, including
- states that indicate whether touching the element will have any effect.
-</div>
-
-<div class="vspace size-3"> </div>
-
-<div class="cols">
- <div class="col-6">
-
- <h4>Communication</h4>
-<p>When your objects react to more complex gestures, help users
-understand what the outcome will be.</p>
-
-<p>In Recents, when a user starts swiping a thumbnail left or right, it
-begins to dim. This helps the user understand that swiping will cause the
-item to be removed.</p>
- </div>
- <div class="col-7">
-
- <img src="{@docRoot}design/media/touch_feedback_manipulation.png">
-
- </div>
-</div>
-<div class="vspace size-3"> </div>
-
-<div class="cols">
- <div class="col-6">
-
- <img src="{@docRoot}design/media/touch_feedback_communication.png">
- <p><em>If a user attempts to scroll past the last home screen panel, the screen
- content tilts to the right to indicate that further navigation in this direction
- isn’t possible.</em></p>
-
- </div>
- <div class="col-6">
-
-<h4>Boundaries</h4>
-<p>
- When users try to scroll past the beginning or end of a scrollable area,
- communicate the boundary with a visual cue. Many of Android's scrollable UI
- widgets, like lists and grid lists, have support for boundary feedback built
- in. If you’re building custom widgets, keep boundary feedback in mind and
- provide it from within your app.
-</p>
diff --git a/docs/html/design/style/typography.jd b/docs/html/design/style/typography.jd
deleted file mode 100644
index af76c40..0000000
--- a/docs/html/design/style/typography.jd
+++ /dev/null
@@ -1,78 +0,0 @@
-page.title=Typography
-page.tags="textview","font"
-page.metaDescription=How to use typography in your Android apps.
-@jd:body
-
-<div class="cols">
- <div class="col-8">
-
- <img src="{@docRoot}design/media/typography_main.png">
-
- </div>
-
-<a class="notice-designers-material"
- style="width: 278px;"
- href="http://www.google.com/design/spec/style/typography.html">
- <div>
- <h3>Material Design</h3>
- <p>Typography<p>
- </div>
-</a>
-
-<div class="col-5">
-
-<p>
- <a class="download-button" onClick="ga('send', 'event', 'Design', 'Download', 'Roboto ZIP');"
- href="{@docRoot}downloads/design/roboto-1.2.zip">Download Roboto</a>
-</p>
-
-<p>The Android design language relies on traditional typographic tools such as scale, space, rhythm,
-and alignment with an underlying grid. Successful deployment of these tools is essential to help
-users quickly understand a screen of information. To support such use of typography, Ice Cream
-Sandwich introduced a new type family named
-<a href="http://www.google.com/fonts/specimen/Roboto" class="external-link">Roboto</a>, created
-specifically for the requirements of UI and high-resolution screens.</p>
-
-<p>The current {@link android.widget.TextView} framework offers Roboto in thin, light, regular and bold
-weights, along with an italic style for each weight. The framework also offers the
-<a href="http://www.google.com/fonts/specimen/Roboto+Condensed" class="external-link">Roboto Condensed</a>
-variant in regular and bold weights, along with an italic style for each weight.</p>
-
- <img src="{@docRoot}design/media/typography_variants@2x.png" width="220">
-
-<p><a onClick="ga('send', 'event', 'Design', 'Download', 'Roboto Specimen Book (@typography page)');"
- href="{@docRoot}downloads/design/Roboto_Specimen_Book_20131031.pdf">Specimen Book</a></p>
-
- </div>
-</div>
-
-<hr>
-
-<div class="cols">
- <div class="col-6">
-
-<h4>Default type colors</h4>
-<p>The Android UI uses the following default color styles: <code>textColorPrimary</code> and
-<code>textColorSecondary</code>. For light themes use <code>textColorPrimaryInverse</code> and
-<code>textColorSecondaryInverse</code>. The framework text color styles also support variants for
-touch feedback states when used inside UI elements.</p>
-
- <img src="{@docRoot}design/media/typography_defaults.png">
-
- </div>
- <div class="col-6">
-
-<h4>Typographic Scale</h4>
-<p>Contrast in type sizes can go a long way to create ordered, understandable layouts. However, too
-many different sizes in the same UI can be messy. The Android framework uses the following limited
-set of type sizes:</p>
-
-<img src="{@docRoot}design/media/typography_sizes.png">
-
-<p>Users can select a system-wide scaling factor for text in the Settings app. In order to support
-these accessibility features, type should be specified in scale-independent pixels
-(<acronym title="Scale-independent pixels. One sp is one pixel on a 160 dpi screen if the user's global text scale is set to 100%.">sp</acronym>)
-wherever possible. Layouts supporting scalable types should be tested against these settings.</p>
-
- </div>
-</div>
diff --git a/docs/html/design/style/writing.jd b/docs/html/design/style/writing.jd
deleted file mode 100644
index 0c62a55..0000000
--- a/docs/html/design/style/writing.jd
+++ /dev/null
@@ -1,322 +0,0 @@
-page.title=Writing Style
-page.tags=dialog,toast,notification
-@jd:body
-
-<h2 id="voa">Android's Voice</h2>
-
-<p>When writing text that appears in your app, keep it concise, simple, and friendly.</p>
-
-<h4 id="concise">Concise</h4>
-
-<ul>
- <li>Describe only what the user needs to know.</li>
- <li>Eliminate redundancy, such as titles that restate the body of an information box.</li>
- <li>Keep text as short as possible.</li>
-</ul>
-
-<p><em>Avoid wordy, stilted text</em></p>
-
-<div class="cols">
- <div class="col-6 layout-with-list-item-margins">
-
- <div class="do-dont-label bad">Don't</div>
-
- <table class="ui-table good"><tbody><tr><td>
- Consult the documentation that came with your phone for further instructions.
- </td></tr></tbody></table>
-
- </div>
- <div class="col-6">
-
- <div class="do-dont-label good">Do</div>
-
- <table class="ui-table good"><tbody><tr><td>
- Read the instructions that came with your phone.
- </td></tr></tbody></table>
-
- </div>
-</div>
-
-<p><em>Don't provide unnecessary information</em></p>
-
-<div class="cols">
- <div class="col-6 layout-with-list-item-margins">
-
- <div class="do-dont-label bad">From a Setup Wizard screen</div>
-
- <table class="ui-table bad">
- <thead>
- <tr>
- <th>
- Signing in...
- </th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>
- Your phone needs to communicate with<br>
- Google servers to sign in to your account.<br>
- This may take up to five minutes.
- </td>
- </tr>
- </tbody>
- </table>
-
- </div>
- <div class="col-6">
-
- <div class="do-dont-label good">From a Setup Wizard screen</div>
-
- <table class="ui-table good">
- <thead>
- <tr>
- <th>
- Signing in...
- </th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>
- Your phone is contacting Google.<br>
- This can take up to 5 minutes.
- </td>
- </tr>
- </tbody>
- </table>
-
- </div>
-</div>
-
-<h4 id="simple">Simple</h4>
-
-<ul>
- <li>Use short words, active verbs, and common nouns.</li>
- <li>Put the most important thing first. “Front-load” the first 11 characters
- with the most salient information in the string.</li>
- <li>Don’t try to explain subtle differences. They are lost on most users.</li>
-</ul>
-
-<p><em>Focus on the user's concern, not technical details</em></p>
-
-<div class="cols">
- <div class="col-6 layout-with-list-item-margins">
-
- <div class="do-dont-label bad">Don't</div>
-
- <table class="ui-table good"><tbody><tr><td>
- Manually control GPS to prevent other apps from using it
- </td></tr></tbody></table>
-
- </div>
- <div class="col-6">
-
- <div class="do-dont-label good">Do</div>
-
- <table class="ui-table good"><tbody><tr><td>
- To save power, switch Location mode to Battery saving
- </td></tr></tbody></table>
-
- </div>
-</div>
-
-<p><em>Put top news first</em></p>
-
-<div class="cols">
- <div class="col-6 layout-with-list-item-margins">
-
- <div class="do-dont-label bad">Don't</div>
-
- <table class="ui-table good"><tbody><tr><td>
- 77 other people +1’d this, including Larry Page
- </td></tr></tbody></table>
-
- </div>
- <div class="col-6">
-
- <div class="do-dont-label good">Do</div>
-
- <table class="ui-table good"><tbody><tr><td>
- Larry Page and 76 others +1’d this
- </td></tr></tbody></table>
-
- </div>
-</div>
-
-<p><em>Put the user's goal first</em></p>
-
-<div class="cols">
- <div class="col-6 layout-with-list-item-margins">
-
- <div class="do-dont-label bad">Don't</div>
-
- <table class="ui-table good"><tbody><tr><td>
- Touch Next to complete setup using a Wi-Fi connection
- </td></tr></tbody></table>
-
- </div>
- <div class="col-6">
-
- <div class="do-dont-label good">Do</div>
-
- <table class="ui-table good"><tbody><tr><td>
- To finish setup using Wi-Fi, touch Next
- </td></tr></tbody></table>
-
- </div>
-</div>
-
-
-<h4 id="friendly">Friendly</h4>
-
-<ul>
- <li>Use contractions.</li>
- <li>Talk directly to the reader. Use “you” to refer to the reader.</li>
- <li>Keep your tone casual and conversational, but avoid slang.</li>
-</li>
-</ul>
-
-<p><em>Avoid being confusing or annoying</em></p>
-<div class="cols">
- <div class="col-6 layout-with-list-item-margins">
- <div class="do-dont-label bad">Don't</div>
- <table class="ui-table bad">
- <thead>
- <tr>
- <th>
- Sorry!
- </th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>
- Activity MyAppActivity (in application<br />
- MyApp) is not responding
- </td>
- </tr>
- </tbody>
- </table>
-
- </div>
- <div class="col-6">
- <div class="do-dont-label good">Do</div>
- <table class="ui-table good">
- <thead>
- <tr>
- <th>
- MyApp isn’t responding
- </th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>
- Do you want to close it?
- </td>
- </tr>
- </tbody>
- </table>
- </div>
-</div>
-
-
-<h4>Words to avoid</h4>
-
-<div style="padding:5px 2.1em;">
-<table>
- <tr>
- <td class="do-dont-label bad" style="width:40%">Don't use</td>
- <td class="do-dont-label good" style="width:40%">Use</td>
- </tr>
- <tr>
- <td>one, two, three, four, ...</td>
- <td>1, 2, 3, 4, ...</td>
- </tr>
- <tr>
- <td>application</td>
- <td>app</td>
- </tr>
- <tr>
- <td>cannot, could not, do not, did not
-will not, you will</td>
- <td><em>Contractions:</em> can’t, couldn’t, don’t, didn’t won’t, you’ll, and so on</td>
- </tr>
- <tr>
- <td>okay, ok</td>
- <td>OK</td>
- </tr>
- <tr>
- <td>please, sorry, thank you</td>
- <td><em>Attempts at politeness can annoy the user, especially in messages that say
- something has gone wrong.<br />
- Exception: In Japanese, “please” is mandatory and imperative verbs should
- be localized accordingly (turn on -> please turn on).
- </em></td>
- </tr>
- <tr>
- <td>there is, there are, it is<br />
- <em>and other “disappeared” subjects (grammatical expletives)</em></td>
- <td><em>Use a noun as the subject</em></td>
- </tr>
- <tr>
- <td>abort, kill, terminate</td>
- <td>stop, cancel, end, exit</td>
- </tr>
- <tr>
- <td>fail, failed, <em>negative language</em></td>
- <td><em>In general, use positive phrasing<br />
- (for example, “do” rather than “don’t,” except in cases such as “Don’t show
- again,” “Can’t connect,” and so on.)</em></td>
- </tr>
- <tr>
- <td>me, I, my, mine</td>
- <td>you, your, yours</td>
- </tr>
- <tr>
- <td>Are you sure? Warning!</td>
- <td><em>Tell user the consequence instead, for example, “You’ll lose all photos
- and media”</em></td>
- </tr>
-</table>
-
-</div>
-
-<h2 id="formatting_text">Formatting text</h2>
-
-<h4 id="capitalization">Capitalization</h4>
-
-<ul>
- <li>Use sentence-style capitalization for all UI strings: “Words to live by.”</li>
- <li>Capitalize all important words in:
- <ul>
- <li>App names (Calendar, Google Drive)</li>
- <li>Named features (Android Beam, Face Unlock)</li>
- <li>Proper nouns (Statue of Liberty, San Francisco Giants)</li>
- </ul>
- </li>
- <li>Be conservative. Don't capitalize words that aren't part of a formal feature name:
- <ul>
- <li>Sim card lock, Home screen, not Sim Card Lock, Home Screen.</li>
- </ul>
- </li>
-</ul>
-
-
-<h4 id="punctuation">Punctuation</h4>
-<ul>
- <li><strong>Period.</strong> Don't use a period after a single sentence or
- phrase used in isolation, such as in a toast, label, or notification. Wherever two or
- more sentences run together, use a period for each sentence. </li>
- <li><strong>Ellipsis.</strong> Use the ellipsis character (…) (Option-; on MacOS and &hellip;
- in HTML) to indicate
- <ul>
- <li>Incompleteness, such as an action in progress (“Downloading...”) or truncated text.</li>
- <li>That a menu item (such as Print… or Share…) leads to further UI involving significant
- choices. Exception: Commands whose wording already implies further (but limited) UI, such
- as <strong>Find in page</strong> or <strong>Pick a date</strong>, do not require an
- ellipsis. </li>
- </ul>
- </li>
-</ul>
diff --git a/docs/html/design/videos/index.jd b/docs/html/design/videos/index.jd
deleted file mode 100644
index 3845b44..0000000
--- a/docs/html/design/videos/index.jd
+++ /dev/null
@@ -1,130 +0,0 @@
-page.title=Videos
-@jd:body
-
-<p>The Android Design Team presents design-oriented sessions at Google I/O every year. Visit these pages to view the videos and presentations from the conferences.</p>
-
-<img src="{@docRoot}images/home/io-logo-2013-alt.png">
-
-<div class="vspace size-2"> </div>
-
-<div class="cols">
- <div class="col-7">
- <h3 id="design-for-success"><a href="https://developers.google.com/events/io/2013/sessions/326368573">Enchant, Simplify, Amaze: Android's Design Principles</a></h3>
- <p>Want to enchant people, simplify their lives, and make them feel amazing with your app? Learn how Android's Design Principles can help you create products that resonate with people. Find out about the meaning and research behind the principles. See real-world examples and practices from the Android Design team. Discover techniques for applying the principles in your daily work. No design experience necessary.</p>
- </div>
- <div class="col-6">
- <iframe width="355" height="200" src="//www.youtube.com/embed/s0HIP8EdlnE" frameborder="0" allowfullscreen=""></iframe>
- </div>
-</div>
-
-<div class="vspace size-2"> </div>
-
-<div class="cols">
- <div class="col-7">
- <h3 id="design-for-success"><a href="https://developers.google.com/events/io/2013/sessions/326301704">Structure in Android App Design</a></h3>
- <p>Life is simple when your app is simple. But when your apps gets more complex, how do you choose between spinners, tabs, and drawers for navigation? Members of the Android Design team look at techniques for making your app predictable and pleasing to use.</p>
- </div>
- <div class="col-6">
- <iframe width="355" height="200" src="//www.youtube.com/embed/XpqyiBR0lJ4" frameborder="0" allowfullscreen=""></iframe>
- </div>
-</div>
-
-<div class="vspace size-2"> </div>
-
-<div class="cols">
- <div class="col-7">
- <h3 id="design-for-success"><a href="https://developers.google.com/events/io/2013/sessions/326425499">Fireside Chat with the Android Team</a></h3>
- <p>Pull up a chair and join the Android platform team for a fireside chat. It's your opportunity to ask us about the platform and learn a little bit more about why things work the way they do, from the people who built it. </p>
- </div>
- <div class="col-6">
- <iframe width="355" height="200" src="//www.youtube.com/embed/A5OOJDIrYls" frameborder="0" allowfullscreen=""></iframe>
- </div>
-</div>
-
-<div class="vspace size-2"> </div>
-
-<div class="cols">
- <div class="col-7">
- <h3 id="design-for-success"><a href="https://developers.google.com/events/io/2013/sessions/326483138">Agile UX Research Practice in Android</a></h3>
- <p>In the Android UX team, it is critical to get user feedback frequently and consistently so that we are able to iterate and develop the best-in-class designs for our users. We will discuss how the team applied "Pulse Studies" (iterative research sessions) in order to put new ideas, designs, and concepts in front of users on a regular basis; it requires minimal advance planning, it can have an immediate product impact, and it can meet urgent needs. </p>
- </div>
- <div class="col-6">
- <iframe width="355" height="200" src="//www.youtube.com/embed/6MOeVNbh9cY" frameborder="0" allowfullscreen=""></iframe>
- </div>
-</div>
-
-<div class="vspace size-2"> </div>
-
-<div class="cols">
- <div class="col-7">
- <h3 id="design-for-success"><a href="https://developers.google.com/events/io/2013/sessions/326460111">Cognitive Science and Design</a></h3>
- <p>This session will provide an in-depth look at human perception and cognition, and its implications for interactive and visual design. The human brain is purely treated as an information processing machine, and we will teach the audience its attributes, its advantages, its limitations, and generally how to hack it. </p>
- </div>
- <div class="col-6">
- <iframe width="355" height="200" src="//www.youtube.com/embed/z2exxj4COhU" frameborder="0" allowfullscreen=""></iframe>
- </div>
-</div>
-
-<img src="{@docRoot}design/media/extras_googleio_12.png">
-
-<div class="vspace size-2"> </div>
-
-<div class="cols">
- <div class="col-7">
- <h3 id="design-for-success"><a href="https://developers.google.com/events/io/sessions/gooio2012/112/">Android Design for Success</a></h3>
- <p>You have a great idea for an Android app. You want it to stand out among hundreds of thousands. You want your users to love it and tell everyone they know. The Android User Experience team is here to help. We talk about the Android Design guide and other tricks of the trade for creating apps that delight users and help them accomplish their goals. No design background is required.</p>
- </div>
- <div class="col-6">
- <iframe width="355" height="200" src="//www.youtube.com/embed/2NL_83EG0no" frameborder="0" allowfullscreen=""></iframe>
- </div>
-</div>
-
-<div class="vspace size-2"> </div>
-
-<div class="cols">
- <div class="col-7">
- <h3 id="design-for-engineers"><a href="https://developers.google.com/events/io/sessions/gooio2012/1204/">Android Design for Engineers</a></h3>
- <p>Design isn't black magic, it's a field that people can learn. In this talk two elite designers from Google give you an advanced crash course in interactive and visual design. Topics include mental models, natural mappings, metaphors, mode errors, visual hierarchies, typography and gestalt principles. Correctly applied, this knowledge can drastically improve the quality of your work.</p>
- </div>
- <div class="col-6">
- <iframe width="355" height="200" src="//www.youtube.com/embed/iJDoxOTyMdk" frameborder="0" allowfullscreen=""></iframe>
- </div>
-</div>
-
-<div class="vspace size-2"> </div>
-
-<div class="cols">
- <div class="col-7">
- <h3 id="navigation-in-android"><a href="https://developers.google.com/events/io/sessions/gooio2012/114/">Navigation in Android</a></h3>
- <p>An app is useless if people can't find their way around it. Android introduced big navigation-support changes in 3.0 and 4.0. The Action Bar offers a convenient control for Up navigation, the Back key's behavior became more consistent within tasks, and the Recent Tasks UI got an overhaul. In this talk, we discuss how and why we got where we are today, how to think about navigation when designing your app's user experience, and how to write apps that offer effortless navigation in multiple Android versions.</p>
- </div>
- <div class="col-6">
- <iframe width="355" height="200" src="//www.youtube.com/embed/XwGHJJYBs0Q" frameborder="0" allowfullscreen=""></iframe>
- </div>
-</div>
-
-<div class="vspace size-2"> </div>
-
-<div class="cols">
- <div class="col-7">
- <h3 id="now-what"><a href="https://developers.google.com/events/io/sessions/gooio2012/115/">So You've Read the Design Guide; Now What?</a></h3>
- <p>The Android Design Guide describes how to design beautiful Android apps, but not how to build them. In this talk we give practical tips for how to apply fit & finish as you implement your design, we show you how to avoid some common pitfalls, we describe some useful patterns, and show how tools can help.</p>
- </div>
- <div class="col-6">
- <iframe width="355" height="200" src="//www.youtube.com/embed/2jCVmfCse1E" frameborder="0" allowfullscreen=""></iframe>
- </div>
-</div>
-
-<div class="vspace size-2"> </div>
-
-<div class="cols">
- <div class="col-7">
- <h3 id="playing-with-patterns"><a href="https://developers.google.com/events/io/sessions/gooio2012/131/">Playing with Patterns</a></h3>
- <p>Best-in-class application designers and developers talk about their experience in developing for Android, showing screenshots from their app, exploring the challenges they faced, and offering creative solutions congruent with the Android Design guide. Guests are invited to show examples of visual and interaction patterns in their application that manage to keep it simultaneously consistent and personal.</p>
- </div>
- <div class="col-6">
- <iframe width="355" height="200" src="//www.youtube.com/embed/8iUbr8RZKtg" frameborder="0" allowfullscreen=""></iframe>
- </div>
-</div>
-
-<p>Videos for the entire Design Track can also be found on the <a href="http://www.youtube.com/playlist?list=PL54FA004D676C3EE9">Android Developers Channel</a> on YouTube.</p>
diff --git a/docs/html/guide/topics/renderscript/reference.jd b/docs/html/guide/topics/renderscript/reference.jd
deleted file mode 100644
index a9d780a..0000000
--- a/docs/html/guide/topics/renderscript/reference.jd
+++ /dev/null
@@ -1,21 +0,0 @@
-page.title=Runtime API Reference
-parent.title=Computation
-parent.link=index.html
-
-@jd:body
-
-<script language="JavaScript">
-
-function autoResize(element){
- var newheight;
- var newwidth;
-
- newheight = element.contentWindow.document.body.scrollHeight + 20;
- newwidth = element.contentWindow.document.body.scrollWidth;
- element.height = (newheight) + "px";
- element.width = (newwidth) + "px";
-}
-</script>
-
-
-<iframe SRC="{@docRoot}reference/renderscript/index.html" width="100%" id="iframe" marginheight="0" frameborder="0" onLoad="autoResize(this);"></iframe>
diff --git a/docs/html/jd_collections.js b/docs/html/jd_collections.js
index b28f978..8538671 100644
--- a/docs/html/jd_collections.js
+++ b/docs/html/jd_collections.js
@@ -1221,6 +1221,14 @@
"https://support.google.com/googleplay/answer/2651410"
]
},
+ "preview/landing/resources": {
+ "title": "",
+ "resources": [
+ "preview/api-overview.html",
+ "preview/setup-sdk.html",
+ "preview/samples.html"
+ ]
+ },
"autolanding": {
"title": "",
"resources": [
diff --git a/docs/html/jd_tag_helpers.js b/docs/html/jd_tag_helpers.js
index 7538e4d..f03b1d7 100644
--- a/docs/html/jd_tag_helpers.js
+++ b/docs/html/jd_tag_helpers.js
@@ -13,6 +13,7 @@
GOOGLE_RESOURCES,
GUIDE_RESOURCES,
SAMPLES_RESOURCES,
+ PREVIEW_RESOURCES,
TOOLS_RESOURCES,
TRAINING_RESOURCES,
YOUTUBE_RESOURCES,
@@ -70,6 +71,7 @@
'google': GOOGLE_RESOURCES,
'guide': GUIDE_RESOURCES,
'samples': SAMPLES_RESOURCES,
+ 'preview': PREVIEW_RESOURCES,
'tools': TOOLS_RESOURCES,
'training': TRAINING_RESOURCES,
'youtube': YOUTUBE_RESOURCES,
@@ -86,6 +88,7 @@
{map:GOOGLE_BY_TAG,arr:GOOGLE_RESOURCES},
{map:GUIDE_BY_TAG,arr:GUIDE_RESOURCES},
{map:SAMPLES_BY_TAG,arr:SAMPLES_RESOURCES},
+ {map:PREVIEW_BY_TAG,arr:PREVIEW_RESOURCES},
{map:TOOLS_BY_TAG,arr:TOOLS_RESOURCES},
{map:TRAINING_BY_TAG,arr:TRAINING_RESOURCES},
{map:YOUTUBE_BY_TAG,arr:YOUTUBE_RESOURCES},
diff --git a/docs/html/preview/api-overview.jd b/docs/html/preview/api-overview.jd
index f72ffbb..dde3c7be 100644
--- a/docs/html/preview/api-overview.jd
+++ b/docs/html/preview/api-overview.jd
@@ -1,5 +1,5 @@
page.title=API Overview
-excludeFromSuggestions=true
+page.keywords=preview,sdk,compatibility
sdk.platform.apiLevel=22
@jd:body
diff --git a/docs/html/preview/index.jd b/docs/html/preview/index.jd
new file mode 100644
index 0000000..a2d0b18
--- /dev/null
+++ b/docs/html/preview/index.jd
@@ -0,0 +1,60 @@
+page.title=M Developer Preview
+page.tags=preview
+meta.tags="preview"
+fullpage=true
+page.viewport_width=970
+section.landing=true
+header.hide=1
+footer.hide=1
+@jd:body
+
+<section class="dac-expand dac-hero dac-light">
+ <div class="wrap">
+ <div class="cols dac-hero-content">
+ <div class="col-1of2 col-push-1of2 dac-hero-figure">
+ <img class="dac-hero-image" src="/design/media/hero-material-design.png">
+ </div>
+ <div class="col-1of2 col-pull-1of2">
+ <h1 class="dac-hero-title">M Developer Preview</h1>
+ <p class="dac-hero-description">
+ Get ready for the next official release of the platform. Test your apps
+ and give us feedback!
+ </p>
+ <a class="dac-hero-cta" href="{@docRoot}preview/setup-sdk.html">
+ <span class="dac-sprite dac-auto-chevron"></span>
+ Set up the Preview SDK
+ </a><br>
+ <a class="dac-hero-cta" href="{@docRoot}preview/api-overview.html">
+ <span class="dac-sprite dac-auto-chevron"></span>
+ Review the API changes
+ </a><br>
+ <a class="dac-hero-cta" href="https://code.google.com/p/android-developer-preview/">
+ <span class="dac-sprite dac-auto-chevron"></span>
+ Report issues
+ </a><br>
+ </div>
+ </div>
+ </div>
+</section>
+
+<section class="dac-section dac-gray dac-small dac-invert"><div class="wrap">
+ <h2 class="norule">Latest</h2>
+ <div class="resource-widget resource-flow-layout col-16"
+ data-query="collection:develop/landing/latest"
+ data-cardSizes="6x6"
+ data-maxResults="3"></div>
+</div></section>
+
+
+<section class="dac-section"><div class="wrap">
+ <h1 class="dac-section-title">Resources</h1>
+ <div class="dac-section-subtitle">
+ Check out these resources to help you get started with the M Developer Preview.
+ </div>
+ <div class="resource-widget resource-flow-layout col-16"
+ data-query="collection:preview/landing/resources"
+ data-cardSizes="6x6"
+ data-maxResults="6"></div>
+</div></section>
+
+
diff --git a/docs/html/preview/overview.jd b/docs/html/preview/overview.jd
new file mode 100644
index 0000000..00f1cfe
--- /dev/null
+++ b/docs/html/preview/overview.jd
@@ -0,0 +1,7 @@
+page.title=Preview Program Overview
+
+@jd:body
+
+<p>
+ This is an overview of the program. Bacon.
+</p>
\ No newline at end of file
diff --git a/docs/html/preview/preview_toc.cs b/docs/html/preview/preview_toc.cs
index bea4914..fbf73f6 100644
--- a/docs/html/preview/preview_toc.cs
+++ b/docs/html/preview/preview_toc.cs
@@ -1,6 +1,11 @@
<ul id="nav">
<li class="nav-section">
+ <div class="nav-section-header empty"><a href="<?cs var:toroot ?>preview/overview.html">
+ Program Overview</a></div>
+ </li>
+
+ <li class="nav-section">
<div class="nav-section-header empty"><a href="<?cs var:toroot ?>preview/setup-sdk.html">
Set up the SDK</a></div>
</li>
@@ -29,9 +34,4 @@
License Agreement</a></div>
</li>
- <li class="nav-section" style="margin: 20px 0 0 -10px;">
- <div class="nav-section-header empty"><a href="<?cs var:toroot ?>index.html" class="back-link">
- Developer Home</a></div>
- </li>
-
</ul>
diff --git a/docs/html/preview/reference.jd b/docs/html/preview/reference.jd
index ee1f24d..2d30c62 100644
--- a/docs/html/preview/reference.jd
+++ b/docs/html/preview/reference.jd
@@ -9,7 +9,7 @@
<ul>
<li>
- <a href="http://storage.googleapis.com/androiddevelopers/preview/l-developer-preview-reference.zip">
+ <a href="http://storage.googleapis.com/androiddevelopers/preview/m-developer-preview-reference.zip">
M Developer Preview reference</a>
</li>
</ul>
\ No newline at end of file
diff --git a/docs/html/training/articles/keystore.jd b/docs/html/training/articles/keystore.jd
index 217db81..fea3b2c 100644
--- a/docs/html/training/articles/keystore.jd
+++ b/docs/html/training/articles/keystore.jd
@@ -129,7 +129,7 @@
for use as soon as the user unlocks the secure lock screen or confirms their secure lock screen
credentials using the {@link android.app.KeyguardManager#createConfirmDeviceCredentialIntent(CharSequence, CharSequence) KeyguardManager.createConfirmDeviceCredentialIntent}
flow. Each key specifies for how long the authorization remains valid for that key. Such keys
- can only be generated or imported if the secure lock screen is enabled (see {@link android.app.KeyguardManager#isKeyguardSecure Keyguard.isKeyguardSecure}).
+ can only be generated or imported if the secure lock screen is enabled (see {@link android.app.KeyguardManager#isDeviceSecure()}).
These keys become permanently invalidated once the secure lock screen is disabled or forcibly
reset (e.g. by a Device Admin).</li>
<li>User authentication is required for every use of the key. In this mode, a specific operation
diff --git a/graphics/java/android/graphics/Atlas.java b/graphics/java/android/graphics/Atlas.java
index 39a5a53..e0a5345 100644
--- a/graphics/java/android/graphics/Atlas.java
+++ b/graphics/java/android/graphics/Atlas.java
@@ -21,10 +21,12 @@
*/
public class Atlas {
/**
- * This flag indicates whether the packing algorithm will attempt
- * to rotate entries to make them fit better in the atlas.
+ * WARNING: These flag values are part of the on-disk configuration information,
+ * do not change their values.
*/
- public static final int FLAG_ALLOW_ROTATIONS = 0x1;
+
+ /** DELETED: FLAG_ROTATION = 0x01 */
+
/**
* This flag indicates whether the packing algorithm should leave
* an empty 1 pixel wide border around each bitmap. This border can
@@ -52,9 +54,7 @@
/**
* Represents a bitmap packed in the atlas. Each entry has a location in
- * pixels in the atlas and a rotation flag. If the entry was rotated, the
- * bitmap must be rotated by 90 degrees (in either direction as long as
- * the origin remains the same) before being rendered into the atlas.
+ * pixels in the atlas and a rotation flag.
*/
public static class Entry {
/**
@@ -65,11 +65,6 @@
* Location, in pixels, of the bitmap on the Y axis in the atlas.
*/
public int y;
-
- /**
- * If true, the bitmap must be rotated 90 degrees in the atlas.
- */
- public boolean rotated;
}
private final Policy mPolicy;
@@ -239,7 +234,6 @@
private final SplitDecision mSplitDecision;
- private final boolean mAllowRotation;
private final int mPadding;
/**
@@ -263,7 +257,6 @@
}
SlicePolicy(int width, int height, int flags, SplitDecision splitDecision) {
- mAllowRotation = (flags & FLAG_ALLOW_ROTATIONS) != 0;
mPadding = (flags & FLAG_ADD_PADDING) != 0 ? 1 : 0;
// The entire atlas is empty at first, minus padding
@@ -360,26 +353,9 @@
*
* @return True if the rectangle was packed in the atlas, false otherwise
*/
- @SuppressWarnings("SuspiciousNameCombination")
private boolean insert(Cell cell, Cell prev, int width, int height, Entry entry) {
- boolean rotated = false;
-
- // If the rectangle doesn't fit we'll try to rotate it
- // if possible before giving up
if (cell.width < width || cell.height < height) {
- if (mAllowRotation) {
- if (cell.width < height || cell.height < width) {
- return false;
- }
-
- // Rotate the rectangle
- int temp = width;
- width = height;
- height = temp;
- rotated = true;
- } else {
- return false;
- }
+ return false;
}
// Remaining free space after packing the rectangle
@@ -433,7 +409,6 @@
// Return the location and rotation of the packed rectangle
entry.x = cell.x;
entry.y = cell.y;
- entry.rotated = rotated;
return true;
}
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 587e7fa..b15caeb 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -1631,6 +1631,7 @@
status_t applyStyle(uint32_t resID, bool force=false);
status_t setTo(const Theme& other);
+ status_t clear();
/**
* Retrieve a value in the theme. If the theme defines this
diff --git a/keystore/java/android/security/AndroidKeyPairGenerator.java b/keystore/java/android/security/AndroidKeyPairGenerator.java
index 3b25ba6..3f29c6a 100644
--- a/keystore/java/android/security/AndroidKeyPairGenerator.java
+++ b/keystore/java/android/security/AndroidKeyPairGenerator.java
@@ -54,13 +54,13 @@
public static class RSA extends AndroidKeyPairGenerator {
public RSA() {
- super("RSA");
+ super(KeyStoreKeyProperties.Algorithm.RSA);
}
}
public static class EC extends AndroidKeyPairGenerator {
public EC() {
- super("EC");
+ super(KeyStoreKeyProperties.Algorithm.EC);
}
}
@@ -83,15 +83,15 @@
private android.security.KeyStore mKeyStore;
private KeyPairGeneratorSpec mSpec;
- private String mKeyAlgorithm;
+ private @KeyStoreKeyProperties.AlgorithmEnum String mKeyAlgorithm;
private int mKeyType;
private int mKeySize;
- protected AndroidKeyPairGenerator(String algorithm) {
+ protected AndroidKeyPairGenerator(@KeyStoreKeyProperties.AlgorithmEnum String algorithm) {
mAlgorithm = algorithm;
}
- public String getAlgorithm() {
+ public @KeyStoreKeyProperties.AlgorithmEnum String getAlgorithm() {
return mAlgorithm;
}
@@ -197,7 +197,7 @@
return certGen.generate(privateKey);
}
- private String getKeyAlgorithm(KeyPairGeneratorSpec spec) {
+ private @KeyStoreKeyProperties.AlgorithmEnum String getKeyAlgorithm(KeyPairGeneratorSpec spec) {
String result = spec.getKeyType();
if (result != null) {
return result;
@@ -248,10 +248,11 @@
}
}
- private static String getDefaultSignatureAlgorithmForKeyAlgorithm(String algorithm) {
- if ("RSA".equalsIgnoreCase(algorithm)) {
+ private static String getDefaultSignatureAlgorithmForKeyAlgorithm(
+ @KeyStoreKeyProperties.AlgorithmEnum String algorithm) {
+ if (KeyStoreKeyProperties.Algorithm.RSA.equalsIgnoreCase(algorithm)) {
return "sha256WithRSA";
- } else if ("EC".equalsIgnoreCase(algorithm)) {
+ } else if (KeyStoreKeyProperties.Algorithm.EC.equalsIgnoreCase(algorithm)) {
return "sha256WithECDSA";
} else {
throw new IllegalArgumentException("Unsupported key type " + algorithm);
@@ -287,7 +288,7 @@
}
KeyPairGeneratorSpec spec = (KeyPairGeneratorSpec) params;
- String keyAlgorithm = getKeyAlgorithm(spec);
+ @KeyStoreKeyProperties.AlgorithmEnum String keyAlgorithm = getKeyAlgorithm(spec);
int keyType = KeyStore.getKeyTypeForAlgorithm(keyAlgorithm);
if (keyType == -1) {
throw new InvalidAlgorithmParameterException(
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java
index 72cb062..e82ff6a 100644
--- a/keystore/java/android/security/AndroidKeyStore.java
+++ b/keystore/java/android/security/AndroidKeyStore.java
@@ -128,10 +128,11 @@
keymasterDigest = keymasterDigests.get(0);
}
- String keyAlgorithmString;
+ @KeyStoreKeyProperties.AlgorithmEnum String keyAlgorithmString;
try {
- keyAlgorithmString = KeymasterUtils.getJcaSecretKeyAlgorithm(
- keymasterAlgorithm, keymasterDigest);
+ keyAlgorithmString =
+ KeyStoreKeyProperties.Algorithm.fromKeymasterSecretKeyAlgorithm(
+ keymasterAlgorithm, keymasterDigest);
} catch (IllegalArgumentException e) {
throw (UnrecoverableKeyException)
new UnrecoverableKeyException("Unsupported secret key type").initCause(e);
@@ -451,10 +452,10 @@
int keymasterAlgorithm;
int keymasterDigest;
try {
- keymasterAlgorithm = KeymasterUtils.getKeymasterAlgorithmFromJcaSecretKeyAlgorithm(
+ keymasterAlgorithm = KeyStoreKeyProperties.Algorithm.toKeymasterSecretKeyAlgorithm(
keyAlgorithmString);
keymasterDigest =
- KeymasterUtils.getKeymasterDigestfromJcaSecretKeyAlgorithm(keyAlgorithmString);
+ KeyStoreKeyProperties.Algorithm.toKeymasterDigest(keyAlgorithmString);
} catch (IllegalArgumentException e) {
throw new KeyStoreException("Unsupported secret key algorithm: " + keyAlgorithmString);
}
@@ -465,8 +466,7 @@
int[] keymasterDigests;
if (params.isDigestsSpecified()) {
// Digest(s) specified in parameters
- keymasterDigests =
- KeymasterUtils.getKeymasterDigestsFromJcaDigestAlgorithms(params.getDigests());
+ keymasterDigests = KeyStoreKeyProperties.Digest.allToKeymaster(params.getDigests());
if (keymasterDigest != -1) {
// Digest also specified in the JCA key algorithm name.
if (!com.android.internal.util.ArrayUtils.contains(
@@ -494,8 +494,8 @@
}
@KeyStoreKeyProperties.PurposeEnum int purposes = params.getPurposes();
- int[] keymasterBlockModes = KeymasterUtils.getKeymasterBlockModesFromJcaBlockModes(
- params.getBlockModes());
+ int[] keymasterBlockModes =
+ KeyStoreKeyProperties.BlockMode.allToKeymaster(params.getBlockModes());
if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0)
&& (params.isRandomizedEncryptionRequired())) {
for (int keymasterBlockMode : keymasterBlockModes) {
@@ -503,8 +503,7 @@
throw new KeyStoreException(
"Randomized encryption (IND-CPA) required but may be violated by block"
+ " mode: "
- + KeymasterUtils.getJcaBlockModeFromKeymasterBlockMode(
- keymasterBlockMode)
+ + KeyStoreKeyProperties.BlockMode.fromKeymaster(keymasterBlockMode)
+ ". See KeyStoreParameter documentation.");
}
}
@@ -513,11 +512,11 @@
args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
}
args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
- int[] keymasterPaddings = ArrayUtils.concat(
- KeymasterUtils.getKeymasterPaddingsFromJcaEncryptionPaddings(
- params.getEncryptionPaddings()),
- KeymasterUtils.getKeymasterPaddingsFromJcaSignaturePaddings(
- params.getSignaturePaddings()));
+ if (params.getSignaturePaddings().length > 0) {
+ throw new KeyStoreException("Signature paddings not supported for symmetric keys");
+ }
+ int[] keymasterPaddings = KeyStoreKeyProperties.EncryptionPadding.allToKeymaster(
+ params.getEncryptionPaddings());
args.addInts(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings);
KeymasterUtils.addUserAuthArgs(args,
params.getContext(),
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index e9c24dd..8e27dc3 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -262,7 +262,8 @@
* unavailable.
*/
public static void choosePrivateKeyAlias(Activity activity, KeyChainAliasCallback response,
- String[] keyTypes, Principal[] issuers, String host, int port, String alias) {
+ @KeyStoreKeyProperties.AlgorithmEnum String[] keyTypes, Principal[] issuers,
+ String host, int port, String alias) {
choosePrivateKeyAlias(activity, response, keyTypes, issuers, host, port, null, alias);
}
@@ -306,9 +307,8 @@
* unavailable.
*/
public static void choosePrivateKeyAlias(Activity activity, KeyChainAliasCallback response,
- String[] keyTypes, Principal[] issuers,
- String host, int port, String url,
- String alias) {
+ @KeyStoreKeyProperties.AlgorithmEnum String[] keyTypes, Principal[] issuers,
+ String host, int port, String url, String alias) {
/*
* TODO currently keyTypes, issuers are unused. They are meant
* to follow the semantics and purpose of X509KeyManager
@@ -431,9 +431,11 @@
* specific {@code PrivateKey} type indicated by {@code algorithm} (e.g.,
* "RSA").
*/
- public static boolean isKeyAlgorithmSupported(String algorithm) {
+ public static boolean isKeyAlgorithmSupported(
+ @KeyStoreKeyProperties.AlgorithmEnum String algorithm) {
final String algUpper = algorithm.toUpperCase(Locale.US);
- return "EC".equals(algUpper) || "RSA".equals(algUpper);
+ return KeyStoreKeyProperties.Algorithm.EC.equals(algUpper)
+ || KeyStoreKeyProperties.Algorithm.RSA.equals(algUpper);
}
/**
@@ -443,7 +445,8 @@
* hardware support that can be used to bind keys to the device in a way
* that makes it non-exportable.
*/
- public static boolean isBoundKeyAlgorithm(String algorithm) {
+ public static boolean isBoundKeyAlgorithm(
+ @KeyStoreKeyProperties.AlgorithmEnum String algorithm) {
if (!isKeyAlgorithmSupported(algorithm)) {
return false;
}
diff --git a/keystore/java/android/security/KeyGeneratorSpec.java b/keystore/java/android/security/KeyGeneratorSpec.java
index 8f135a6..729646d 100644
--- a/keystore/java/android/security/KeyGeneratorSpec.java
+++ b/keystore/java/android/security/KeyGeneratorSpec.java
@@ -48,8 +48,8 @@
private final Date mKeyValidityForOriginationEnd;
private final Date mKeyValidityForConsumptionEnd;
private final @KeyStoreKeyProperties.PurposeEnum int mPurposes;
- private final String[] mEncryptionPaddings;
- private final String[] mBlockModes;
+ private final @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
+ private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
private final boolean mRandomizedEncryptionRequired;
private final boolean mUserAuthenticationRequired;
private final int mUserAuthenticationValidityDurationSeconds;
@@ -63,8 +63,8 @@
Date keyValidityForOriginationEnd,
Date keyValidityForConsumptionEnd,
@KeyStoreKeyProperties.PurposeEnum int purposes,
- String[] encryptionPaddings,
- String[] blockModes,
+ @KeyStoreKeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
+ @KeyStoreKeyProperties.BlockModeEnum String[] blockModes,
boolean randomizedEncryptionRequired,
boolean userAuthenticationRequired,
int userAuthenticationValidityDurationSeconds) {
@@ -160,14 +160,14 @@
/**
* Gets the set of padding schemes with which the key can be used when encrypting/decrypting.
*/
- public String[] getEncryptionPaddings() {
+ public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
}
/**
* Gets the set of block modes with which the key can be used.
*/
- public String[] getBlockModes() {
+ public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() {
return ArrayUtils.cloneIfNotEmpty(mBlockModes);
}
@@ -195,10 +195,12 @@
/**
* Gets the duration of time (seconds) for which this key can be used after the user is
- * successfully authenticated.
+ * successfully authenticated. This has effect only if user authentication is required.
*
- * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication
- * is required for every use of the key.
+ * @return duration in seconds or {@code -1} if authentication is required for every use of the
+ * key.
+ *
+ * @see #isUserAuthenticationRequired()
*/
public int getUserAuthenticationValidityDurationSeconds() {
return mUserAuthenticationValidityDurationSeconds;
@@ -220,8 +222,8 @@
private Date mKeyValidityForOriginationEnd;
private Date mKeyValidityForConsumptionEnd;
private @KeyStoreKeyProperties.PurposeEnum int mPurposes;
- private String[] mEncryptionPaddings;
- private String[] mBlockModes;
+ private @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
+ private @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
private boolean mRandomizedEncryptionRequired = true;
private boolean mUserAuthenticationRequired;
private int mUserAuthenticationValidityDurationSeconds = -1;
@@ -346,7 +348,8 @@
*
* <p>This must be specified for keys which are used for encryption/decryption.
*/
- public Builder setEncryptionPaddings(String... paddings) {
+ public Builder setEncryptionPaddings(
+ @KeyStoreKeyProperties.EncryptionPaddingEnum String... paddings) {
mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings);
return this;
}
@@ -357,7 +360,7 @@
*
* <p>This must be specified for encryption/decryption keys.
*/
- public Builder setBlockModes(String... blockModes) {
+ public Builder setBlockModes(@KeyStoreKeyProperties.BlockModeEnum String... blockModes) {
mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes);
return this;
}
@@ -425,7 +428,7 @@
*
* <p>By default, the user needs to authenticate for every use of the key.
*
- * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
+ * @param seconds duration in seconds or {@code -1} if the user needs to authenticate for
* every use of the key.
*
* @see #setUserAuthenticationRequired(boolean)
diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java
index d6d3789..25c61fd 100644
--- a/keystore/java/android/security/KeyPairGeneratorSpec.java
+++ b/keystore/java/android/security/KeyPairGeneratorSpec.java
@@ -85,13 +85,13 @@
private final @KeyStoreKeyProperties.PurposeEnum int mPurposes;
- private final String[] mDigests;
+ private final @KeyStoreKeyProperties.DigestEnum String[] mDigests;
- private final String[] mEncryptionPaddings;
+ private final @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
- private final String[] mSignaturePaddings;
+ private final @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
- private final String[] mBlockModes;
+ private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
private final boolean mRandomizedEncryptionRequired;
@@ -138,10 +138,10 @@
Date keyValidityForOriginationEnd,
Date keyValidityForConsumptionEnd,
@KeyStoreKeyProperties.PurposeEnum int purposes,
- String[] digests,
- String[] encryptionPaddings,
- String[] signaturePaddings,
- String[] blockModes,
+ @KeyStoreKeyProperties.DigestEnum String[] digests,
+ @KeyStoreKeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
+ @KeyStoreKeyProperties.SignaturePaddingEnum String[] signaturePaddings,
+ @KeyStoreKeyProperties.BlockModeEnum String[] blockModes,
boolean randomizedEncryptionRequired,
boolean userAuthenticationRequired,
int userAuthenticationValidityDurationSeconds) {
@@ -246,7 +246,7 @@
/**
* Returns the key type (e.g., "EC", "RSA") specified by this parameter.
*/
- public String getKeyType() {
+ public @KeyStoreKeyProperties.AlgorithmEnum String getKeyType() {
return mKeyType;
}
@@ -352,28 +352,28 @@
/**
* Gets the set of digest algorithms with which the key can be used.
*/
- public String[] getDigests() {
+ public @KeyStoreKeyProperties.DigestEnum String[] getDigests() {
return ArrayUtils.cloneIfNotEmpty(mDigests);
}
/**
* Gets the set of padding schemes with which the key can be used when encrypting/decrypting.
*/
- public String[] getEncryptionPaddings() {
+ public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
}
/**
* Gets the set of padding schemes with which the key can be used when signing/verifying.
*/
- public String[] getSignaturePaddings() {
+ public @KeyStoreKeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() {
return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings);
}
/**
* Gets the set of block modes with which the key can be used.
*/
- public String[] getBlockModes() {
+ public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() {
return ArrayUtils.cloneIfNotEmpty(mBlockModes);
}
@@ -403,14 +403,14 @@
}
/**
- * Gets the duration of time (seconds) for which the private key can be used after the user
- * is successfully authenticated.
+ * Gets the duration of time (seconds) for which this key can be used after the user is
+ * successfully authenticated. This has effect only if user authentication is required.
*
* <p>This restriction applies only to private key operations. Public key operations are not
* restricted.
*
- * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication
- * is required for every use of the key.
+ * @return duration in seconds or {@code -1} if authentication is required for every use of the
+ * key.
*
* @see #isUserAuthenticationRequired()
*/
@@ -468,13 +468,13 @@
private @KeyStoreKeyProperties.PurposeEnum int mPurposes;
- private String[] mDigests;
+ private @KeyStoreKeyProperties.DigestEnum String[] mDigests;
- private String[] mEncryptionPaddings;
+ private @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
- private String[] mSignaturePaddings;
+ private @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
- private String[] mBlockModes;
+ private @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
private boolean mRandomizedEncryptionRequired = true;
@@ -511,7 +511,8 @@
/**
* Sets the key type (e.g., EC, RSA) of the keypair to be created.
*/
- public Builder setKeyType(String keyType) throws NoSuchAlgorithmException {
+ public Builder setKeyType(@KeyStoreKeyProperties.AlgorithmEnum String keyType)
+ throws NoSuchAlgorithmException {
if (keyType == null) {
throw new NullPointerException("keyType == null");
} else {
@@ -628,6 +629,8 @@
*
* <p>By default, the key is valid at any instant.
*
+ * <p><b>NOTE: This has currently no effect.
+ *
* @see #setKeyValidityEnd(Date)
*/
public Builder setKeyValidityStart(Date startDate) {
@@ -640,6 +643,8 @@
*
* <p>By default, the key is valid at any instant.
*
+ * <p><b>NOTE: This has currently no effect.
+ *
* @see #setKeyValidityStart(Date)
* @see #setKeyValidityForConsumptionEnd(Date)
* @see #setKeyValidityForOriginationEnd(Date)
@@ -655,6 +660,8 @@
*
* <p>By default, the key is valid at any instant.
*
+ * <p><b>NOTE: This has currently no effect.
+ *
* @see #setKeyValidityForConsumptionEnd(Date)
*/
public Builder setKeyValidityForOriginationEnd(Date endDate) {
@@ -668,6 +675,8 @@
*
* <p>By default, the key is valid at any instant.
*
+ * <p><b>NOTE: This has currently no effect.
+ *
* @see #setKeyValidityForOriginationEnd(Date)
*/
public Builder setKeyValidityForConsumptionEnd(Date endDate) {
@@ -679,6 +688,8 @@
* Sets the set of purposes for which the key can be used.
*
* <p>This must be specified for all keys. There is no default.
+ *
+ * <p><b>NOTE: This has currently no effect.
*/
public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) {
mPurposes = purposes;
@@ -690,8 +701,10 @@
* to use the key with any other digest will be rejected.
*
* <p>This must be specified for keys which are used for signing/verification.
+ *
+ * <p><b>NOTE: This has currently no effect.
*/
- public Builder setDigests(String... digests) {
+ public Builder setDigests(@KeyStoreKeyProperties.DigestEnum String... digests) {
mDigests = ArrayUtils.cloneIfNotEmpty(digests);
return this;
}
@@ -702,8 +715,11 @@
* rejected.
*
* <p>This must be specified for keys which are used for encryption/decryption.
+ *
+ * <p><b>NOTE: This has currently no effect.
*/
- public Builder setEncryptionPaddings(String... paddings) {
+ public Builder setEncryptionPaddings(
+ @KeyStoreKeyProperties.EncryptionPaddingEnum String... paddings) {
mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings);
return this;
}
@@ -714,8 +730,11 @@
* rejected.
*
* <p>This must be specified for RSA keys which are used for signing/verification.
+ *
+ * <p><b>NOTE: This has currently no effect.
*/
- public Builder setSignaturePaddings(String... paddings) {
+ public Builder setSignaturePaddings(
+ @KeyStoreKeyProperties.SignaturePaddingEnum String... paddings) {
mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(paddings);
return this;
}
@@ -725,8 +744,10 @@
* Attempts to use the key with any other block modes will be rejected.
*
* <p>This must be specified for encryption/decryption keys.
+ *
+ * <p><b>NOTE: This has currently no effect.
*/
- public Builder setBlockModes(String... blockModes) {
+ public Builder setBlockModes(@KeyStoreKeyProperties.BlockModeEnum String... blockModes) {
mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes);
return this;
}
@@ -750,6 +771,8 @@
* <li>If you are using RSA encryption without padding, consider switching to padding
* schemes which offer {@code IND-CPA}, such as PKCS#1 or OAEP.</li>
* </ul>
+ *
+ * <p><b>NOTE: This has currently no effect.
*/
public Builder setRandomizedEncryptionRequired(boolean required) {
mRandomizedEncryptionRequired = required;
@@ -772,6 +795,8 @@
* <p>This restriction applies only to private key operations. Public key operations are not
* restricted.
*
+ * <p><b>NOTE: This has currently no effect.
+ *
* @see #setUserAuthenticationValidityDurationSeconds(int)
*/
public Builder setUserAuthenticationRequired(boolean required) {
@@ -788,7 +813,9 @@
* <p>This restriction applies only to private key operations. Public key operations are not
* restricted.
*
- * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
+ * <p><b>NOTE: This has currently no effect.
+ *
+ * @param seconds duration in seconds or {@code -1} if the user needs to authenticate for
* every use of the key.
*
* @see #setUserAuthenticationRequired(boolean)
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 82d328b..304d277 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -115,10 +115,10 @@
return mToken;
}
- static int getKeyTypeForAlgorithm(String keyType) {
- if ("RSA".equalsIgnoreCase(keyType)) {
+ static int getKeyTypeForAlgorithm(@KeyStoreKeyProperties.AlgorithmEnum String keyType) {
+ if (KeyStoreKeyProperties.Algorithm.RSA.equalsIgnoreCase(keyType)) {
return NativeConstants.EVP_PKEY_RSA;
- } else if ("EC".equalsIgnoreCase(keyType)) {
+ } else if (KeyStoreKeyProperties.Algorithm.EC.equalsIgnoreCase(keyType)) {
return NativeConstants.EVP_PKEY_EC;
} else {
return -1;
diff --git a/keystore/java/android/security/KeyStoreCipherSpi.java b/keystore/java/android/security/KeyStoreCipherSpi.java
index 094aa75..bd601bc 100644
--- a/keystore/java/android/security/KeyStoreCipherSpi.java
+++ b/keystore/java/android/security/KeyStoreCipherSpi.java
@@ -27,6 +27,7 @@
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
+import java.security.ProviderException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
@@ -315,15 +316,15 @@
} else if (e instanceof InvalidAlgorithmParameterException) {
throw (InvalidAlgorithmParameterException) e;
} else {
- throw new RuntimeException("Unexpected exception type", e);
+ throw new ProviderException("Unexpected exception type", e);
}
}
if (mOperationToken == null) {
- throw new IllegalStateException("Keystore returned null operation token");
+ throw new ProviderException("Keystore returned null operation token");
}
if (mOperationHandle == 0) {
- throw new IllegalStateException("Keystore returned invalid operation handle");
+ throw new ProviderException("Keystore returned invalid operation handle");
}
loadAlgorithmSpecificParametersFromBeginResult(keymasterOutputArgs);
@@ -494,13 +495,14 @@
}
if ((mIv != null) && (mIv.length > 0)) {
try {
- AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
+ AlgorithmParameters params =
+ AlgorithmParameters.getInstance(KeyStoreKeyProperties.Algorithm.AES);
params.init(new IvParameterSpec(mIv));
return params;
} catch (NoSuchAlgorithmException e) {
- throw new RuntimeException("Failed to obtain AES AlgorithmParameters", e);
+ throw new ProviderException("Failed to obtain AES AlgorithmParameters", e);
} catch (InvalidParameterSpecException e) {
- throw new RuntimeException(
+ throw new ProviderException(
"Failed to initialize AES AlgorithmParameters with an IV", e);
}
}
@@ -633,10 +635,9 @@
if ((mIv == null) && (mEncrypting)) {
// IV was not provided by the caller and thus will be generated by keymaster.
// Mix in some additional entropy from the provided SecureRandom.
- if (mRng != null) {
- mAdditionalEntropyForBegin = new byte[mBlockSizeBytes];
- mRng.nextBytes(mAdditionalEntropyForBegin);
- }
+ mAdditionalEntropyForBegin =
+ KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
+ mRng, mBlockSizeBytes);
}
}
}
@@ -668,11 +669,11 @@
if (mIv == null) {
mIv = returnedIv;
} else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) {
- throw new IllegalStateException("IV in use differs from provided IV");
+ throw new ProviderException("IV in use differs from provided IV");
}
} else {
if (returnedIv != null) {
- throw new IllegalStateException(
+ throw new ProviderException(
"IV in use despite IV not being used by this transformation");
}
}
diff --git a/keystore/java/android/security/KeyStoreConnectException.java b/keystore/java/android/security/KeyStoreConnectException.java
index 1aa3aec..885f1f7 100644
--- a/keystore/java/android/security/KeyStoreConnectException.java
+++ b/keystore/java/android/security/KeyStoreConnectException.java
@@ -16,12 +16,14 @@
package android.security;
+import java.security.ProviderException;
+
/**
* Indicates a communications error with keystore service.
*
* @hide
*/
-public class KeyStoreConnectException extends IllegalStateException {
+public class KeyStoreConnectException extends ProviderException {
public KeyStoreConnectException() {
super("Failed to communicate with keystore service");
}
diff --git a/keystore/java/android/security/KeyStoreCryptoOperationUtils.java b/keystore/java/android/security/KeyStoreCryptoOperationUtils.java
index e5933ad..311278b 100644
--- a/keystore/java/android/security/KeyStoreCryptoOperationUtils.java
+++ b/keystore/java/android/security/KeyStoreCryptoOperationUtils.java
@@ -21,6 +21,7 @@
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
+import java.security.SecureRandom;
/**
* Assorted utility methods for implementing crypto operations on top of KeyStore.
@@ -28,6 +29,9 @@
* @hide
*/
abstract class KeyStoreCryptoOperationUtils {
+
+ private static volatile SecureRandom sRng;
+
private KeyStoreCryptoOperationUtils() {}
/**
@@ -81,4 +85,28 @@
// General cases
return getInvalidKeyExceptionForInit(keyStore, key, beginOpResultCode);
}
+
+ /**
+ * Returns the requested number of random bytes to mix into keystore/keymaster RNG.
+ *
+ * @param rng RNG from which to obtain the random bytes or {@code null} for the platform-default
+ * RNG.
+ */
+ static byte[] getRandomBytesToMixIntoKeystoreRng(SecureRandom rng, int sizeBytes) {
+ if (rng == null) {
+ rng = getRng();
+ }
+ byte[] result = new byte[sizeBytes];
+ rng.nextBytes(result);
+ return result;
+ }
+
+ private static SecureRandom getRng() {
+ // IMPLEMENTATION NOTE: It's OK to share a SecureRandom instance because SecureRandom is
+ // required to be thread-safe.
+ if (sRng == null) {
+ sRng = new SecureRandom();
+ }
+ return sRng;
+ }
}
diff --git a/keystore/java/android/security/KeyStoreHmacSpi.java b/keystore/java/android/security/KeyStoreHmacSpi.java
index 0dbe788..5089a25 100644
--- a/keystore/java/android/security/KeyStoreHmacSpi.java
+++ b/keystore/java/android/security/KeyStoreHmacSpi.java
@@ -24,6 +24,7 @@
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
+import java.security.ProviderException;
import java.security.spec.AlgorithmParameterSpec;
import javax.crypto.MacSpi;
@@ -185,10 +186,10 @@
}
if (mOperationToken == null) {
- throw new IllegalStateException("Keystore returned null operation token");
+ throw new ProviderException("Keystore returned null operation token");
}
if (mOperationHandle == 0) {
- throw new IllegalStateException("Keystore returned invalid operation handle");
+ throw new ProviderException("Keystore returned invalid operation handle");
}
mChunkedStreamer = new KeyStoreCryptoOperationChunkedStreamer(
@@ -206,17 +207,17 @@
try {
ensureKeystoreOperationInitialized();
} catch (InvalidKeyException e) {
- throw new IllegalStateException("Failed to reinitialize MAC", e);
+ throw new ProviderException("Failed to reinitialize MAC", e);
}
byte[] output;
try {
output = mChunkedStreamer.update(input, offset, len);
} catch (KeyStoreException e) {
- throw new IllegalStateException("Keystore operation failed", e);
+ throw new ProviderException("Keystore operation failed", e);
}
if ((output != null) && (output.length != 0)) {
- throw new IllegalStateException("Update operation unexpectedly produced output");
+ throw new ProviderException("Update operation unexpectedly produced output");
}
}
@@ -225,14 +226,14 @@
try {
ensureKeystoreOperationInitialized();
} catch (InvalidKeyException e) {
- throw new IllegalStateException("Failed to reinitialize MAC", e);
+ throw new ProviderException("Failed to reinitialize MAC", e);
}
byte[] result;
try {
result = mChunkedStreamer.doFinal(null, 0, 0);
} catch (KeyStoreException e) {
- throw new IllegalStateException("Keystore operation failed", e);
+ throw new ProviderException("Keystore operation failed", e);
}
resetWhilePreservingInitState();
diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
index 68b5751..4b914c2 100644
--- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
@@ -21,6 +21,7 @@
import android.security.keymaster.KeymasterDefs;
import java.security.InvalidAlgorithmParameterException;
+import java.security.ProviderException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Date;
@@ -39,6 +40,17 @@
public AES() {
super(KeymasterDefs.KM_ALGORITHM_AES, 128);
}
+
+ @Override
+ protected void engineInit(AlgorithmParameterSpec params, SecureRandom random)
+ throws InvalidAlgorithmParameterException {
+ super.engineInit(params, random);
+ if ((mKeySizeBits != 128) && (mKeySizeBits != 192) && (mKeySizeBits != 256)) {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported key size: " + mKeySizeBits
+ + ". Supported: 128, 192, 256.");
+ }
+ }
}
protected static abstract class HmacBase extends KeyStoreKeyGeneratorSpi {
@@ -87,6 +99,11 @@
private KeyGeneratorSpec mSpec;
private SecureRandom mRng;
+ protected int mKeySizeBits;
+ private int[] mKeymasterPurposes;
+ private int[] mKeymasterBlockModes;
+ private int[] mKeymasterPaddings;
+
protected KeyStoreKeyGeneratorSpi(
int keymasterAlgorithm,
int defaultKeySizeBits) {
@@ -100,6 +117,97 @@
mKeymasterAlgorithm = keymasterAlgorithm;
mKeymasterDigest = keymasterDigest;
mDefaultKeySizeBits = defaultKeySizeBits;
+ if (mDefaultKeySizeBits <= 0) {
+ throw new IllegalArgumentException("Default key size must be positive");
+ }
+
+ if ((mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) && (mKeymasterDigest == -1)) {
+ throw new IllegalArgumentException(
+ "Digest algorithm must be specified for HMAC key");
+ }
+ }
+
+ @Override
+ protected void engineInit(SecureRandom random) {
+ throw new UnsupportedOperationException("Cannot initialize without an "
+ + KeyGeneratorSpec.class.getName() + " parameter");
+ }
+
+ @Override
+ protected void engineInit(int keySize, SecureRandom random) {
+ throw new UnsupportedOperationException("Cannot initialize without a "
+ + KeyGeneratorSpec.class.getName() + " parameter");
+ }
+
+ @Override
+ protected void engineInit(AlgorithmParameterSpec params, SecureRandom random)
+ throws InvalidAlgorithmParameterException {
+ resetAll();
+
+ boolean success = false;
+ try {
+ if ((params == null) || (!(params instanceof KeyGeneratorSpec))) {
+ throw new InvalidAlgorithmParameterException("Cannot initialize without an "
+ + KeyGeneratorSpec.class.getName() + " parameter");
+ }
+ KeyGeneratorSpec spec = (KeyGeneratorSpec) params;
+ if (spec.getKeystoreAlias() == null) {
+ throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
+ }
+
+ mRng = random;
+ mSpec = spec;
+
+ mKeySizeBits = (spec.getKeySize() != -1) ? spec.getKeySize() : mDefaultKeySizeBits;
+ if (mKeySizeBits <= 0) {
+ throw new InvalidAlgorithmParameterException(
+ "Key size must be positive: " + mKeySizeBits);
+ } else if ((mKeySizeBits % 8) != 0) {
+ throw new InvalidAlgorithmParameterException(
+ "Key size in must be a multiple of 8: " + mKeySizeBits);
+ }
+
+ try {
+ mKeymasterPurposes =
+ KeyStoreKeyProperties.Purpose.allToKeymaster(spec.getPurposes());
+ mKeymasterPaddings = KeyStoreKeyProperties.EncryptionPadding.allToKeymaster(
+ spec.getEncryptionPaddings());
+ mKeymasterBlockModes =
+ KeyStoreKeyProperties.BlockMode.allToKeymaster(spec.getBlockModes());
+ if (((spec.getPurposes() & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0)
+ && (spec.isRandomizedEncryptionRequired())) {
+ for (int keymasterBlockMode : mKeymasterBlockModes) {
+ if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatible(
+ keymasterBlockMode)) {
+ throw new InvalidAlgorithmParameterException(
+ "Randomized encryption (IND-CPA) required but may be violated"
+ + " by block mode: "
+ + KeyStoreKeyProperties.BlockMode.fromKeymaster(
+ keymasterBlockMode)
+ + ". See " + KeyGeneratorSpec.class.getName()
+ + " documentation.");
+ }
+ }
+ }
+ } catch (IllegalArgumentException e) {
+ throw new InvalidAlgorithmParameterException(e);
+ }
+
+ success = true;
+ } finally {
+ if (!success) {
+ resetAll();
+ }
+ }
+ }
+
+ private void resetAll() {
+ mSpec = null;
+ mRng = null;
+ mKeySizeBits = -1;
+ mKeymasterPurposes = null;
+ mKeymasterPaddings = null;
+ mKeymasterBlockModes = null;
}
@Override
@@ -117,43 +225,14 @@
}
KeymasterArguments args = new KeymasterArguments();
+ args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, mKeySizeBits);
args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, mKeymasterAlgorithm);
if (mKeymasterDigest != -1) {
args.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
}
- if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
- if (mKeymasterDigest == -1) {
- throw new IllegalStateException("Digest algorithm must be specified for HMAC key");
- }
- }
- int keySizeBits = (spec.getKeySize() != -1) ? spec.getKeySize() : mDefaultKeySizeBits;
- args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keySizeBits);
- @KeyStoreKeyProperties.PurposeEnum int purposes = spec.getPurposes();
- int[] keymasterBlockModes = KeymasterUtils.getKeymasterBlockModesFromJcaBlockModes(
- spec.getBlockModes());
- if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0)
- && (spec.isRandomizedEncryptionRequired())) {
- for (int keymasterBlockMode : keymasterBlockModes) {
- if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatible(keymasterBlockMode)) {
- throw new IllegalStateException(
- "Randomized encryption (IND-CPA) required but may be violated by block"
- + " mode: "
- + KeymasterUtils.getJcaBlockModeFromKeymasterBlockMode(
- keymasterBlockMode)
- + ". See KeyGeneratorSpec documentation.");
- }
- }
- }
-
- for (int keymasterPurpose :
- KeyStoreKeyProperties.Purpose.allToKeymaster(purposes)) {
- args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
- }
- args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
- args.addInts(
- KeymasterDefs.KM_TAG_PADDING,
- KeymasterUtils.getKeymasterPaddingsFromJcaEncryptionPaddings(
- spec.getEncryptionPaddings()));
+ args.addInts(KeymasterDefs.KM_TAG_PURPOSE, mKeymasterPurposes);
+ args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes);
+ args.addInts(KeymasterDefs.KM_TAG_PADDING, mKeymasterPaddings);
KeymasterUtils.addUserAuthArgs(args,
spec.getContext(),
spec.isUserAuthenticationRequired(),
@@ -168,57 +247,31 @@
(spec.getKeyValidityForConsumptionEnd() != null)
? spec.getKeyValidityForConsumptionEnd() : new Date(Long.MAX_VALUE));
- if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0)
+ if (((spec.getPurposes() & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0)
&& (!spec.isRandomizedEncryptionRequired())) {
// Permit caller-provided IV when encrypting with this key
args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
}
- byte[] additionalEntropy = null;
- SecureRandom rng = mRng;
- if (rng != null) {
- additionalEntropy = new byte[(keySizeBits + 7) / 8];
- rng.nextBytes(additionalEntropy);
- }
-
+ byte[] additionalEntropy =
+ KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
+ mRng, (mKeySizeBits + 7) / 8);
int flags = spec.getFlags();
String keyAliasInKeystore = Credentials.USER_SECRET_KEY + spec.getKeystoreAlias();
+ KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
int errorCode = mKeyStore.generateKey(
- keyAliasInKeystore, args, additionalEntropy, flags, new KeyCharacteristics());
+ keyAliasInKeystore, args, additionalEntropy, flags, resultingKeyCharacteristics);
if (errorCode != KeyStore.NO_ERROR) {
- throw new IllegalStateException(
+ throw new ProviderException(
"Keystore operation failed", KeyStore.getKeyStoreException(errorCode));
}
- String keyAlgorithmJCA =
- KeymasterUtils.getJcaSecretKeyAlgorithm(mKeymasterAlgorithm, mKeymasterDigest);
+ String keyAlgorithmJCA;
+ try {
+ keyAlgorithmJCA = KeyStoreKeyProperties.Algorithm.fromKeymasterSecretKeyAlgorithm(
+ mKeymasterAlgorithm, mKeymasterDigest);
+ } catch (IllegalArgumentException e) {
+ throw new ProviderException("Failed to obtain JCA secret key algorithm name", e);
+ }
return new KeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmJCA);
}
-
- @Override
- protected void engineInit(SecureRandom random) {
- throw new UnsupportedOperationException("Cannot initialize without an "
- + KeyGeneratorSpec.class.getName() + " parameter");
- }
-
- @Override
- protected void engineInit(AlgorithmParameterSpec params, SecureRandom random)
- throws InvalidAlgorithmParameterException {
- if ((params == null) || (!(params instanceof KeyGeneratorSpec))) {
- throw new InvalidAlgorithmParameterException("Cannot initialize without an "
- + KeyGeneratorSpec.class.getName() + " parameter");
- }
- KeyGeneratorSpec spec = (KeyGeneratorSpec) params;
- if (spec.getKeystoreAlias() == null) {
- throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
- }
-
- mSpec = spec;
- mRng = random;
- }
-
- @Override
- protected void engineInit(int keySize, SecureRandom random) {
- throw new UnsupportedOperationException("Cannot initialize without a "
- + KeyGeneratorSpec.class.getName() + " parameter");
- }
}
diff --git a/keystore/java/android/security/KeyStoreKeyProperties.java b/keystore/java/android/security/KeyStoreKeyProperties.java
index b85ec53..1cf6a7a 100644
--- a/keystore/java/android/security/KeyStoreKeyProperties.java
+++ b/keystore/java/android/security/KeyStoreKeyProperties.java
@@ -17,13 +17,23 @@
package android.security;
import android.annotation.IntDef;
+import android.annotation.StringDef;
import android.security.keymaster.KeymasterDefs;
import libcore.util.EmptyArray;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
import java.util.Collection;
+import java.util.Locale;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
+import javax.crypto.SecretKeyFactory;
/**
* Properties of {@code AndroidKeyStore} keys.
@@ -37,7 +47,7 @@
public @interface PurposeEnum {}
/**
- * Purpose of key.
+ * Purposes of key.
*/
public static abstract class Purpose {
private Purpose() {}
@@ -122,6 +132,514 @@
}
@Retention(RetentionPolicy.SOURCE)
+ @StringDef({
+ Algorithm.RSA,
+ Algorithm.EC,
+ Algorithm.AES,
+ Algorithm.HMAC_SHA1,
+ Algorithm.HMAC_SHA224,
+ Algorithm.HMAC_SHA256,
+ Algorithm.HMAC_SHA384,
+ Algorithm.HMAC_SHA512,
+ })
+ public @interface AlgorithmEnum {}
+
+ /**
+ * Key algorithms.
+ *
+ * <p>These are standard names which can be used to obtain instances of {@link KeyGenerator},
+ * {@link KeyPairGenerator}, {@link Cipher} (as part of the transformation string), {@link Mac},
+ * {@link KeyFactory}, {@link SecretKeyFactory}. These are also the names used by
+ * {@link Key#getAlgorithm()}.
+ */
+ public static abstract class Algorithm {
+ private Algorithm() {}
+
+ /** Rivest Shamir Adleman (RSA) key. */
+ public static final String RSA = "RSA";
+
+ /** Elliptic Curve (EC) key. */
+ public static final String EC = "EC";
+
+ /** Advanced Encryption Standard (AES) key. */
+ public static final String AES = "AES";
+
+ /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-1 as the hash. */
+ public static final String HMAC_SHA1 = "HmacSHA1";
+
+ /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-224 as the hash. */
+ public static final String HMAC_SHA224 = "HmacSHA224";
+
+ /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-256 as the hash. */
+ public static final String HMAC_SHA256 = "HmacSHA256";
+
+ /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-384 as the hash. */
+ public static final String HMAC_SHA384 = "HmacSHA384";
+
+ /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-512 as the hash. */
+ public static final String HMAC_SHA512 = "HmacSHA512";
+
+ /**
+ * @hide
+ */
+ static int toKeymasterSecretKeyAlgorithm(@AlgorithmEnum String algorithm) {
+ if (AES.equalsIgnoreCase(algorithm)) {
+ return KeymasterDefs.KM_ALGORITHM_AES;
+ } else if (algorithm.toUpperCase(Locale.US).startsWith("HMAC")) {
+ return KeymasterDefs.KM_ALGORITHM_HMAC;
+ } else {
+ throw new IllegalArgumentException(
+ "Unsupported secret key algorithm: " + algorithm);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static @AlgorithmEnum String fromKeymasterSecretKeyAlgorithm(
+ int keymasterAlgorithm, int keymasterDigest) {
+ switch (keymasterAlgorithm) {
+ case KeymasterDefs.KM_ALGORITHM_AES:
+ if (keymasterDigest != -1) {
+ throw new IllegalArgumentException("Digest not supported for AES key: "
+ + Digest.fromKeymaster(keymasterDigest));
+ }
+ return AES;
+ case KeymasterDefs.KM_ALGORITHM_HMAC:
+ switch (keymasterDigest) {
+ case KeymasterDefs.KM_DIGEST_SHA1:
+ return HMAC_SHA1;
+ case KeymasterDefs.KM_DIGEST_SHA_2_224:
+ return HMAC_SHA224;
+ case KeymasterDefs.KM_DIGEST_SHA_2_256:
+ return HMAC_SHA256;
+ case KeymasterDefs.KM_DIGEST_SHA_2_384:
+ return HMAC_SHA384;
+ case KeymasterDefs.KM_DIGEST_SHA_2_512:
+ return HMAC_SHA512;
+ default:
+ throw new IllegalArgumentException("Unsupported HMAC digest: "
+ + Digest.fromKeymaster(keymasterDigest));
+ }
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported algorithm: " + keymasterAlgorithm);
+ }
+ }
+
+ /**
+ * @hide
+ *
+ * @return keymaster digest or {@code -1} if the algorithm does not involve a digest.
+ */
+ static int toKeymasterDigest(@AlgorithmEnum String algorithm) {
+ String algorithmUpper = algorithm.toUpperCase(Locale.US);
+ if (algorithmUpper.startsWith("HMAC")) {
+ String digestUpper = algorithmUpper.substring("HMAC".length());
+ switch (digestUpper) {
+ case "SHA1":
+ return KeymasterDefs.KM_DIGEST_SHA1;
+ case "SHA224":
+ return KeymasterDefs.KM_DIGEST_SHA_2_224;
+ case "SHA256":
+ return KeymasterDefs.KM_DIGEST_SHA_2_256;
+ case "SHA384":
+ return KeymasterDefs.KM_DIGEST_SHA_2_384;
+ case "SHA512":
+ return KeymasterDefs.KM_DIGEST_SHA_2_512;
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported HMAC digest: " + digestUpper);
+ }
+ } else {
+ return -1;
+ }
+ }
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef({
+ BlockMode.ECB,
+ BlockMode.CBC,
+ BlockMode.CTR,
+ BlockMode.GCM,
+ })
+ public @interface BlockModeEnum {}
+
+ /**
+ * Block modes that can be used when encrypting/decrypting using a key.
+ */
+ public static abstract class BlockMode {
+ private BlockMode() {}
+
+ /** Electronic Codebook (ECB) block mode. */
+ public static final String ECB = "ECB";
+
+ /** Cipher Block Chaining (CBC) block mode. */
+ public static final String CBC = "CBC";
+
+ /** Counter (CTR) block mode. */
+ public static final String CTR = "CTR";
+
+ /** Galois/Counter Mode (GCM) block mode. */
+ public static final String GCM = "GCM";
+
+ /**
+ * @hide
+ */
+ static int toKeymaster(@BlockModeEnum String blockMode) {
+ if (ECB.equalsIgnoreCase(blockMode)) {
+ return KeymasterDefs.KM_MODE_ECB;
+ } else if (CBC.equalsIgnoreCase(blockMode)) {
+ return KeymasterDefs.KM_MODE_CBC;
+ } else if (CTR.equalsIgnoreCase(blockMode)) {
+ return KeymasterDefs.KM_MODE_CTR;
+ } else if (GCM.equalsIgnoreCase(blockMode)) {
+ return KeymasterDefs.KM_MODE_GCM;
+ } else {
+ throw new IllegalArgumentException("Unsupported block mode: " + blockMode);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static @BlockModeEnum String fromKeymaster(int blockMode) {
+ switch (blockMode) {
+ case KeymasterDefs.KM_MODE_ECB:
+ return ECB;
+ case KeymasterDefs.KM_MODE_CBC:
+ return CBC;
+ case KeymasterDefs.KM_MODE_CTR:
+ return CTR;
+ case KeymasterDefs.KM_MODE_GCM:
+ return GCM;
+ default:
+ throw new IllegalArgumentException("Unsupported block mode: " + blockMode);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static @BlockModeEnum String[] allFromKeymaster(Collection<Integer> blockModes) {
+ if ((blockModes == null) || (blockModes.isEmpty())) {
+ return EmptyArray.STRING;
+ }
+ @BlockModeEnum String[] result = new String[blockModes.size()];
+ int offset = 0;
+ for (int blockMode : blockModes) {
+ result[offset] = fromKeymaster(blockMode);
+ offset++;
+ }
+ return result;
+ }
+
+ /**
+ * @hide
+ */
+ static int[] allToKeymaster(@BlockModeEnum String[] blockModes) {
+ if ((blockModes == null) || (blockModes.length == 0)) {
+ return EmptyArray.INT;
+ }
+ int[] result = new int[blockModes.length];
+ for (int i = 0; i < blockModes.length; i++) {
+ result[i] = toKeymaster(blockModes[i]);
+ }
+ return result;
+ }
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef({
+ EncryptionPadding.NONE,
+ EncryptionPadding.PKCS7,
+ EncryptionPadding.RSA_PKCS1,
+ EncryptionPadding.RSA_OAEP,
+ })
+ public @interface EncryptionPaddingEnum {}
+
+ /**
+ * Padding schemes for encryption/decryption.
+ */
+ public static abstract class EncryptionPadding {
+ private EncryptionPadding() {}
+
+ /**
+ * No padding.
+ */
+ public static final String NONE = "NoPadding";
+
+ /**
+ * PKCS#7 padding.
+ */
+ public static final String PKCS7 = "PKCS7Padding";
+
+ /**
+ * RSA PKCS#1 v1.5 padding for encryption/decryption.
+ */
+ public static final String RSA_PKCS1 = "PKCS1Padding";
+
+ /**
+ * RSA Optimal Asymmetric Encryption Padding (OAEP).
+ */
+ public static final String RSA_OAEP = "OAEPPadding";
+
+ /**
+ * @hide
+ */
+ static int toKeymaster(@EncryptionPaddingEnum String padding) {
+ if (NONE.equalsIgnoreCase(padding)) {
+ return KeymasterDefs.KM_PAD_NONE;
+ } else if (PKCS7.equalsIgnoreCase(padding)) {
+ return KeymasterDefs.KM_PAD_PKCS7;
+ } else if (RSA_PKCS1.equalsIgnoreCase(padding)) {
+ return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT;
+ } else if (RSA_OAEP.equalsIgnoreCase(padding)) {
+ return KeymasterDefs.KM_PAD_RSA_OAEP;
+ } else {
+ throw new IllegalArgumentException(
+ "Unsupported encryption padding scheme: " + padding);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static @EncryptionPaddingEnum String fromKeymaster(int padding) {
+ switch (padding) {
+ case KeymasterDefs.KM_PAD_NONE:
+ return NONE;
+ case KeymasterDefs.KM_PAD_PKCS7:
+ return PKCS7;
+ case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
+ return RSA_PKCS1;
+ case KeymasterDefs.KM_PAD_RSA_OAEP:
+ return RSA_OAEP;
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported encryption padding: " + padding);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static int[] allToKeymaster(@EncryptionPaddingEnum String[] paddings) {
+ if ((paddings == null) || (paddings.length == 0)) {
+ return EmptyArray.INT;
+ }
+ int[] result = new int[paddings.length];
+ for (int i = 0; i < paddings.length; i++) {
+ result[i] = toKeymaster(paddings[i]);
+ }
+ return result;
+ }
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef({
+ SignaturePadding.RSA_PKCS1,
+ SignaturePadding.RSA_PSS,
+ })
+ public @interface SignaturePaddingEnum {}
+
+ /**
+ * Padding schemes for signing/verification.
+ */
+ public static abstract class SignaturePadding {
+ private SignaturePadding() {}
+
+ /**
+ * RSA PKCS#1 v1.5 padding for signatures.
+ */
+ public static final String RSA_PKCS1 = "PKCS1";
+
+ /**
+ * RSA PKCS#1 v2.1 Probabilistic Signature Scheme (PSS) padding.
+ */
+ public static final String RSA_PSS = "PSS";
+
+ /**
+ * @hide
+ */
+ static int toKeymaster(@SignaturePaddingEnum String padding) {
+ switch (padding.toUpperCase(Locale.US)) {
+ case RSA_PKCS1:
+ return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN;
+ case RSA_PSS:
+ return KeymasterDefs.KM_PAD_RSA_PSS;
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported signature padding scheme: " + padding);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static @SignaturePaddingEnum String fromKeymaster(int padding) {
+ switch (padding) {
+ case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN:
+ return RSA_PKCS1;
+ case KeymasterDefs.KM_PAD_RSA_PSS:
+ return RSA_PSS;
+ default:
+ throw new IllegalArgumentException("Unsupported signature padding: " + padding);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static int[] allToKeymaster(@SignaturePaddingEnum String[] paddings) {
+ if ((paddings == null) || (paddings.length == 0)) {
+ return EmptyArray.INT;
+ }
+ int[] result = new int[paddings.length];
+ for (int i = 0; i < paddings.length; i++) {
+ result[i] = toKeymaster(paddings[i]);
+ }
+ return result;
+ }
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef({
+ Digest.NONE,
+ Digest.MD5,
+ Digest.SHA1,
+ Digest.SHA224,
+ Digest.SHA256,
+ Digest.SHA384,
+ Digest.SHA512,
+ })
+ public @interface DigestEnum {}
+
+ /**
+ * Digests that can be used with a key when signing or generating Message Authentication
+ * Codes (MACs).
+ */
+ public static abstract class Digest {
+ private Digest() {}
+
+ /**
+ * No digest: sign/authenticate the raw message.
+ */
+ public static final String NONE = "NONE";
+
+ /**
+ * MD5 digest.
+ */
+ public static final String MD5 = "MD5";
+
+ /**
+ * SHA-1 digest.
+ */
+ public static final String SHA1 = "SHA-1";
+
+ /**
+ * SHA-2 224 (aka SHA-224) digest.
+ */
+ public static final String SHA224 = "SHA-224";
+
+ /**
+ * SHA-2 256 (aka SHA-256) digest.
+ */
+ public static final String SHA256 = "SHA-256";
+
+ /**
+ * SHA-2 384 (aka SHA-384) digest.
+ */
+ public static final String SHA384 = "SHA-384";
+
+ /**
+ * SHA-2 512 (aka SHA-512) digest.
+ */
+ public static final String SHA512 = "SHA-512";
+
+ /**
+ * @hide
+ */
+ static int toKeymaster(@DigestEnum String digest) {
+ switch (digest.toUpperCase(Locale.US)) {
+ case SHA1:
+ return KeymasterDefs.KM_DIGEST_SHA1;
+ case SHA224:
+ return KeymasterDefs.KM_DIGEST_SHA_2_224;
+ case SHA256:
+ return KeymasterDefs.KM_DIGEST_SHA_2_256;
+ case SHA384:
+ return KeymasterDefs.KM_DIGEST_SHA_2_384;
+ case SHA512:
+ return KeymasterDefs.KM_DIGEST_SHA_2_512;
+ case NONE:
+ return KeymasterDefs.KM_DIGEST_NONE;
+ case MD5:
+ return KeymasterDefs.KM_DIGEST_MD5;
+ default:
+ throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static @DigestEnum String fromKeymaster(int digest) {
+ switch (digest) {
+ case KeymasterDefs.KM_DIGEST_NONE:
+ return NONE;
+ case KeymasterDefs.KM_DIGEST_MD5:
+ return MD5;
+ case KeymasterDefs.KM_DIGEST_SHA1:
+ return SHA1;
+ case KeymasterDefs.KM_DIGEST_SHA_2_224:
+ return SHA224;
+ case KeymasterDefs.KM_DIGEST_SHA_2_256:
+ return SHA256;
+ case KeymasterDefs.KM_DIGEST_SHA_2_384:
+ return SHA384;
+ case KeymasterDefs.KM_DIGEST_SHA_2_512:
+ return SHA512;
+ default:
+ throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static @DigestEnum String[] allFromKeymaster(Collection<Integer> digests) {
+ if (digests.isEmpty()) {
+ return EmptyArray.STRING;
+ }
+ String[] result = new String[digests.size()];
+ int offset = 0;
+ for (int digest : digests) {
+ result[offset] = fromKeymaster(digest);
+ offset++;
+ }
+ return result;
+ }
+
+ /**
+ * @hide
+ */
+ static int[] allToKeymaster(@DigestEnum String[] digests) {
+ if ((digests == null) || (digests.length == 0)) {
+ return EmptyArray.INT;
+ }
+ int[] result = new int[digests.length];
+ int offset = 0;
+ for (@DigestEnum String digest : digests) {
+ result[offset] = toKeymaster(digest);
+ offset++;
+ }
+ return result;
+ }
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
@IntDef({Origin.GENERATED, Origin.IMPORTED, Origin.UNKNOWN})
public @interface OriginEnum {}
@@ -138,7 +656,7 @@
public static final int IMPORTED = 1 << 1;
/**
- * Origin of the key is unknown. This can occur only for keys backed by an old TEE
+ * Origin of the key is unknown. This can occur only for keys backed by an old TEE-backed
* implementation which does not record origin information.
*/
public static final int UNKNOWN = 1 << 2;
diff --git a/keystore/java/android/security/KeyStoreKeySpec.java b/keystore/java/android/security/KeyStoreKeySpec.java
index 96d58d8..a630a0a 100644
--- a/keystore/java/android/security/KeyStoreKeySpec.java
+++ b/keystore/java/android/security/KeyStoreKeySpec.java
@@ -26,40 +26,40 @@
public class KeyStoreKeySpec implements KeySpec {
private final String mKeystoreAlias;
private final int mKeySize;
- private final boolean mTeeBacked;
+ private final boolean mInsideSecureHardware;
private final @KeyStoreKeyProperties.OriginEnum int mOrigin;
private final Date mKeyValidityStart;
private final Date mKeyValidityForOriginationEnd;
private final Date mKeyValidityForConsumptionEnd;
private final @KeyStoreKeyProperties.PurposeEnum int mPurposes;
- private final String[] mEncryptionPaddings;
- private final String[] mSignaturePaddings;
- private final String[] mDigests;
- private final String[] mBlockModes;
+ private final @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
+ private final @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
+ private final @KeyStoreKeyProperties.DigestEnum String[] mDigests;
+ private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
private final boolean mUserAuthenticationRequired;
private final int mUserAuthenticationValidityDurationSeconds;
- private final boolean mUserAuthenticationRequirementTeeEnforced;
+ private final boolean mUserAuthenticationRequirementEnforcedBySecureHardware;
/**
* @hide
*/
KeyStoreKeySpec(String keystoreKeyAlias,
- boolean teeBacked,
+ boolean insideSecureHardware,
@KeyStoreKeyProperties.OriginEnum int origin,
int keySize,
Date keyValidityStart,
Date keyValidityForOriginationEnd,
Date keyValidityForConsumptionEnd,
@KeyStoreKeyProperties.PurposeEnum int purposes,
- String[] encryptionPaddings,
- String[] signaturePaddings,
- String[] digests,
- String[] blockModes,
+ @KeyStoreKeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
+ @KeyStoreKeyProperties.SignaturePaddingEnum String[] signaturePaddings,
+ @KeyStoreKeyProperties.DigestEnum String[] digests,
+ @KeyStoreKeyProperties.BlockModeEnum String[] blockModes,
boolean userAuthenticationRequired,
int userAuthenticationValidityDurationSeconds,
- boolean userAuthenticationRequirementTeeEnforced) {
+ boolean userAuthenticationRequirementEnforcedBySecureHardware) {
mKeystoreAlias = keystoreKeyAlias;
- mTeeBacked = teeBacked;
+ mInsideSecureHardware = insideSecureHardware;
mOrigin = origin;
mKeySize = keySize;
mKeyValidityStart = keyValidityStart;
@@ -74,7 +74,8 @@
mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes));
mUserAuthenticationRequired = userAuthenticationRequired;
mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
- mUserAuthenticationRequirementTeeEnforced = userAuthenticationRequirementTeeEnforced;
+ mUserAuthenticationRequirementEnforcedBySecureHardware =
+ userAuthenticationRequirementEnforcedBySecureHardware;
}
/**
@@ -85,11 +86,12 @@
}
/**
- * Returns {@code true} if the key is TEE-backed. Key material of TEE-backed keys is available
- * in plaintext only inside the TEE.
+ * Returns {@code true} if the key resides inside secure hardware (e.g., Trusted Execution
+ * Environment (TEE) or Secure Element (SE)). Key material of such keys is available in
+ * plaintext only inside the secure hardware and is not exposed outside of it.
*/
- public boolean isTeeBacked() {
- return mTeeBacked;
+ public boolean isInsideSecureHardware() {
+ return mInsideSecureHardware;
}
/**
@@ -143,28 +145,28 @@
/**
* Gets the set of block modes with which the key can be used.
*/
- public String[] getBlockModes() {
+ public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() {
return ArrayUtils.cloneIfNotEmpty(mBlockModes);
}
/**
* Gets the set of padding modes with which the key can be used when encrypting/decrypting.
*/
- public String[] getEncryptionPaddings() {
+ public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
}
/**
* Gets the set of padding modes with which the key can be used when signing/verifying.
*/
- public String[] getSignaturePaddings() {
+ public @KeyStoreKeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() {
return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings);
}
/**
* Gets the set of digest algorithms with which the key can be used.
*/
- public String[] getDigests() {
+ public @KeyStoreKeyProperties.DigestEnum String[] getDigests() {
return ArrayUtils.cloneIfNotEmpty(mDigests);
}
@@ -179,10 +181,10 @@
/**
* Gets the duration of time (seconds) for which this key can be used after the user is
- * successfully authenticated.
+ * successfully authenticated. This has effect only if user authentication is required.
*
- * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication
- * is required for every use of the key.
+ * @return duration in seconds or {@code -1} if authentication is required for every use of the
+ * key.
*
* @see #isUserAuthenticationRequired()
*/
@@ -192,11 +194,12 @@
/**
* Returns {@code true} if the requirement that this key can only be used if the user has been
- * authenticated if enforced by the TEE.
+ * authenticated if enforced by secure hardware (e.g., Trusted Execution Environment (TEE) or
+ * Secure Element (SE)).
*
* @see #isUserAuthenticationRequired()
*/
- public boolean isUserAuthenticationRequirementTeeEnforced() {
- return mUserAuthenticationRequirementTeeEnforced;
+ public boolean isUserAuthenticationRequirementEnforcedBySecureHardware() {
+ return mUserAuthenticationRequirementEnforcedBySecureHardware;
}
}
diff --git a/keystore/java/android/security/KeyStoreParameter.java b/keystore/java/android/security/KeyStoreParameter.java
index b4747e9..8d7a19f 100644
--- a/keystore/java/android/security/KeyStoreParameter.java
+++ b/keystore/java/android/security/KeyStoreParameter.java
@@ -45,10 +45,10 @@
private final Date mKeyValidityForOriginationEnd;
private final Date mKeyValidityForConsumptionEnd;
private final @KeyStoreKeyProperties.PurposeEnum int mPurposes;
- private final String[] mEncryptionPaddings;
- private final String[] mSignaturePaddings;
- private final String[] mDigests;
- private final String[] mBlockModes;
+ private final @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
+ private final @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
+ private final @KeyStoreKeyProperties.DigestEnum String[] mDigests;
+ private final @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
private final boolean mRandomizedEncryptionRequired;
private final boolean mUserAuthenticationRequired;
private final int mUserAuthenticationValidityDurationSeconds;
@@ -60,10 +60,10 @@
Date keyValidityForOriginationEnd,
Date keyValidityForConsumptionEnd,
@KeyStoreKeyProperties.PurposeEnum int purposes,
- String[] encryptionPaddings,
- String[] signaturePaddings,
- String[] digests,
- String[] blockModes,
+ @KeyStoreKeyProperties.EncryptionPaddingEnum String[] encryptionPaddings,
+ @KeyStoreKeyProperties.SignaturePaddingEnum String[] signaturePaddings,
+ @KeyStoreKeyProperties.DigestEnum String[] digests,
+ @KeyStoreKeyProperties.BlockModeEnum String[] blockModes,
boolean randomizedEncryptionRequired,
boolean userAuthenticationRequired,
int userAuthenticationValidityDurationSeconds) {
@@ -151,7 +151,7 @@
/**
* Gets the set of padding schemes with which the key can be used when encrypting/decrypting.
*/
- public String[] getEncryptionPaddings() {
+ public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
}
@@ -159,7 +159,7 @@
* Gets the set of padding schemes with which the key can be used when signing or verifying
* signatures.
*/
- public String[] getSignaturePaddings() {
+ public @KeyStoreKeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() {
return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings);
}
@@ -170,7 +170,7 @@
*
* @see #isDigestsSpecified()
*/
- public String[] getDigests() {
+ public @KeyStoreKeyProperties.DigestEnum String[] getDigests() {
if (mDigests == null) {
throw new IllegalStateException("Digests not specified");
}
@@ -190,7 +190,7 @@
/**
* Gets the set of block modes with which the key can be used.
*/
- public String[] getBlockModes() {
+ public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() {
return ArrayUtils.cloneIfNotEmpty(mBlockModes);
}
@@ -218,10 +218,12 @@
/**
* Gets the duration of time (seconds) for which this key can be used after the user is
- * successfully authenticated.
+ * successfully authenticated. This has effect only if user authentication is required.
*
- * @return duration in seconds or {@code -1} if not restricted. {@code 0} means authentication
- * is required for every use of the key.
+ * @return duration in seconds or {@code -1} if authentication is required for every use of the
+ * key.
+ *
+ * @see #isUserAuthenticationRequired()
*/
public int getUserAuthenticationValidityDurationSeconds() {
return mUserAuthenticationValidityDurationSeconds;
@@ -251,10 +253,10 @@
private Date mKeyValidityForOriginationEnd;
private Date mKeyValidityForConsumptionEnd;
private @KeyStoreKeyProperties.PurposeEnum int mPurposes;
- private String[] mEncryptionPaddings;
- private String[] mSignaturePaddings;
- private String[] mDigests;
- private String[] mBlockModes;
+ private @KeyStoreKeyProperties.EncryptionPaddingEnum String[] mEncryptionPaddings;
+ private @KeyStoreKeyProperties.SignaturePaddingEnum String[] mSignaturePaddings;
+ private @KeyStoreKeyProperties.DigestEnum String[] mDigests;
+ private @KeyStoreKeyProperties.BlockModeEnum String[] mBlockModes;
private boolean mRandomizedEncryptionRequired = true;
private boolean mUserAuthenticationRequired;
private int mUserAuthenticationValidityDurationSeconds = -1;
@@ -292,6 +294,8 @@
*
* <p>By default, the key is valid at any instant.
*
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
+ *
* @see #setKeyValidityEnd(Date)
*/
public Builder setKeyValidityStart(Date startDate) {
@@ -304,6 +308,8 @@
*
* <p>By default, the key is valid at any instant.
*
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
+ *
* @see #setKeyValidityStart(Date)
* @see #setKeyValidityForConsumptionEnd(Date)
* @see #setKeyValidityForOriginationEnd(Date)
@@ -319,6 +325,8 @@
*
* <p>By default, the key is valid at any instant.
*
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
+ *
* @see #setKeyValidityForConsumptionEnd(Date)
*/
public Builder setKeyValidityForOriginationEnd(Date endDate) {
@@ -332,6 +340,8 @@
*
* <p>By default, the key is valid at any instant.
*
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
+ *
* @see #setKeyValidityForOriginationEnd(Date)
*/
public Builder setKeyValidityForConsumptionEnd(Date endDate) {
@@ -343,6 +353,8 @@
* Sets the set of purposes for which the key can be used.
*
* <p>This must be specified for all keys. There is no default.
+ *
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
*/
public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) {
mPurposes = purposes;
@@ -355,8 +367,11 @@
* rejected.
*
* <p>This must be specified for keys which are used for encryption/decryption.
+ *
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
*/
- public Builder setEncryptionPaddings(String... paddings) {
+ public Builder setEncryptionPaddings(
+ @KeyStoreKeyProperties.EncryptionPaddingEnum String... paddings) {
mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings);
return this;
}
@@ -367,8 +382,11 @@
* rejected.
*
* <p>This must be specified for RSA keys which are used for signing/verification.
+ *
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
*/
- public Builder setSignaturePaddings(String... paddings) {
+ public Builder setSignaturePaddings(
+ @KeyStoreKeyProperties.SignaturePaddingEnum String... paddings) {
mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(paddings);
return this;
}
@@ -380,8 +398,10 @@
*
* <p>For HMAC keys, the default is the digest specified in {@link Key#getAlgorithm()}. For
* asymmetric signing keys this constraint must be specified.
+ *
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
*/
- public Builder setDigests(String... digests) {
+ public Builder setDigests(@KeyStoreKeyProperties.DigestEnum String... digests) {
mDigests = ArrayUtils.cloneIfNotEmpty(digests);
return this;
}
@@ -391,8 +411,10 @@
* Attempts to use the key with any other block modes will be rejected.
*
* <p>This must be specified for encryption/decryption keys.
+ *
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
*/
- public Builder setBlockModes(String... blockModes) {
+ public Builder setBlockModes(@KeyStoreKeyProperties.BlockModeEnum String... blockModes) {
mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes);
return this;
}
@@ -430,6 +452,8 @@
* <li>If you are using RSA encryption without padding, consider switching to padding
* schemes which offer {@code IND-CPA}, such as PKCS#1 or OAEP.</li>
* </ul>
+ *
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
*/
public Builder setRandomizedEncryptionRequired(boolean required) {
mRandomizedEncryptionRequired = required;
@@ -449,6 +473,8 @@
* <a href="{@docRoot}training/articles/keystore.html#UserAuthentication">More
* information</a>.
*
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
+ *
* @see #setUserAuthenticationValidityDurationSeconds(int)
*/
public Builder setUserAuthenticationRequired(boolean required) {
@@ -462,7 +488,9 @@
*
* <p>By default, the user needs to authenticate for every use of the key.
*
- * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
+ * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
+ *
+ * @param seconds duration in seconds or {@code -1} if the user needs to authenticate for
* every use of the key.
*
* @see #setUserAuthenticationRequired(boolean)
diff --git a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
index bfe09e3..548296b 100644
--- a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
@@ -74,22 +74,22 @@
+ " Keystore error: " + errorCode);
}
- boolean teeBacked;
+ boolean insideSecureHardware;
@KeyStoreKeyProperties.OriginEnum int origin;
int keySize;
@KeyStoreKeyProperties.PurposeEnum int purposes;
String[] encryptionPaddings;
- String[] digests;
- String[] blockModes;
+ @KeyStoreKeyProperties.DigestEnum String[] digests;
+ @KeyStoreKeyProperties.BlockModeEnum String[] blockModes;
int keymasterSwEnforcedUserAuthenticators;
int keymasterHwEnforcedUserAuthenticators;
try {
if (keyCharacteristics.hwEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) {
- teeBacked = true;
+ insideSecureHardware = true;
origin = KeyStoreKeyProperties.Origin.fromKeymaster(
keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_ORIGIN, -1));
} else if (keyCharacteristics.swEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) {
- teeBacked = false;
+ insideSecureHardware = false;
origin = KeyStoreKeyProperties.Origin.fromKeymaster(
keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_ORIGIN, -1));
} else {
@@ -105,10 +105,10 @@
List<String> encryptionPaddingsList = new ArrayList<String>();
for (int keymasterPadding : keyCharacteristics.getInts(KeymasterDefs.KM_TAG_PADDING)) {
- String jcaPadding;
+ @KeyStoreKeyProperties.EncryptionPaddingEnum String jcaPadding;
try {
- jcaPadding = KeymasterUtils.getJcaEncryptionPaddingFromKeymasterPadding(
- keymasterPadding);
+ jcaPadding =
+ KeyStoreKeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding);
} catch (IllegalArgumentException e) {
throw new InvalidKeySpecException(
"Unsupported encryption padding: " + keymasterPadding);
@@ -118,9 +118,9 @@
encryptionPaddings =
encryptionPaddingsList.toArray(new String[encryptionPaddingsList.size()]);
- digests = KeymasterUtils.getJcaDigestAlgorithmsFromKeymasterDigests(
+ digests = KeyStoreKeyProperties.Digest.allFromKeymaster(
keyCharacteristics.getInts(KeymasterDefs.KM_TAG_DIGEST));
- blockModes = KeymasterUtils.getJcaBlockModesFromKeymasterBlockModes(
+ blockModes = KeyStoreKeyProperties.BlockMode.allFromKeymaster(
keyCharacteristics.getInts(KeymasterDefs.KM_TAG_BLOCK_MODE));
keymasterSwEnforcedUserAuthenticators =
keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
@@ -150,12 +150,12 @@
!keyCharacteristics.getBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
int userAuthenticationValidityDurationSeconds =
keyCharacteristics.getInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, -1);
- boolean userAuthenticationRequirementEnforcedInTee = (userAuthenticationRequired)
+ boolean userAuthenticationRequirementEnforcedBySecureHardware = (userAuthenticationRequired)
&& (keymasterHwEnforcedUserAuthenticators != 0)
&& (keymasterSwEnforcedUserAuthenticators == 0);
return new KeyStoreKeySpec(entryAlias,
- teeBacked,
+ insideSecureHardware,
origin,
keySize,
keyValidityStart,
@@ -168,7 +168,7 @@
blockModes,
userAuthenticationRequired,
userAuthenticationValidityDurationSeconds,
- userAuthenticationRequirementEnforcedInTee);
+ userAuthenticationRequirementEnforcedBySecureHardware);
}
@Override
diff --git a/keystore/java/android/security/KeymasterUtils.java b/keystore/java/android/security/KeymasterUtils.java
index aa44ecd..df67ae7 100644
--- a/keystore/java/android/security/KeymasterUtils.java
+++ b/keystore/java/android/security/KeymasterUtils.java
@@ -21,11 +21,6 @@
import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;
-import libcore.util.EmptyArray;
-
-import java.util.Collection;
-import java.util.Locale;
-
/**
* @hide
*/
@@ -33,152 +28,6 @@
private KeymasterUtils() {}
- public static int getKeymasterAlgorithmFromJcaSecretKeyAlgorithm(String jcaKeyAlgorithm) {
- if ("AES".equalsIgnoreCase(jcaKeyAlgorithm)) {
- return KeymasterDefs.KM_ALGORITHM_AES;
- } else if (jcaKeyAlgorithm.toUpperCase(Locale.US).startsWith("HMAC")) {
- return KeymasterDefs.KM_ALGORITHM_HMAC;
- } else {
- throw new IllegalArgumentException(
- "Unsupported secret key algorithm: " + jcaKeyAlgorithm);
- }
- }
-
- public static String getJcaSecretKeyAlgorithm(int keymasterAlgorithm, int keymasterDigest) {
- switch (keymasterAlgorithm) {
- case KeymasterDefs.KM_ALGORITHM_AES:
- if (keymasterDigest != -1) {
- throw new IllegalArgumentException(
- "Digest not supported for AES key: " + keymasterDigest);
- }
- return "AES";
- case KeymasterDefs.KM_ALGORITHM_HMAC:
- switch (keymasterDigest) {
- case KeymasterDefs.KM_DIGEST_SHA1:
- return "HmacSHA1";
- case KeymasterDefs.KM_DIGEST_SHA_2_224:
- return "HmacSHA224";
- case KeymasterDefs.KM_DIGEST_SHA_2_256:
- return "HmacSHA256";
- case KeymasterDefs.KM_DIGEST_SHA_2_384:
- return "HmacSHA384";
- case KeymasterDefs.KM_DIGEST_SHA_2_512:
- return "HmacSHA512";
- default:
- throw new IllegalArgumentException(
- "Unsupported HMAC digest: " + keymasterDigest);
- }
- default:
- throw new IllegalArgumentException("Unsupported algorithm: " + keymasterAlgorithm);
- }
- }
-
- public static String getJcaKeyPairAlgorithmFromKeymasterAlgorithm(int keymasterAlgorithm) {
- switch (keymasterAlgorithm) {
- case KeymasterDefs.KM_ALGORITHM_RSA:
- return "RSA";
- case KeymasterDefs.KM_ALGORITHM_EC:
- return "EC";
- default:
- throw new IllegalArgumentException("Unsupported algorithm: " + keymasterAlgorithm);
- }
- }
-
- public static int getKeymasterDigestfromJcaSecretKeyAlgorithm(String jcaKeyAlgorithm) {
- String algorithmUpper = jcaKeyAlgorithm.toUpperCase(Locale.US);
- if (algorithmUpper.startsWith("HMAC")) {
- String digestUpper = algorithmUpper.substring("HMAC".length());
- switch (digestUpper) {
- case "MD5":
- return KeymasterDefs.KM_DIGEST_MD5;
- case "SHA1":
- return KeymasterDefs.KM_DIGEST_SHA1;
- case "SHA224":
- return KeymasterDefs.KM_DIGEST_SHA_2_224;
- case "SHA256":
- return KeymasterDefs.KM_DIGEST_SHA_2_256;
- case "SHA384":
- return KeymasterDefs.KM_DIGEST_SHA_2_384;
- case "SHA512":
- return KeymasterDefs.KM_DIGEST_SHA_2_512;
- default:
- throw new IllegalArgumentException("Unsupported HMAC digest: " + digestUpper);
- }
- } else {
- return -1;
- }
- }
-
- public static int getKeymasterDigestFromJcaDigestAlgorithm(String jcaDigestAlgorithm) {
- if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-1")) {
- return KeymasterDefs.KM_DIGEST_SHA1;
- } else if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-224")) {
- return KeymasterDefs.KM_DIGEST_SHA_2_224;
- } else if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-256")) {
- return KeymasterDefs.KM_DIGEST_SHA_2_256;
- } else if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-384")) {
- return KeymasterDefs.KM_DIGEST_SHA_2_384;
- } else if (jcaDigestAlgorithm.equalsIgnoreCase("SHA-512")) {
- return KeymasterDefs.KM_DIGEST_SHA_2_512;
- } else if (jcaDigestAlgorithm.equalsIgnoreCase("NONE")) {
- return KeymasterDefs.KM_DIGEST_NONE;
- } else if (jcaDigestAlgorithm.equalsIgnoreCase("MD5")) {
- return KeymasterDefs.KM_DIGEST_MD5;
- } else {
- throw new IllegalArgumentException(
- "Unsupported digest algorithm: " + jcaDigestAlgorithm);
- }
- }
-
- public static String getJcaDigestAlgorithmFromKeymasterDigest(int keymasterDigest) {
- switch (keymasterDigest) {
- case KeymasterDefs.KM_DIGEST_NONE:
- return "NONE";
- case KeymasterDefs.KM_DIGEST_MD5:
- return "MD5";
- case KeymasterDefs.KM_DIGEST_SHA1:
- return "SHA-1";
- case KeymasterDefs.KM_DIGEST_SHA_2_224:
- return "SHA-224";
- case KeymasterDefs.KM_DIGEST_SHA_2_256:
- return "SHA-256";
- case KeymasterDefs.KM_DIGEST_SHA_2_384:
- return "SHA-384";
- case KeymasterDefs.KM_DIGEST_SHA_2_512:
- return "SHA-512";
- default:
- throw new IllegalArgumentException(
- "Unsupported digest algorithm: " + keymasterDigest);
- }
- }
-
- public static String[] getJcaDigestAlgorithmsFromKeymasterDigests(
- Collection<Integer> keymasterDigests) {
- if (keymasterDigests.isEmpty()) {
- return EmptyArray.STRING;
- }
- String[] result = new String[keymasterDigests.size()];
- int offset = 0;
- for (int keymasterDigest : keymasterDigests) {
- result[offset] = getJcaDigestAlgorithmFromKeymasterDigest(keymasterDigest);
- offset++;
- }
- return result;
- }
-
- public static int[] getKeymasterDigestsFromJcaDigestAlgorithms(String[] jcaDigestAlgorithms) {
- if ((jcaDigestAlgorithms == null) || (jcaDigestAlgorithms.length == 0)) {
- return EmptyArray.INT;
- }
- int[] result = new int[jcaDigestAlgorithms.length];
- int offset = 0;
- for (String jcaDigestAlgorithm : jcaDigestAlgorithms) {
- result[offset] = getKeymasterDigestFromJcaDigestAlgorithm(jcaDigestAlgorithm);
- offset++;
- }
- return result;
- }
-
public static int getDigestOutputSizeBits(int keymasterDigest) {
switch (keymasterDigest) {
case KeymasterDefs.KM_DIGEST_NONE:
@@ -200,60 +49,6 @@
}
}
- public static int getKeymasterBlockModeFromJcaBlockMode(String jcaBlockMode) {
- if ("ECB".equalsIgnoreCase(jcaBlockMode)) {
- return KeymasterDefs.KM_MODE_ECB;
- } else if ("CBC".equalsIgnoreCase(jcaBlockMode)) {
- return KeymasterDefs.KM_MODE_CBC;
- } else if ("CTR".equalsIgnoreCase(jcaBlockMode)) {
- return KeymasterDefs.KM_MODE_CTR;
- } else if ("GCM".equalsIgnoreCase(jcaBlockMode)) {
- return KeymasterDefs.KM_MODE_GCM;
- } else {
- throw new IllegalArgumentException("Unsupported block mode: " + jcaBlockMode);
- }
- }
-
- public static String getJcaBlockModeFromKeymasterBlockMode(int keymasterBlockMode) {
- switch (keymasterBlockMode) {
- case KeymasterDefs.KM_MODE_ECB:
- return "ECB";
- case KeymasterDefs.KM_MODE_CBC:
- return "CBC";
- case KeymasterDefs.KM_MODE_CTR:
- return "CTR";
- case KeymasterDefs.KM_MODE_GCM:
- return "GCM";
- default:
- throw new IllegalArgumentException("Unsupported block mode: " + keymasterBlockMode);
- }
- }
-
- public static String[] getJcaBlockModesFromKeymasterBlockModes(
- Collection<Integer> keymasterBlockModes) {
- if ((keymasterBlockModes == null) || (keymasterBlockModes.isEmpty())) {
- return EmptyArray.STRING;
- }
- String[] result = new String[keymasterBlockModes.size()];
- int offset = 0;
- for (int keymasterBlockMode : keymasterBlockModes) {
- result[offset] = getJcaBlockModeFromKeymasterBlockMode(keymasterBlockMode);
- offset++;
- }
- return result;
- }
-
- public static int[] getKeymasterBlockModesFromJcaBlockModes(String[] jcaBlockModes) {
- if ((jcaBlockModes == null) || (jcaBlockModes.length == 0)) {
- return EmptyArray.INT;
- }
- int[] result = new int[jcaBlockModes.length];
- for (int i = 0; i < jcaBlockModes.length; i++) {
- result[i] = getKeymasterBlockModeFromJcaBlockMode(jcaBlockModes[i]);
- }
- return result;
- }
-
public static boolean isKeymasterBlockModeIndCpaCompatible(int keymasterBlockMode) {
switch (keymasterBlockMode) {
case KeymasterDefs.KM_MODE_ECB:
@@ -267,82 +62,6 @@
}
}
- public static int getKeymasterPaddingFromJcaEncryptionPadding(String jcaPadding) {
- if ("NoPadding".equalsIgnoreCase(jcaPadding)) {
- return KeymasterDefs.KM_PAD_NONE;
- } else if ("PKCS7Padding".equalsIgnoreCase(jcaPadding)) {
- return KeymasterDefs.KM_PAD_PKCS7;
- } else if ("PKCS1Padding".equalsIgnoreCase(jcaPadding)) {
- return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT;
- } else if ("OEAPPadding".equalsIgnoreCase(jcaPadding)) {
- return KeymasterDefs.KM_PAD_RSA_OAEP;
- } else {
- throw new IllegalArgumentException(
- "Unsupported encryption padding scheme: " + jcaPadding);
- }
- }
-
- public static String getJcaEncryptionPaddingFromKeymasterPadding(int keymasterPadding) {
- switch (keymasterPadding) {
- case KeymasterDefs.KM_PAD_NONE:
- return "NoPadding";
- case KeymasterDefs.KM_PAD_PKCS7:
- return "PKCS7Padding";
- case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
- return "PKCS1Padding";
- case KeymasterDefs.KM_PAD_RSA_OAEP:
- return "OEAPPadding";
- default:
- throw new IllegalArgumentException(
- "Unsupported encryption padding: " + keymasterPadding);
- }
- }
-
- public static int getKeymasterPaddingFromJcaSignaturePadding(String jcaPadding) {
- if ("PKCS#1".equalsIgnoreCase(jcaPadding)) {
- return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN;
- } if ("PSS".equalsIgnoreCase(jcaPadding)) {
- return KeymasterDefs.KM_PAD_RSA_PSS;
- } else {
- throw new IllegalArgumentException(
- "Unsupported signature padding scheme: " + jcaPadding);
- }
- }
-
- public static String getJcaSignaturePaddingFromKeymasterPadding(int keymasterPadding) {
- switch (keymasterPadding) {
- case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN:
- return "PKCS#1";
- case KeymasterDefs.KM_PAD_RSA_PSS:
- return "PSS";
- default:
- throw new IllegalArgumentException(
- "Unsupported signature padding: " + keymasterPadding);
- }
- }
-
- public static int[] getKeymasterPaddingsFromJcaEncryptionPaddings(String[] jcaPaddings) {
- if ((jcaPaddings == null) || (jcaPaddings.length == 0)) {
- return EmptyArray.INT;
- }
- int[] result = new int[jcaPaddings.length];
- for (int i = 0; i < jcaPaddings.length; i++) {
- result[i] = getKeymasterPaddingFromJcaEncryptionPadding(jcaPaddings[i]);
- }
- return result;
- }
-
- public static int[] getKeymasterPaddingsFromJcaSignaturePaddings(String[] jcaPaddings) {
- if ((jcaPaddings == null) || (jcaPaddings.length == 0)) {
- return EmptyArray.INT;
- }
- int[] result = new int[jcaPaddings.length];
- for (int i = 0; i < jcaPaddings.length; i++) {
- result[i] = getKeymasterPaddingFromJcaSignaturePadding(jcaPaddings[i]);
- }
- return result;
- }
-
/**
* Adds keymaster arguments to express the key's authorization policy supported by user
* authentication.
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 19a5beb..2ae7b08 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -3336,6 +3336,30 @@
return NO_ERROR;
}
+status_t ResTable::Theme::clear()
+{
+ if (kDebugTableTheme) {
+ ALOGI("Clearing theme %p...\n", this);
+ dumpToLog();
+ }
+
+ for (size_t i = 0; i < Res_MAXPACKAGE; i++) {
+ if (mPackages[i] != NULL) {
+ free_package(mPackages[i]);
+ mPackages[i] = NULL;
+ }
+ }
+
+ mTypeSpecFlags = 0;
+
+ if (kDebugTableTheme) {
+ ALOGI("Final theme:");
+ dumpToLog();
+ }
+
+ return NO_ERROR;
+}
+
ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue,
uint32_t* outTypeSpecFlags) const
{
diff --git a/libs/hwui/AssetAtlas.cpp b/libs/hwui/AssetAtlas.cpp
index 882826e..2889d2f 100644
--- a/libs/hwui/AssetAtlas.cpp
+++ b/libs/hwui/AssetAtlas.cpp
@@ -112,9 +112,6 @@
Texture* const mDelegate;
}; // struct DelegateTexture
-/**
- * TODO: This method does not take the rotation flag into account
- */
void AssetAtlas::createEntries(Caches& caches, int64_t* map, int count) {
const float width = float(mTexture->width);
const float height = float(mTexture->height);
@@ -128,7 +125,6 @@
// pointers on 64 bit architectures.
const int x = static_cast<int>(map[i++]);
const int y = static_cast<int>(map[i++]);
- bool rotated = map[i++] > 0;
// Bitmaps should never be null, we're just extra paranoid
if (!pixelRef) continue;
@@ -142,7 +138,7 @@
texture->width = pixelRef->info().width();
texture->height = pixelRef->info().height();
- Entry* entry = new Entry(pixelRef, x, y, rotated, texture, mapper, *this);
+ Entry* entry = new Entry(pixelRef, texture, mapper, *this);
texture->uvMapper = &entry->uvMapper;
mEntries.add(entry->pixelRef, entry);
diff --git a/libs/hwui/AssetAtlas.h b/libs/hwui/AssetAtlas.h
index 17c5281..f1cd0b4 100644
--- a/libs/hwui/AssetAtlas.h
+++ b/libs/hwui/AssetAtlas.h
@@ -45,8 +45,8 @@
class AssetAtlas {
public:
/**
- * Entry representing the position and rotation of a
- * bitmap inside the atlas.
+ * Entry representing the texture and uvMapper of a PixelRef in the
+ * atlas
*/
class Entry {
public:
@@ -78,30 +78,15 @@
SkPixelRef* pixelRef;
/**
- * Location of the bitmap inside the atlas, in pixels.
- */
- int x;
- int y;
-
- /**
- * If set, the bitmap is rotated 90 degrees (clockwise)
- * inside the atlas.
- */
- bool rotated;
-
- /**
* Atlas this entry belongs to.
*/
const AssetAtlas& atlas;
- Entry(SkPixelRef* pixelRef, int x, int y, bool rotated,
- Texture* texture, const UvMapper& mapper, const AssetAtlas& atlas)
+ Entry(SkPixelRef* pixelRef, Texture* texture, const UvMapper& mapper,
+ const AssetAtlas& atlas)
: texture(texture)
, uvMapper(mapper)
, pixelRef(pixelRef)
- , x(x)
- , y(y)
- , rotated(rotated)
, atlas(atlas) {
}
@@ -120,8 +105,7 @@
* Initializes the atlas with the specified buffer and
* map. The buffer is a gralloc'd texture that will be
* used as an EGLImage. The map is a list of SkBitmap*
- * and their (x, y) positions as well as their rotation
- * flags.
+ * and their (x, y) positions
*
* This method returns immediately if the atlas is already
* initialized. To re-initialize the atlas, you must
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 4540bec..e679bff 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -41,10 +41,6 @@
ResourceCache& resourceCache = ResourceCache::getInstance();
resourceCache.lock();
- for (size_t i = 0; i < bitmapResources.size(); i++) {
- resourceCache.decrementRefcountLocked(bitmapResources.itemAt(i));
- }
-
for (size_t i = 0; i < patchResources.size(); i++) {
resourceCache.decrementRefcountLocked(patchResources.itemAt(i));
}
@@ -59,7 +55,6 @@
delete path;
}
- bitmapResources.clear();
patchResources.clear();
pathResources.clear();
paints.clear();
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
index 0064236..d997ef4 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -350,9 +350,10 @@
// correctly, such as creating the bitmap from scratch, drawing with it, changing its
// contents, and drawing again. The only fix would be to always copy it the first time,
// which doesn't seem worth the extra cycles for this unlikely case.
- const SkBitmap* cachedBitmap = mResourceCache.insert(bitmap);
- mDisplayListData->bitmapResources.add(cachedBitmap);
- return cachedBitmap;
+ SkBitmap* localBitmap = new (alloc()) SkBitmap(bitmap);
+ alloc().autoDestroy(localBitmap);
+ mDisplayListData->bitmapResources.push_back(localBitmap);
+ return localBitmap;
}
inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) {
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index fd32b6f..723a177 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -28,6 +28,8 @@
bool Properties::debugLayersUpdates = false;
bool Properties::debugOverdraw = false;
bool Properties::showDirtyRegions = false;
+bool Properties::skipEmptyFrames = true;
+bool Properties::swapBuffersWithDamage = false;
DebugLevel Properties::debugLevel = kDebugDisabled;
OverdrawColorSet Properties::overdrawColorSet = OverdrawColorSet::Default;
@@ -101,6 +103,9 @@
debugLevel = (DebugLevel) atoi(property);
}
+ skipEmptyFrames = property_get_bool(PROPERTY_SKIP_EMPTY_DAMAGE, true);
+ swapBuffersWithDamage = property_get_bool(PROPERTY_SWAP_WITH_DAMAGE, false);
+
return (prevDebugLayersUpdates != debugLayersUpdates)
|| (prevDebugOverdraw != debugOverdraw)
|| (prevDebugStencilClip != debugStencilClip);
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 46fa940..cb5560f 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -158,6 +158,21 @@
*/
#define PROPERTY_DISABLE_DRAW_REORDER "debug.hwui.disable_draw_reorder"
+/**
+ * Setting this property will enable or disable the dropping of frames with
+ * empty damage. Default is "true".
+ */
+#define PROPERTY_SKIP_EMPTY_DAMAGE "debug.hwui.skip_empty_damage"
+
+/**
+ * Setting this property will enable usage of EGL_KHR_swap_buffers_with_damage
+ * See: https://www.khronos.org/registry/egl/extensions/KHR/EGL_KHR_swap_buffers_with_damage.txt
+ * Default is "false" temporarily
+ * TODO: Change to "true", make sure to remove the log in EglManager::swapBuffers
+ * before changing this to default to true!
+ */
+#define PROPERTY_SWAP_WITH_DAMAGE "debug.hwui.swap_with_damage"
+
///////////////////////////////////////////////////////////////////////////////
// Runtime configuration properties
///////////////////////////////////////////////////////////////////////////////
@@ -288,6 +303,10 @@
static bool debugLayersUpdates;
static bool debugOverdraw;
static bool showDirtyRegions;
+ // TODO: Remove after stabilization period
+ static bool skipEmptyFrames;
+ // TODO: Remove after stabilization period
+ static bool swapBuffersWithDamage;
static DebugLevel debugLevel;
static OverdrawColorSet overdrawColorSet;
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
index 454fedc..75d8134 100644
--- a/libs/hwui/ResourceCache.cpp
+++ b/libs/hwui/ResourceCache.cpp
@@ -59,21 +59,6 @@
mLock.unlock();
}
-const SkBitmap* ResourceCache::insert(const SkBitmap& bitmapResource) {
- Mutex::Autolock _l(mLock);
-
- BitmapKey bitmapKey(bitmapResource);
- ssize_t index = mBitmapCache.indexOfKey(bitmapKey);
- if (index == NAME_NOT_FOUND) {
- SkBitmap* cachedBitmap = new SkBitmap(bitmapResource);
- index = mBitmapCache.add(bitmapKey, cachedBitmap);
- return cachedBitmap;
- }
-
- mBitmapCache.keyAt(index).mRefCount++;
- return mBitmapCache.valueAt(index);
-}
-
void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) {
Mutex::Autolock _l(mLock);
incrementRefcountLocked(resource, resourceType);
@@ -98,11 +83,6 @@
decrementRefcountLocked(resource);
}
-void ResourceCache::decrementRefcount(const SkBitmap* bitmapResource) {
- Mutex::Autolock _l(mLock);
- decrementRefcountLocked(bitmapResource);
-}
-
void ResourceCache::decrementRefcount(const Res_png_9patch* patchResource) {
decrementRefcount((void*) patchResource);
}
@@ -120,23 +100,6 @@
}
}
-void ResourceCache::decrementRefcountLocked(const SkBitmap* bitmapResource) {
- BitmapKey bitmapKey(*bitmapResource);
- ssize_t index = mBitmapCache.indexOfKey(bitmapKey);
-
- LOG_ALWAYS_FATAL_IF(index == NAME_NOT_FOUND,
- "Decrementing the reference of an untracked Bitmap");
-
- const BitmapKey& cacheEntry = mBitmapCache.keyAt(index);
- if (cacheEntry.mRefCount == 1) {
- // delete the bitmap and remove it from the cache
- delete mBitmapCache.valueAt(index);
- mBitmapCache.removeItemsAt(index);
- } else {
- cacheEntry.mRefCount--;
- }
-}
-
void ResourceCache::decrementRefcountLocked(const Res_png_9patch* patchResource) {
decrementRefcountLocked((void*) patchResource);
}
@@ -190,38 +153,5 @@
delete ref;
}
-///////////////////////////////////////////////////////////////////////////////
-// Bitmap Key
-///////////////////////////////////////////////////////////////////////////////
-
-void BitmapKey::operator=(const BitmapKey& other) {
- this->mRefCount = other.mRefCount;
- this->mBitmapDimensions = other.mBitmapDimensions;
- this->mPixelRefOrigin = other.mPixelRefOrigin;
- this->mPixelRefStableID = other.mPixelRefStableID;
-}
-
-bool BitmapKey::operator==(const BitmapKey& other) const {
- return mPixelRefStableID == other.mPixelRefStableID &&
- mPixelRefOrigin == other.mPixelRefOrigin &&
- mBitmapDimensions == other.mBitmapDimensions;
-}
-
-bool BitmapKey::operator<(const BitmapKey& other) const {
- if (mPixelRefStableID != other.mPixelRefStableID) {
- return mPixelRefStableID < other.mPixelRefStableID;
- }
- if (mPixelRefOrigin.x() != other.mPixelRefOrigin.x()) {
- return mPixelRefOrigin.x() < other.mPixelRefOrigin.x();
- }
- if (mPixelRefOrigin.y() != other.mPixelRefOrigin.y()) {
- return mPixelRefOrigin.y() < other.mPixelRefOrigin.y();
- }
- if (mBitmapDimensions.width() != other.mBitmapDimensions.width()) {
- return mBitmapDimensions.width() < other.mBitmapDimensions.width();
- }
- return mBitmapDimensions.height() < other.mBitmapDimensions.height();
-}
-
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h
index 6c483fa..4583c8d 100644
--- a/libs/hwui/ResourceCache.h
+++ b/libs/hwui/ResourceCache.h
@@ -51,37 +51,6 @@
ResourceType resourceType;
};
-class BitmapKey {
-public:
- BitmapKey(const SkBitmap& bitmap)
- : mRefCount(1)
- , mBitmapDimensions(bitmap.dimensions())
- , mPixelRefOrigin(bitmap.pixelRefOrigin())
- , mPixelRefStableID(bitmap.pixelRef()->getStableID()) { }
-
- void operator=(const BitmapKey& other);
- bool operator==(const BitmapKey& other) const;
- bool operator<(const BitmapKey& other) const;
-
-private:
- // This constructor is only used by the KeyedVector implementation
- BitmapKey()
- : mRefCount(-1)
- , mBitmapDimensions(SkISize::Make(0,0))
- , mPixelRefOrigin(SkIPoint::Make(0,0))
- , mPixelRefStableID(0) { }
-
- // reference count of all HWUI object using this bitmap
- mutable int mRefCount;
-
- SkISize mBitmapDimensions;
- SkIPoint mPixelRefOrigin;
- uint32_t mPixelRefStableID;
-
- friend class ResourceCache;
- friend struct android::key_value_pair_t<BitmapKey, SkBitmap*>;
-};
-
class ANDROID_API ResourceCache: public Singleton<ResourceCache> {
ResourceCache();
~ResourceCache();
@@ -97,18 +66,10 @@
void lock();
void unlock();
- /**
- * The cache stores a copy of the provided resource or refs an existing resource
- * if the bitmap has previously been inserted and returns the cached copy.
- */
- const SkBitmap* insert(const SkBitmap& resource);
-
void incrementRefcount(const Res_png_9patch* resource);
- void decrementRefcount(const SkBitmap* resource);
void decrementRefcount(const Res_png_9patch* resource);
- void decrementRefcountLocked(const SkBitmap* resource);
void decrementRefcountLocked(const Res_png_9patch* resource);
void destructor(Res_png_9patch* resource);
@@ -134,7 +95,6 @@
mutable Mutex mLock;
KeyedVector<const void*, ResourceReference*>* mCache;
- KeyedVector<BitmapKey, SkBitmap*> mBitmapCache;
};
}; // namespace uirenderer
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 3de3086..436946f 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -16,36 +16,25 @@
#include "CanvasContext.h"
+#include "AnimationContext.h"
+#include "Caches.h"
+#include "DeferredLayerUpdater.h"
#include "EglManager.h"
+#include "LayerRenderer.h"
+#include "OpenGLRenderer.h"
+#include "Properties.h"
#include "RenderThread.h"
-#include "../AnimationContext.h"
-#include "../Caches.h"
-#include "../DeferredLayerUpdater.h"
-#include "../renderstate/RenderState.h"
-#include "../renderstate/Stencil.h"
-#include "../LayerRenderer.h"
-#include "../OpenGLRenderer.h"
+#include "renderstate/RenderState.h"
+#include "renderstate/Stencil.h"
#include <algorithm>
+#include <strings.h>
#include <cutils/properties.h>
#include <private/hwui/DrawGlInfo.h>
-#include <strings.h>
#define TRIM_MEMORY_COMPLETE 80
#define TRIM_MEMORY_UI_HIDDEN 20
-#define PROPERTY_SKIP_EMPTY_DAMAGE "debug.hwui.skip_empty_damage"
-
-static bool sInitialized = false;
-static bool sSkipEmptyDamage = true;
-
-static void initGlobals() {
- if (sInitialized) return;
- sInitialized = true;
- sSkipEmptyDamage = property_get_bool(PROPERTY_SKIP_EMPTY_DAMAGE,
- sSkipEmptyDamage);
-}
-
namespace android {
namespace uirenderer {
namespace renderthread {
@@ -58,9 +47,6 @@
, mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
, mRootRenderNode(rootRenderNode)
, mJankTracker(thread.timeLord().frameIntervalNanos()) {
- // Done lazily at first draw instead of at library load to avoid
- // running pre-zygote fork
- initGlobals();
mRenderThread.renderState().registerCanvasContext(this);
mProfiler.setDensity(mRenderThread.mainDisplayInfo().density);
}
@@ -106,8 +92,8 @@
}
}
-void CanvasContext::swapBuffers() {
- if (CC_UNLIKELY(!mEglManager.swapBuffers(mEglSurface))) {
+void CanvasContext::swapBuffers(const SkRect& dirty, EGLint width, EGLint height) {
+ if (CC_UNLIKELY(!mEglManager.swapBuffers(mEglSurface, dirty, width, height))) {
setSurface(nullptr);
}
mHaveNewSurface = false;
@@ -222,7 +208,7 @@
SkRect dirty;
mDamageAccumulator.finish(&dirty);
- if (dirty.isEmpty() && sSkipEmptyDamage) {
+ if (dirty.isEmpty() && Properties::skipEmptyFrames) {
mCurrentFrameInfo->addFlag(FrameInfoFlags::kSkippedFrame);
return;
}
@@ -267,7 +253,7 @@
mCurrentFrameInfo->markSwapBuffers();
if (drew) {
- swapBuffers();
+ swapBuffers(dirty, width, height);
} else {
mEglManager.cancelFrame();
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index f5f1f54..8163b0f 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -29,6 +29,7 @@
#include <cutils/compiler.h>
#include <EGL/egl.h>
#include <SkBitmap.h>
+#include <SkRect.h>
#include <utils/Functor.h>
#include <utils/Vector.h>
@@ -117,7 +118,7 @@
friend class android::uirenderer::RenderState;
void setSurface(ANativeWindow* window);
- void swapBuffers();
+ void swapBuffers(const SkRect& dirty, EGLint width, EGLint height);
void requireSurface();
void requireGlContext();
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 3afca2f..6255f5e 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -16,9 +16,10 @@
#include "EglManager.h"
-#include "../Caches.h"
-#include "../renderstate/RenderState.h"
+#include "Caches.h"
+#include "Properties.h"
#include "RenderThread.h"
+#include "renderstate/RenderState.h"
#include <cutils/log.h>
#include <cutils/properties.h>
@@ -261,7 +262,8 @@
mInFrame = true;
}
-bool EglManager::swapBuffers(EGLSurface surface) {
+bool EglManager::swapBuffers(EGLSurface surface, const SkRect& dirty,
+ EGLint width, EGLint height) {
mInFrame = false;
#if WAIT_FOR_GPU_COMPLETION
@@ -271,7 +273,37 @@
}
#endif
+#ifdef EGL_KHR_swap_buffers_with_damage
+ if (CC_UNLIKELY(Properties::swapBuffersWithDamage)) {
+ SkIRect idirty;
+ dirty.roundOut(&idirty);
+ /*
+ * EGL_KHR_swap_buffers_with_damage spec states:
+ *
+ * The rectangles are specified relative to the bottom-left of the surface
+ * and the x and y components of each rectangle specify the bottom-left
+ * position of that rectangle.
+ *
+ * HWUI does everything with 0,0 being top-left, so need to map
+ * the rect
+ */
+ EGLint y = height - (idirty.y() + idirty.height());
+ // layout: {x, y, width, height}
+ EGLint rects[4] = { idirty.x(), y, idirty.width(), idirty.height() };
+ EGLint numrects = dirty.isEmpty() ? 0 : 1;
+ // TODO: Remove prior to enabling this path by default
+ ALOGD("Swap buffers with damage %d: %d, %d, %d, %d (src="
+ RECT_STRING ")",
+ dirty.isEmpty() ? 0 : 1, rects[0], rects[1], rects[2], rects[3],
+ SK_RECT_ARGS(dirty));
+ eglSwapBuffersWithDamageKHR(mEglDisplay, surface, rects, numrects);
+ } else {
+ eglSwapBuffers(mEglDisplay, surface);
+ }
+#else
eglSwapBuffers(mEglDisplay, surface);
+#endif
+
EGLint err = eglGetError();
if (CC_LIKELY(err == EGL_SUCCESS)) {
return true;
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index b1a18a9..0855516 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -18,6 +18,7 @@
#include <cutils/compiler.h>
#include <EGL/egl.h>
+#include <SkRect.h>
#include <ui/GraphicBuffer.h>
#include <utils/StrongPointer.h>
@@ -47,7 +48,7 @@
// Returns true if the current surface changed, false if it was already current
bool makeCurrent(EGLSurface surface);
void beginFrame(EGLSurface surface, EGLint* width, EGLint* height);
- bool swapBuffers(EGLSurface surface);
+ bool swapBuffers(EGLSurface surface, const SkRect& dirty, EGLint width, EGLint height);
void cancelFrame();
// Returns true iff the surface is now preserving buffers.
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index c643e1d..17e47b9 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -353,7 +353,6 @@
}
void RenderProxy::overrideProperty(const char* name, const char* value) {
- RenderThread& thread = RenderThread::getInstance();
SETUP_TASK(overrideProperty);
args->name = name;
args->value = value;
diff --git a/libs/hwui/tests/main.cpp b/libs/hwui/tests/main.cpp
index 9468d96..2f79c58 100644
--- a/libs/hwui/tests/main.cpp
+++ b/libs/hwui/tests/main.cpp
@@ -58,13 +58,20 @@
class TreeContentAnimation {
public:
virtual ~TreeContentAnimation() {}
- virtual int getFrameCount() { return 150; }
+ int frameCount = 150;
+ virtual int getFrameCount() { return frameCount; }
+ virtual void setFrameCount(int fc) {
+ if (fc > 0) {
+ frameCount = fc;
+ }
+ }
virtual void createContent(int width, int height, DisplayListCanvas* renderer) = 0;
virtual void doFrame(int frameNr) = 0;
template <class T>
- static void run() {
+ static void run(int frameCount) {
T animation;
+ animation.setFrameCount(frameCount);
TestContext testContext;
@@ -137,9 +144,10 @@
renderer->insertReorderBarrier(false);
}
void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
for (size_t ci = 0; ci < cards.size(); ci++) {
- cards[ci]->mutateStagingProperties().setTranslationX(frameNr);
- cards[ci]->mutateStagingProperties().setTranslationY(frameNr);
+ cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
+ cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
}
}
@@ -159,6 +167,47 @@
}
};
+class ShadowGrid2Animation : public TreeContentAnimation {
+public:
+ std::vector< sp<RenderNode> > cards;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
+
+ for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
+ for (int y = dp(8); y < (height - dp(58)); y += dp(58)) {
+ sp<RenderNode> card = createCard(x, y, dp(50), dp(50));
+ renderer->drawRenderNode(card.get());
+ cards.push_back(card);
+ }
+ }
+
+ renderer->insertReorderBarrier(false);
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ for (size_t ci = 0; ci < cards.size(); ci++) {
+ cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
+ cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
+ cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->mutateStagingProperties().setElevation(dp(16));
+ node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
+ node->mutateStagingProperties().mutableOutline().setShouldClip(true);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+ renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+
class RectGridAnimation : public TreeContentAnimation {
public:
sp<RenderNode> card;
@@ -172,8 +221,9 @@
renderer->insertReorderBarrier(false);
}
void doFrame(int frameNr) override {
- card->mutateStagingProperties().setTranslationX(frameNr);
- card->mutateStagingProperties().setTranslationY(frameNr);
+ int curFrame = frameNr % 150;
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->mutateStagingProperties().setTranslationY(curFrame);
card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
}
private:
@@ -220,8 +270,9 @@
}
void doFrame(int frameNr) override {
- card->mutateStagingProperties().setTranslationX(frameNr);
- card->mutateStagingProperties().setTranslationY(frameNr);
+ int curFrame = frameNr % 150;
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->mutateStagingProperties().setTranslationY(curFrame);
card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
}
private:
@@ -248,10 +299,11 @@
}
};
-typedef void (*testProc)();
+typedef void (*testProc)(int);
std::map<const char*, testProc, cstr_cmp> gTestMap {
{"shadowgrid", TreeContentAnimation::run<ShadowGridAnimation>},
+ {"shadowgrid2", TreeContentAnimation::run<ShadowGrid2Animation>},
{"rectgrid", TreeContentAnimation::run<RectGridAnimation> },
{"oval", TreeContentAnimation::run<OvalAnimation> },
};
@@ -271,11 +323,19 @@
return 1;
}
}
+ int frameCount = 150;
+ if (argc > 3) {
+ frameCount = atoi(argv[3]);
+ if (frameCount < 1) {
+ printf("Invalid frame count!\n");
+ return 1;
+ }
+ }
if (loopCount < 0) {
loopCount = INT_MAX;
}
for (int i = 0; i < loopCount; i++) {
- proc();
+ proc(frameCount);
}
printf("Success!\n");
return 0;
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 6ec10c7..b3c1b4d 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2824,6 +2824,13 @@
mSubtitleController.selectDefaultTrack();
}
break;
+ case MEDIA_INFO_BUFFERING_START:
+ case MEDIA_INFO_BUFFERING_END:
+ TimeProvider timeProvider = mTimeProvider;
+ if (timeProvider != null) {
+ timeProvider.onBuffering(msg.arg1 == MEDIA_INFO_BUFFERING_START);
+ }
+ break;
}
if (mOnInfoListener != null) {
@@ -3362,6 +3369,7 @@
private MediaPlayer mPlayer;
private boolean mPaused = true;
private boolean mStopped = true;
+ private boolean mBuffering;
private long mLastReportedTime;
private long mTimeAdjustment;
// since we are expecting only a handful listeners per stream, there is
@@ -3455,12 +3463,22 @@
}
/** @hide */
+ public void onBuffering(boolean buffering) {
+ synchronized (this) {
+ if (DEBUG) Log.d(TAG, "onBuffering: " + buffering);
+ mBuffering = buffering;
+ scheduleNotification(REFRESH_AND_NOTIFY_TIME, 0 /* delay */);
+ }
+ }
+
+ /** @hide */
public void onStopped() {
synchronized(this) {
if (DEBUG) Log.d(TAG, "onStopped");
mPaused = true;
mStopped = true;
mSeeking = false;
+ mBuffering = false;
scheduleNotification(NOTIFY_STOP, 0 /* delay */);
}
}
@@ -3481,6 +3499,7 @@
synchronized(this) {
mStopped = false;
mSeeking = true;
+ mBuffering = false;
scheduleNotification(NOTIFY_SEEK, 0 /* delay */);
}
}
@@ -3683,7 +3702,7 @@
nanoTime >= mLastNanoTime + MAX_NS_WITHOUT_POSITION_CHECK) {
try {
mLastTimeUs = mPlayer.getCurrentPosition() * 1000L;
- mPaused = !mPlayer.isPlaying();
+ mPaused = !mPlayer.isPlaying() || mBuffering;
if (DEBUG) Log.v(TAG, (mPaused ? "paused" : "playing") + " at " + mLastTimeUs);
} catch (IllegalStateException e) {
if (mPausing) {
diff --git a/media/java/android/media/midi/MidiDeviceInfo.java b/media/java/android/media/midi/MidiDeviceInfo.java
index 35374ed..57607e9 100644
--- a/media/java/android/media/midi/MidiDeviceInfo.java
+++ b/media/java/android/media/midi/MidiDeviceInfo.java
@@ -298,6 +298,9 @@
@Override
public String toString() {
+ // This is a hack to force the mProperties Bundle to unparcel so we can
+ // print all the names and values.
+ mProperties.getString(PROPERTY_NAME);
return ("MidiDeviceInfo[mType=" + mType +
",mInputPortCount=" + mInputPortCount +
",mOutputPortCount=" + mOutputPortCount +
diff --git a/media/java/android/media/midi/MidiDeviceStatus.java b/media/java/android/media/midi/MidiDeviceStatus.java
index 7522dcf..d4abeff 100644
--- a/media/java/android/media/midi/MidiDeviceStatus.java
+++ b/media/java/android/media/midi/MidiDeviceStatus.java
@@ -89,10 +89,9 @@
@Override
public String toString() {
- StringBuilder builder = new StringBuilder(mDeviceInfo.toString());
int inputPortCount = mDeviceInfo.getInputPortCount();
int outputPortCount = mDeviceInfo.getOutputPortCount();
- builder.append(" mInputPortOpen=[");
+ StringBuilder builder = new StringBuilder("mInputPortOpen=[");
for (int i = 0; i < inputPortCount; i++) {
builder.append(mInputPortOpen[i]);
if (i < inputPortCount -1) {
diff --git a/media/java/android/media/routing/IMediaRouteClientCallback.aidl b/media/java/android/media/routing/IMediaRouteClientCallback.aidl
deleted file mode 100644
index d90ea3b..0000000
--- a/media/java/android/media/routing/IMediaRouteClientCallback.aidl
+++ /dev/null
@@ -1,41 +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.media.routing;
-
-import android.media.routing.MediaRouteSelector;
-import android.media.routing.ParcelableConnectionInfo;
-import android.media.routing.ParcelableDestinationInfo;
-import android.media.routing.ParcelableRouteInfo;
-import android.os.IBinder;
-import android.os.Bundle;
-
-/**
- * @hide
- */
-oneway interface IMediaRouteClientCallback {
- void onDestinationFound(int seq, in ParcelableDestinationInfo destination,
- in ParcelableRouteInfo[] routes);
-
- void onDestinationLost(int seq, String id);
-
- void onDiscoveryFailed(int seq, int error, in CharSequence message, in Bundle extras);
-
- void onConnected(int seq, in ParcelableConnectionInfo connection);
-
- void onDisconnected(int seq);
-
- void onConnectionFailed(int seq, int error, in CharSequence message, in Bundle extras);
-}
diff --git a/media/java/android/media/routing/IMediaRouteService.aidl b/media/java/android/media/routing/IMediaRouteService.aidl
deleted file mode 100644
index 493ab6d..0000000
--- a/media/java/android/media/routing/IMediaRouteService.aidl
+++ /dev/null
@@ -1,46 +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.media.routing;
-
-import android.media.routing.IMediaRouteClientCallback;
-import android.media.routing.MediaRouteSelector;
-import android.os.Bundle;
-
-/**
- * Interface to an app's MediaRouteService.
- * @hide
- */
-oneway interface IMediaRouteService {
- void registerClient(int clientUid, String clientPackageName,
- in IMediaRouteClientCallback callback);
-
- void unregisterClient(in IMediaRouteClientCallback callback);
-
- void startDiscovery(in IMediaRouteClientCallback callback, int seq,
- in List<MediaRouteSelector> selectors, int flags);
-
- void stopDiscovery(in IMediaRouteClientCallback callback);
-
- void connect(in IMediaRouteClientCallback callback, int seq,
- String destinationId, String routeId, int flags, in Bundle extras);
-
- void disconnect(in IMediaRouteClientCallback callback);
-
- void pauseStream(in IMediaRouteClientCallback callback);
-
- void resumeStream(in IMediaRouteClientCallback callback);
-}
-
diff --git a/media/java/android/media/routing/IMediaRouter.aidl b/media/java/android/media/routing/IMediaRouter.aidl
deleted file mode 100644
index 0abb258..0000000
--- a/media/java/android/media/routing/IMediaRouter.aidl
+++ /dev/null
@@ -1,22 +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.media.routing;
-
-/** @hide */
-interface IMediaRouter {
-
-}
-
diff --git a/media/java/android/media/routing/IMediaRouterDelegate.aidl b/media/java/android/media/routing/IMediaRouterDelegate.aidl
deleted file mode 100644
index 35f84c8..0000000
--- a/media/java/android/media/routing/IMediaRouterDelegate.aidl
+++ /dev/null
@@ -1,22 +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.media.routing;
-
-/** @hide */
-interface IMediaRouterDelegate {
-
-}
-
diff --git a/media/java/android/media/routing/IMediaRouterRoutingCallback.aidl b/media/java/android/media/routing/IMediaRouterRoutingCallback.aidl
deleted file mode 100644
index 173ae55..0000000
--- a/media/java/android/media/routing/IMediaRouterRoutingCallback.aidl
+++ /dev/null
@@ -1,22 +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.media.routing;
-
-/** @hide */
-interface IMediaRouterRoutingCallback {
-
-}
-
diff --git a/media/java/android/media/routing/IMediaRouterStateCallback.aidl b/media/java/android/media/routing/IMediaRouterStateCallback.aidl
deleted file mode 100644
index 0299904..0000000
--- a/media/java/android/media/routing/IMediaRouterStateCallback.aidl
+++ /dev/null
@@ -1,22 +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.media.routing;
-
-/** @hide */
-interface IMediaRouterStateCallback {
-
-}
-
diff --git a/media/java/android/media/routing/MediaRouteSelector.aidl b/media/java/android/media/routing/MediaRouteSelector.aidl
deleted file mode 100644
index 37bfa4a..0000000
--- a/media/java/android/media/routing/MediaRouteSelector.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/* Copyright 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.media.routing;
-
-parcelable MediaRouteSelector;
diff --git a/media/java/android/media/routing/MediaRouteSelector.java b/media/java/android/media/routing/MediaRouteSelector.java
deleted file mode 100644
index 0bfc796..0000000
--- a/media/java/android/media/routing/MediaRouteSelector.java
+++ /dev/null
@@ -1,356 +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.media.routing;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.media.routing.MediaRouter.RouteFeatures;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A media route selector consists of a set of constraints that are used to select
- * the routes to which an application would like to connect. The constraints consist
- * of a set of required or optional features and protocols. The constraints may also
- * require the use of a specific media route service package or additional characteristics
- * that are described by a bundle of extra parameters.
- * <p>
- * The application will typically create several different selectors that express
- * various combinations of characteristics that it would like to use together when
- * it connects to a destination media device. For each destination that is discovered,
- * media route services will publish some number of routes and include information
- * about which selector each route matches. The application will then choose among
- * these routes to determine which best satisfies its desired purpose and connect to it.
- * </p>
- */
-public final class MediaRouteSelector implements Parcelable {
- private final int mRequiredFeatures;
- private final int mOptionalFeatures;
- private final List<String> mRequiredProtocols;
- private final List<String> mOptionalProtocols;
- private final String mServicePackageName;
- private final Bundle mExtras;
-
- MediaRouteSelector(int requiredFeatures, int optionalFeatures,
- List<String> requiredProtocols, List<String> optionalProtocols,
- String servicePackageName, Bundle extras) {
- mRequiredFeatures = requiredFeatures;
- mOptionalFeatures = optionalFeatures;
- mRequiredProtocols = requiredProtocols;
- mOptionalProtocols = optionalProtocols;
- mServicePackageName = servicePackageName;
- mExtras = extras;
- }
-
- /**
- * Gets the set of required route features.
- *
- * @return A set of required route feature flags.
- */
- public @RouteFeatures int getRequiredFeatures() {
- return mRequiredFeatures;
- }
-
- /**
- * Gets the set of optional route features.
- *
- * @return A set of optional route feature flags.
- */
- public @RouteFeatures int getOptionalFeatures() {
- return mOptionalFeatures;
- }
-
- /**
- * Gets the list of route protocols that a route must support in order to be selected.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- *
- * @return The list of fully qualified route protocol names.
- */
- public @NonNull List<String> getRequiredProtocols() {
- return mRequiredProtocols;
- }
-
- /**
- * Gets the list of optional route protocols that a client may use if they are available.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- *
- * @return The list of optional fully qualified route protocol names.
- */
- public @NonNull List<String> getOptionalProtocols() {
- return mOptionalProtocols;
- }
-
- /**
- * Returns true if the selector includes a required or optional request for
- * the specified protocol using its fully qualified class name.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- *
- * @param clazz The protocol class.
- * @return True if the protocol was requested.
- */
- public boolean containsProtocol(@NonNull Class<?> clazz) {
- return containsProtocol(clazz.getName());
- }
-
- /**
- * Returns true if the selector includes a required or optional request for
- * the specified protocol.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- *
- * @param name The name of the protocol.
- * @return True if the protocol was requested.
- */
- public boolean containsProtocol(@NonNull String name) {
- return mRequiredProtocols.contains(name)
- || mOptionalProtocols.contains(name);
- }
-
- /**
- * Gets the package name of a specific media route service that this route selector
- * requires.
- *
- * @return The required media route service package name, or null if none.
- */
- public @Nullable String getServicePackageName() {
- return mServicePackageName;
- }
-
- /**
- * Gets optional extras that may be used to select or configure routes for a
- * particular purpose. Some extras may be used by media route services to apply
- * additional constraints or parameters for the routes to be discovered.
- *
- * @return The optional extras, or null if none.
- */
- public @Nullable Bundle getExtras() {
- return mExtras;
- }
-
- @Override
- public String toString() {
- return "MediaRouteSelector{ "
- + ", requiredFeatures=0x" + Integer.toHexString(mRequiredFeatures)
- + ", optionalFeatures=0x" + Integer.toHexString(mOptionalFeatures)
- + ", requiredProtocols=" + mRequiredProtocols
- + ", optionalProtocols=" + mOptionalProtocols
- + ", servicePackageName=" + mServicePackageName
- + ", extras=" + mExtras + " }";
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mRequiredFeatures);
- dest.writeInt(mOptionalFeatures);
- dest.writeStringList(mRequiredProtocols);
- dest.writeStringList(mOptionalProtocols);
- dest.writeString(mServicePackageName);
- dest.writeBundle(mExtras);
- }
-
- public static final Parcelable.Creator<MediaRouteSelector> CREATOR =
- new Parcelable.Creator<MediaRouteSelector>() {
- @Override
- public MediaRouteSelector createFromParcel(Parcel source) {
- int requiredFeatures = source.readInt();
- int optionalFeatures = source.readInt();
- ArrayList<String> requiredProtocols = new ArrayList<String>();
- ArrayList<String> optionalProtocols = new ArrayList<String>();
- source.readStringList(requiredProtocols);
- source.readStringList(optionalProtocols);
- return new MediaRouteSelector(requiredFeatures, optionalFeatures,
- requiredProtocols, optionalProtocols,
- source.readString(), source.readBundle());
- }
-
- @Override
- public MediaRouteSelector[] newArray(int size) {
- return new MediaRouteSelector[size];
- }
- };
-
- /**
- * Builder for {@link MediaRouteSelector} objects.
- */
- public static final class Builder {
- private int mRequiredFeatures;
- private int mOptionalFeatures;
- private final ArrayList<String> mRequiredProtocols = new ArrayList<String>();
- private final ArrayList<String> mOptionalProtocols = new ArrayList<String>();
- private String mServicePackageName;
- private Bundle mExtras;
-
- /**
- * Creates an initially empty selector builder.
- */
- public Builder() {
- }
-
- /**
- * Sets the set of required route features.
- *
- * @param features A set of required route feature flags.
- */
- public @NonNull Builder setRequiredFeatures(@RouteFeatures int features) {
- mRequiredFeatures = features;
- return this;
- }
-
- /**
- * Sets the set of optional route features.
- *
- * @param features A set of optional route feature flags.
- */
- public @NonNull Builder setOptionalFeatures(@RouteFeatures int features) {
- mOptionalFeatures = features;
- return this;
- }
-
- /**
- * Adds a route protocol that a route must support in order to be selected
- * using its fully qualified class name.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- *
- * @param clazz The protocol class.
- * @return this
- */
- public @NonNull Builder addRequiredProtocol(@NonNull Class<?> clazz) {
- if (clazz == null) {
- throw new IllegalArgumentException("clazz must not be null");
- }
- return addRequiredProtocol(clazz.getName());
- }
-
- /**
- * Adds a route protocol that a route must support in order to be selected.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- *
- * @param name The fully qualified name of the required protocol.
- * @return this
- */
- public @NonNull Builder addRequiredProtocol(@NonNull String name) {
- if (TextUtils.isEmpty(name)) {
- throw new IllegalArgumentException("name must not be null or empty");
- }
- mRequiredProtocols.add(name);
- return this;
- }
-
- /**
- * Adds an optional route protocol that a client may use if available
- * using its fully qualified class name.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- *
- * @param clazz The protocol class.
- * @return this
- */
- public @NonNull Builder addOptionalProtocol(@NonNull Class<?> clazz) {
- if (clazz == null) {
- throw new IllegalArgumentException("clazz must not be null");
- }
- return addOptionalProtocol(clazz.getName());
- }
-
- /**
- * Adds an optional route protocol that a client may use if available.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- *
- * @param name The fully qualified name of the optional protocol.
- * @return this
- */
- public @NonNull Builder addOptionalProtocol(@NonNull String name) {
- if (TextUtils.isEmpty(name)) {
- throw new IllegalArgumentException("name must not be null or empty");
- }
- mOptionalProtocols.add(name);
- return this;
- }
-
- /**
- * Sets the package name of the media route service to which this selector
- * appertains.
- * <p>
- * If a package name is specified here then this selector will only be
- * passed to media route services from that package. This has the effect
- * of restricting the set of matching routes to just those that are offered
- * by that package.
- * </p>
- *
- * @param packageName The required service package name, or null if none.
- * @return this
- */
- public @NonNull Builder setServicePackageName(@Nullable String packageName) {
- mServicePackageName = packageName;
- return this;
- }
-
- /**
- * Sets optional extras that may be used to select or configure routes for a
- * particular purpose. Some extras may be used by route services to specify
- * additional constraints or parameters for the routes to be discovered.
- *
- * @param extras The optional extras, or null if none.
- * @return this
- */
- public @NonNull Builder setExtras(@Nullable Bundle extras) {
- mExtras = extras;
- return this;
- }
-
- /**
- * Builds the {@link MediaRouteSelector} object.
- *
- * @return The new media route selector instance.
- */
- public @NonNull MediaRouteSelector build() {
- return new MediaRouteSelector(mRequiredFeatures, mOptionalFeatures,
- mRequiredProtocols, mOptionalProtocols, mServicePackageName, mExtras);
- }
- }
-}
diff --git a/media/java/android/media/routing/MediaRouteService.java b/media/java/android/media/routing/MediaRouteService.java
deleted file mode 100644
index 4d5a8a9..0000000
--- a/media/java/android/media/routing/MediaRouteService.java
+++ /dev/null
@@ -1,1023 +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.media.routing;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SdkConstant;
-import android.app.Service;
-import android.content.Intent;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.media.routing.MediaRouter.ConnectionError;
-import android.media.routing.MediaRouter.ConnectionInfo;
-import android.media.routing.MediaRouter.ConnectionRequest;
-import android.media.routing.MediaRouter.DestinationInfo;
-import android.media.routing.MediaRouter.DiscoveryError;
-import android.media.routing.MediaRouter.DiscoveryRequest;
-import android.media.routing.MediaRouter.RouteInfo;
-import android.media.routing.MediaRouter.ServiceMetadata;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Media route services implement strategies for discovering
- * and establishing connections to media devices and their routes. These services
- * are also known as media route providers.
- * <p>
- * Each media route service subclass is responsible for enabling applications
- * and the system to interact with media devices of some kind.
- * For example, one particular media route service implementation might
- * offer support for discovering nearby wireless display devices and streaming
- * video contents to them; another media route service implementation might
- * offer support for discovering nearby speakers and streaming media appliances
- * and sending commands to play content on request.
- * </p><p>
- * Subclasses must override the {@link #onCreateClientSession} method to return
- * a {@link ClientSession} object that implements the {@link ClientSession#onStartDiscovery},
- * {@link ClientSession#onStopDiscovery}, and {@link ClientSession#onConnect} methods
- * to allow clients to discover and connect to media devices.
- * </p><p>
- * This object is not thread-safe. All callbacks are invoked on the main looper.
- * </p>
- *
- * <h3>Clients</h3>
- * <p>
- * The clients of this API are media applications that would like to discover
- * and connect to media devices. The client may also be the system, such as
- * when the user initiates display mirroring via the Cast Screen function.
- * </p><p>
- * There may be multiple client sessions active at the same time. Each client
- * session can request discovery and connect to routes independently of any
- * other client. It is the responsibility of the media route service to maintain
- * separate state for each client session and to ensure that clients cannot interfere
- * with one another in harmful ways.
- * </p><p>
- * Notwithstanding the requirement to support any number of concurrent client
- * sessions, the media route service may impose constraints on how many clients
- * can connect to the same media device in a particular mode at the same time.
- * In some cases, media devices may support connections from an arbitrary number
- * of clients simultaneously but often it may be necessary to ensure that only
- * one client is in control. When this happens, the media route service should
- * report a connection error unless the connection request specifies that the
- * client should take control of the media device (and forcibly disconnect other
- * clients that may be using it).
- * </p>
- *
- * <h3>Destinations</h3>
- * <p>
- * The media devices to which an application may send media content are referred
- * to in the API as destinations. Each destination therefore represents a single
- * independent device such as a speaker or TV set. Destinations are given meaningful
- * names and descriptions to help the user associate them with devices in their
- * environment.
- * </p><p>
- * Destinations may be local or remote and may be accessed through various means,
- * often wirelessly. The user may install media route services to enable
- * media applications to connect to a variety of destinations with different
- * capabilities.
- * </p>
- *
- * <h3>Routes</h3>
- * <p>
- * Routes represent possible usages or means of reaching and interacting with
- * a destination. Since destinations may support many different features, they may
- * each offer multiple routes for applications to choose from based on their needs.
- * For example, one route might express the ability to stream locally rendered audio
- * and video to the device; another route might express the ability to send a URL for
- * the destination to download from the network and play all by itself.
- * </p><p>
- * Routes are discovered according to the set of capabilities that
- * an application or the system is seeking to use at a particular time. For example,
- * if an application wants to stream music to a destination then it will ask the
- * {@link MediaRouter} to find routes to destinations can stream music and ignore
- * all other destinations that cannot.
- * </p><p>
- * In general, the application will inspect the set of routes that have been
- * offered then connect to the most appropriate route for its desired purpose.
- * </p>
- *
- * <h3>Discovery</h3>
- * <p>
- * Discovery is the process of finding destinations based on a description of the
- * kinds of routes that an application or the system would like to use.
- * </p><p>
- * Discovery begins when {@link ClientSession#onStartDiscovery} is called and ends when
- * {@link ClientSession#onStopDiscovery} is called. There may be multiple simultaneous
- * discovery requests in progress at the same time from different clients. It is up to
- * the media route service to perform these requests in parallel or multiplex them
- * as required.
- * </p><p>
- * Media route services are <em>strongly encouraged</em> to use the information
- * in the discovery request to optimize discovery and avoid redundant work.
- * In the case where no media device supported by the media route service
- * could possibly offer the requested capabilities, the
- * {@link ClientSession#onStartDiscovery} method should return <code>false</code> to
- * let the system know that it can unbind from the media route service and
- * release its resources.
- * </p>
- *
- * <h3>Settings</h3>
- * <p>
- * Many kinds of devices can be discovered on demand simply by scanning the local network
- * or using wireless protocols such as Bluetooth to find them. However, in some cases
- * it may be necessary for the user to manually configure destinations before they
- * can be used (or to adjust settings later). Actual user configuration of destinations
- * is beyond the scope of this API but media route services may specify an activity
- * in their manifest that the user can launch to perform these tasks.
- * </p><p>
- * Note that media route services that are installed from the store must be enabled
- * by the user before they become available for applications to use.
- * The {@link android.provider.Settings#ACTION_CAST_SETTINGS Settings.ACTION_CAST_SETTINGS}
- * settings activity provides the ability for the user to configure media route services.
- * </p>
- *
- * <h3>Manifest Declaration</h3>
- * <p>
- * Media route services must be declared in the manifest along with meta-data
- * about the kinds of routes that they are capable of discovering. The system
- * uses this information to optimize the set of services to which it binds in
- * order to satisfy a particular discovery request.
- * </p><p>
- * To extend this class, you must declare the service in your manifest file with
- * the {@link android.Manifest.permission#BIND_MEDIA_ROUTE_SERVICE} permission
- * and include an intent filter with the {@link #SERVICE_INTERFACE} action. You must
- * also add meta-data to describe the kinds of routes that your service is capable
- * of discovering.
- * </p><p>
- * For example:
- * </p><pre>
- * <service android:name=".MediaRouteProvider"
- * android:label="@string/service_name"
- * android:permission="android.permission.BIND_MEDIA_ROUTE_SERVICE">
- * <intent-filter>
- * <action android:name="android.media.routing.MediaRouteService" />
- * </intent-filter>
- *
- * TODO: INSERT METADATA DECLARATIONS HERE
- *
- * </service>
- * </pre>
- */
-public abstract class MediaRouteService extends Service {
- private static final String TAG = "MediaRouteService";
-
- private static final boolean DEBUG = true;
-
- private final Handler mHandler;
- private final BinderService mService;
- private final ArrayMap<IBinder, ClientRecord> mClientRecords =
- new ArrayMap<IBinder, ClientRecord>();
-
- private ServiceMetadata mMetadata;
-
- /**
- * The {@link Intent} that must be declared as handled by the service.
- */
- @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
- public static final String SERVICE_INTERFACE =
- "android.media.routing.MediaRouteService";
-
- /**
- * Creates a media route service.
- */
- public MediaRouteService() {
- mHandler = new Handler(true);
- mService = new BinderService();
- }
-
- @Override
- public @Nullable IBinder onBind(Intent intent) {
- if (SERVICE_INTERFACE.equals(intent.getAction())) {
- return mService;
- }
- return null;
- }
-
- /**
- * Creates a new client session on behalf of a client.
- * <p>
- * The implementation should return a {@link ClientSession} for the client
- * to use. The media route service must take care to manage the state of
- * each client session independently from any others that might also be
- * in use at the same time.
- * </p>
- *
- * @param client Information about the client.
- * @return The client session object, or null if the client is not allowed
- * to interact with this media route service.
- */
- public abstract @Nullable ClientSession onCreateClientSession(@NonNull ClientInfo client);
-
- /**
- * Gets metadata about this service.
- * <p>
- * Use this method to obtain a {@link ServiceMetadata} object to provide when creating
- * a {@link android.media.routing.MediaRouter.DestinationInfo.Builder}.
- * </p>
- *
- * @return Metadata about this service.
- */
- public @NonNull ServiceMetadata getServiceMetadata() {
- if (mMetadata == null) {
- try {
- mMetadata = new ServiceMetadata(this);
- } catch (NameNotFoundException ex) {
- Log.wtf(TAG, "Could not retrieve own service metadata!");
- }
- }
- return mMetadata;
- }
-
- /**
- * Enables a single client to access the functionality of the media route service.
- */
- public static abstract class ClientSession {
- /**
- * Starts discovery.
- * <p>
- * If the media route service is capable of discovering routes that satisfy
- * the request then this method should start discovery and return true.
- * Otherwise, this method should return false. If false is returned,
- * then the framework will not call {@link #onStopDiscovery} since discovery
- * was never actually started.
- * </p><p>
- * There may already be other discovery requests in progress at the same time
- * for other clients; the media route service must keep track of them all.
- * </p>
- *
- * @param req The discovery request to start.
- * @param callback A callback to receive discovery events related to this
- * particular request. The events that the service sends to this callback
- * will be sent to the client that initiated the discovery request.
- * @return True if discovery has started. False if the media route service
- * is unable to discover routes that satisfy the request.
- */
- public abstract boolean onStartDiscovery(@NonNull DiscoveryRequest req,
- @NonNull DiscoveryCallback callback);
-
- /**
- * Stops discovery.
- * <p>
- * If {@link #onStartDiscovery} returned true, then this method will eventually
- * be called when the framework no longer requires this discovery request
- * to be performed.
- * </p><p>
- * There may still be other discovery requests in progress for other clients;
- * they must keep working until they have each been stopped by their client.
- * </p>
- */
- public abstract void onStopDiscovery();
-
- /**
- * Starts connecting to a route.
- *
- * @param req The connection request.
- * @param callback A callback to receive events connection events related
- * to this particular request. The events that the service sends to this callback
- * will be sent to the client that initiated the discovery request.
- * @return True if the connection is in progress, or false if the client
- * unable to connect to the requested route.
- */
- public abstract boolean onConnect(@NonNull ConnectionRequest req,
- @NonNull ConnectionCallback callback);
-
- /**
- * Called when the client requests to disconnect from the route
- * or abort a connection attempt in progress.
- */
- public abstract void onDisconnect();
-
- /**
- * Called when the client requests to pause streaming of content to
- * live audio/video routes such as when it goes into the background.
- * <p>
- * The default implementation does nothing.
- * </p>
- */
- public void onPauseStream() { }
-
- /**
- * Called when the application requests to resume streaming of content to
- * live audio/video routes such as when it returns to the foreground.
- * <p>
- * The default implementation does nothing.
- * </p>
- */
- public void onResumeStream() { }
-
- /**
- * Called when the client is releasing the session.
- * <p>
- * The framework automatically takes care of stopping discovery and
- * terminating the connection politely before calling this method to release
- * the session.
- * </p><p>
- * The default implementation does nothing.
- * </p>
- */
- public void onRelease() { }
- }
-
- /**
- * Provides events in response to a discovery request.
- */
- public final class DiscoveryCallback {
- private final ClientRecord mRecord;
-
- DiscoveryCallback(ClientRecord record) {
- mRecord = record;
- }
-
- /**
- * Called by the service when a destination is found that
- * offers one or more routes that satisfy the discovery request.
- * <p>
- * This method should be called whenever the list of available routes
- * at a destination changes or whenever the properties of the destination
- * itself change.
- * </p>
- *
- * @param destination The destination that was found.
- * @param routes The list of that destination's routes that satisfy the
- * discovery request.
- */
- public void onDestinationFound(final @NonNull DestinationInfo destination,
- final @NonNull List<RouteInfo> routes) {
- if (destination == null) {
- throw new IllegalArgumentException("destination must not be null");
- }
- if (routes == null) {
- throw new IllegalArgumentException("routes must not be null");
- }
- for (int i = 0; i < routes.size(); i++) {
- if (routes.get(i).getDestination() != destination) {
- throw new IllegalArgumentException("routes must refer to the "
- + "destination");
- }
- }
-
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mRecord.dispatchDestinationFound(DiscoveryCallback.this,
- destination, routes);
- }
- });
- }
-
- /**
- * Called by the service when a destination is no longer
- * reachable or is no longer offering any routes that satisfy
- * the discovery request.
- *
- * @param destination The destination that went away.
- */
- public void onDestinationLost(final @NonNull DestinationInfo destination) {
- if (destination == null) {
- throw new IllegalArgumentException("destination must not be null");
- }
-
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mRecord.dispatchDestinationLost(DiscoveryCallback.this, destination);
- }
- });
- }
-
- /**
- * Called by the service when a discovery has failed in a non-recoverable manner.
- *
- * @param error The error code: one of
- * {@link MediaRouter#DISCOVERY_ERROR_UNKNOWN},
- * {@link MediaRouter#DISCOVERY_ERROR_ABORTED},
- * or {@link MediaRouter#DISCOVERY_ERROR_NO_CONNECTIVITY}.
- * @param message The localized error message, or null if none. This message
- * may be shown to the user.
- * @param extras Additional information about the error which a client
- * may use, or null if none.
- */
- public void onDiscoveryFailed(final @DiscoveryError int error,
- final @Nullable CharSequence message, final @Nullable Bundle extras) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mRecord.dispatchDiscoveryFailed(DiscoveryCallback.this,
- error, message, extras);
- }
- });
- }
- }
-
- /**
- * Provides events in response to a connection request.
- */
- public final class ConnectionCallback {
- private final ClientRecord mRecord;
-
- ConnectionCallback(ClientRecord record) {
- mRecord = record;
- }
-
- /**
- * Called by the service when the connection succeeds.
- *
- * @param connection Immutable information about the connection.
- */
- public void onConnected(final @NonNull ConnectionInfo connection) {
- if (connection == null) {
- throw new IllegalArgumentException("connection must not be null");
- }
-
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mRecord.dispatchConnected(ConnectionCallback.this, connection);
- }
- });
- }
-
- /**
- * Called by the service when the connection is terminated normally.
- * <p>
- * Abnormal termination is reported via {@link #onConnectionFailed}.
- * </p>
- */
- public void onDisconnected() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mRecord.dispatchDisconnected(ConnectionCallback.this);
- }
- });
- }
-
- /**
- * Called by the service when a connection attempt or connection in
- * progress has failed in a non-recoverable manner.
- *
- * @param error The error code: one of
- * {@link MediaRouter#CONNECTION_ERROR_ABORTED},
- * {@link MediaRouter#CONNECTION_ERROR_UNAUTHORIZED},
- * {@link MediaRouter#CONNECTION_ERROR_UNREACHABLE},
- * {@link MediaRouter#CONNECTION_ERROR_BUSY},
- * {@link MediaRouter#CONNECTION_ERROR_TIMEOUT},
- * {@link MediaRouter#CONNECTION_ERROR_BROKEN},
- * or {@link MediaRouter#CONNECTION_ERROR_BARGED}.
- * @param message The localized error message, or null if none. This message
- * may be shown to the user.
- * @param extras Additional information about the error which a client
- * may use, or null if none.
- */
- public void onConnectionFailed(final @ConnectionError int error,
- final @Nullable CharSequence message, final @Nullable Bundle extras) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mRecord.dispatchConnectionFailed(ConnectionCallback.this,
- error, message, extras);
- }
- });
- }
- }
-
- /**
- * Identifies a client of the media route service.
- */
- public static final class ClientInfo {
- private final int mUid;
- private final String mPackageName;
-
- ClientInfo(int uid, String packageName) {
- mUid = uid;
- mPackageName = packageName;
- }
-
- /**
- * Gets the UID of the client application.
- */
- public int getUid() {
- return mUid;
- }
-
- /**
- * Gets the package name of the client application.
- */
- public @NonNull String getPackageName() {
- return mPackageName;
- }
-
- @Override
- public @NonNull String toString() {
- return "ClientInfo{ uid=" + mUid + ", package=" + mPackageName + " }";
- }
- }
-
- private final class BinderService extends IMediaRouteService.Stub {
- @Override
- public void registerClient(final int clientUid, final String clientPackageName,
- final IMediaRouteClientCallback callback) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- ClientInfo client = new ClientInfo(clientUid, clientPackageName);
- if (DEBUG) {
- Log.d(TAG, "registerClient: client=" + client);
- }
-
- ClientSession session = onCreateClientSession(client);
- if (session == null) {
- // request refused by service
- Log.w(TAG, "Media route service refused to create session for client: "
- + "client=" + client);
- return;
- }
-
- ClientRecord record = new ClientRecord(callback, client, session);
- try {
- callback.asBinder().linkToDeath(record, 0);
- } catch (RemoteException ex) {
- // client died prematurely
- Log.w(TAG, "Client died prematurely while creating session: "
- + "client=" + client);
- record.release();
- return;
- }
-
- mClientRecords.put(callback.asBinder(), record);
- }
- });
- }
-
- @Override
- public void unregisterClient(IMediaRouteClientCallback callback) {
- unregisterClient(callback, false);
- }
-
- void unregisterClient(final IMediaRouteClientCallback callback,
- final boolean died) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- ClientRecord record = mClientRecords.remove(callback.asBinder());
- if (record == null) {
- return; // spurious
- }
-
- if (DEBUG) {
- Log.d(TAG, "unregisterClient: client=" + record.getClientInfo()
- + ", died=" + died);
- }
-
- record.release();
- callback.asBinder().unlinkToDeath(record, 0);
- }
- });
- }
-
- @Override
- public void startDiscovery(final IMediaRouteClientCallback callback,
- final int seq, final List<MediaRouteSelector> selectors,
- final int flags) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- ClientRecord record = mClientRecords.get(callback.asBinder());
- if (record == null) {
- return; // spurious
- }
-
- if (DEBUG) {
- Log.d(TAG, "startDiscovery: client=" + record.getClientInfo()
- + ", seq=" + seq + ", selectors=" + selectors
- + ", flags=0x" + Integer.toHexString(flags));
- }
- record.startDiscovery(seq, selectors, flags);
- }
- });
- }
-
- @Override
- public void stopDiscovery(final IMediaRouteClientCallback callback) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- ClientRecord record = mClientRecords.get(callback.asBinder());
- if (record == null) {
- return; // spurious
- }
-
- if (DEBUG) {
- Log.d(TAG, "stopDiscovery: client=" + record.getClientInfo());
- }
- record.stopDiscovery();
- }
- });
- }
-
- @Override
- public void connect(final IMediaRouteClientCallback callback,
- final int seq, final String destinationId, final String routeId,
- final int flags, final Bundle extras) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- ClientRecord record = mClientRecords.get(callback.asBinder());
- if (record == null) {
- return; // spurious
- }
-
- if (DEBUG) {
- Log.d(TAG, "connect: client=" + record.getClientInfo()
- + ", seq=" + seq + ", destinationId=" + destinationId
- + ", routeId=" + routeId
- + ", flags=0x" + Integer.toHexString(flags)
- + ", extras=" + extras);
- }
- record.connect(seq, destinationId, routeId, flags, extras);
- }
- });
- }
-
- @Override
- public void disconnect(final IMediaRouteClientCallback callback) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- ClientRecord record = mClientRecords.get(callback.asBinder());
- if (record == null) {
- return; // spurious
- }
-
- if (DEBUG) {
- Log.d(TAG, "disconnect: client=" + record.getClientInfo());
- }
- record.disconnect();
- }
- });
- }
-
- @Override
- public void pauseStream(final IMediaRouteClientCallback callback) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- ClientRecord record = mClientRecords.get(callback.asBinder());
- if (record == null) {
- return; // spurious
- }
-
- if (DEBUG) {
- Log.d(TAG, "pauseStream: client=" + record.getClientInfo());
- }
- record.pauseStream();
- }
- });
- }
-
- @Override
- public void resumeStream(final IMediaRouteClientCallback callback) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- ClientRecord record = mClientRecords.get(callback.asBinder());
- if (record == null) {
- return; // spurious
- }
-
- if (DEBUG) {
- Log.d(TAG, "resumeStream: client=" + record.getClientInfo());
- }
- record.resumeStream();
- }
- });
- }
- }
-
- // Must be accessed on handler
- private final class ClientRecord implements IBinder.DeathRecipient {
- private final IMediaRouteClientCallback mClientCallback;
- private final ClientInfo mClient;
- private final ClientSession mSession;
-
- private int mDiscoverySeq;
- private DiscoveryRequest mDiscoveryRequest;
- private DiscoveryCallback mDiscoveryCallback;
- private final ArrayMap<String, DestinationRecord> mDestinations =
- new ArrayMap<String, DestinationRecord>();
-
- private int mConnectionSeq;
- private ConnectionRequest mConnectionRequest;
- private ConnectionCallback mConnectionCallback;
- private ConnectionInfo mConnection;
- private boolean mConnectionPaused;
-
- public ClientRecord(IMediaRouteClientCallback callback,
- ClientInfo client, ClientSession session) {
- mClientCallback = callback;
- mClient = client;
- mSession = session;
- }
-
- // Invoked on binder thread unlike all other methods in this class.
- @Override
- public void binderDied() {
- mService.unregisterClient(mClientCallback, true);
- }
-
- public ClientInfo getClientInfo() {
- return mClient;
- }
-
- public void release() {
- stopDiscovery();
- disconnect();
- }
-
- public void startDiscovery(int seq, List<MediaRouteSelector> selectors,
- int flags) {
- stopDiscovery();
-
- mDiscoverySeq = seq;
- mDiscoveryRequest = new DiscoveryRequest(selectors);
- mDiscoveryRequest.setFlags(flags);
- mDiscoveryCallback = new DiscoveryCallback(this);
- boolean started = mSession.onStartDiscovery(mDiscoveryRequest, mDiscoveryCallback);
- if (!started) {
- dispatchDiscoveryFailed(mDiscoveryCallback,
- MediaRouter.DISCOVERY_ERROR_ABORTED, null, null);
- clearDiscovery();
- }
- }
-
- public void stopDiscovery() {
- if (mDiscoveryRequest != null) {
- mSession.onStopDiscovery();
- clearDiscovery();
- }
- }
-
- private void clearDiscovery() {
- mDestinations.clear();
- mDiscoveryRequest = null;
- mDiscoveryCallback = null;
- }
-
- public void connect(int seq, String destinationId, String routeId,
- int flags, Bundle extras) {
- disconnect();
-
- mConnectionSeq = seq;
- mConnectionCallback = new ConnectionCallback(this);
-
- DestinationRecord destinationRecord = mDestinations.get(destinationId);
- if (destinationRecord == null) {
- Log.w(TAG, "Aborting connection to route since no matching destination "
- + "was found in the list of known destinations: "
- + "destinationId=" + destinationId);
- dispatchConnectionFailed(mConnectionCallback,
- MediaRouter.CONNECTION_ERROR_ABORTED, null, null);
- clearConnection();
- return;
- }
-
- RouteInfo route = destinationRecord.getRoute(routeId);
- if (route == null) {
- Log.w(TAG, "Aborting connection to route since no matching route "
- + "was found in the list of known routes: "
- + "destination=" + destinationRecord.destination
- + ", routeId=" + routeId);
- dispatchConnectionFailed(mConnectionCallback,
- MediaRouter.CONNECTION_ERROR_ABORTED, null, null);
- clearConnection();
- return;
- }
-
- mConnectionRequest = new ConnectionRequest(route);
- mConnectionRequest.setFlags(flags);
- mConnectionRequest.setExtras(extras);
- boolean started = mSession.onConnect(mConnectionRequest, mConnectionCallback);
- if (!started) {
- dispatchConnectionFailed(mConnectionCallback,
- MediaRouter.CONNECTION_ERROR_ABORTED, null, null);
- clearConnection();
- }
- }
-
- public void disconnect() {
- if (mConnectionRequest != null) {
- mSession.onDisconnect();
- clearConnection();
- }
- }
-
- private void clearConnection() {
- mConnectionRequest = null;
- mConnectionCallback = null;
- if (mConnection != null) {
- mConnection.close();
- mConnection = null;
- }
- mConnectionPaused = false;
- }
-
- public void pauseStream() {
- if (mConnectionRequest != null && !mConnectionPaused) {
- mConnectionPaused = true;
- mSession.onPauseStream();
- }
- }
-
- public void resumeStream() {
- if (mConnectionRequest != null && mConnectionPaused) {
- mConnectionPaused = false;
- mSession.onResumeStream();
- }
- }
-
- public void dispatchDestinationFound(DiscoveryCallback callback,
- DestinationInfo destination, List<RouteInfo> routes) {
- if (callback == mDiscoveryCallback) {
- if (DEBUG) {
- Log.d(TAG, "destinationFound: destination=" + destination
- + ", routes=" + routes);
- }
- mDestinations.put(destination.getId(),
- new DestinationRecord(destination, routes));
-
- ParcelableDestinationInfo pdi = new ParcelableDestinationInfo();
- pdi.id = destination.getId();
- pdi.name = destination.getName();
- pdi.description = destination.getDescription();
- pdi.iconResourceId = destination.getIconResourceId();
- pdi.extras = destination.getExtras();
- ArrayList<ParcelableRouteInfo> pris = new ArrayList<ParcelableRouteInfo>();
- for (RouteInfo route : routes) {
- int selectorIndex = mDiscoveryRequest.getSelectors().indexOf(
- route.getSelector());
- if (selectorIndex < 0) {
- Log.w(TAG, "Ignoring route because the selector does not match "
- + "any of those that were originally supplied by the "
- + "client's discovery request: destination=" + destination
- + ", route=" + route);
- continue;
- }
-
- ParcelableRouteInfo pri = new ParcelableRouteInfo();
- pri.id = route.getId();
- pri.selectorIndex = selectorIndex;
- pri.features = route.getFeatures();
- pri.protocols = route.getProtocols().toArray(
- new String[route.getProtocols().size()]);
- pri.extras = route.getExtras();
- pris.add(pri);
- }
- try {
- mClientCallback.onDestinationFound(mDiscoverySeq, pdi,
- pris.toArray(new ParcelableRouteInfo[pris.size()]));
- } catch (RemoteException ex) {
- // binder death handled elsewhere
- }
- }
- }
-
- public void dispatchDestinationLost(DiscoveryCallback callback,
- DestinationInfo destination) {
- if (callback == mDiscoveryCallback) {
- if (DEBUG) {
- Log.d(TAG, "destinationLost: destination=" + destination);
- }
-
- if (mDestinations.get(destination.getId()).destination == destination) {
- mDestinations.remove(destination.getId());
- try {
- mClientCallback.onDestinationLost(mDiscoverySeq, destination.getId());
- } catch (RemoteException ex) {
- // binder death handled elsewhere
- }
- }
- }
- }
-
- public void dispatchDiscoveryFailed(DiscoveryCallback callback,
- int error, CharSequence message, Bundle extras) {
- if (callback == mDiscoveryCallback) {
- if (DEBUG) {
- Log.d(TAG, "discoveryFailed: error=" + error + ", message=" + message
- + ", extras=" + extras);
- }
-
- try {
- mClientCallback.onDiscoveryFailed(mDiscoverySeq, error, message, extras);
- } catch (RemoteException ex) {
- // binder death handled elsewhere
- }
- }
- }
-
- public void dispatchConnected(ConnectionCallback callback, ConnectionInfo connection) {
- if (callback == mConnectionCallback) {
- if (DEBUG) {
- Log.d(TAG, "connected: connection=" + connection);
- }
- if (mConnection == null) {
- mConnection = connection;
-
- ParcelableConnectionInfo pci = new ParcelableConnectionInfo();
- pci.audioAttributes = connection.getAudioAttributes();
- pci.presentationDisplayId = connection.getPresentationDisplay() != null ?
- connection.getPresentationDisplay().getDisplayId() : -1;
- pci.protocolBinders = new IBinder[connection.getProtocols().size()];
- for (int i = 0; i < pci.protocolBinders.length; i++) {
- pci.protocolBinders[i] = connection.getProtocolBinder(i);
- }
- pci.extras = connection.getExtras();
- try {
- mClientCallback.onConnected(mConnectionSeq, pci);
- } catch (RemoteException ex) {
- // binder death handled elsewhere
- }
- } else {
- Log.w(TAG, "Media route service called onConnected() while already "
- + "connected.");
- }
- }
- }
-
- public void dispatchDisconnected(ConnectionCallback callback) {
- if (callback == mConnectionCallback) {
- if (DEBUG) {
- Log.d(TAG, "disconnected");
- }
-
- if (mConnection != null) {
- mConnection.close();
- mConnection = null;
-
- try {
- mClientCallback.onDisconnected(mConnectionSeq);
- } catch (RemoteException ex) {
- // binder death handled elsewhere
- }
- }
- }
- }
-
- public void dispatchConnectionFailed(ConnectionCallback callback,
- int error, CharSequence message, Bundle extras) {
- if (callback == mConnectionCallback) {
- if (DEBUG) {
- Log.d(TAG, "connectionFailed: error=" + error + ", message=" + message
- + ", extras=" + extras);
- }
-
- try {
- mClientCallback.onConnectionFailed(mConnectionSeq, error, message, extras);
- } catch (RemoteException ex) {
- // binder death handled elsewhere
- }
- }
- }
- }
-
- private static final class DestinationRecord {
- public final DestinationInfo destination;
- public final List<RouteInfo> routes;
-
- public DestinationRecord(DestinationInfo destination, List<RouteInfo> routes) {
- this.destination = destination;
- this.routes = routes;
- }
-
- public RouteInfo getRoute(String routeId) {
- final int count = routes.size();
- for (int i = 0; i < count; i++) {
- RouteInfo route = routes.get(i);
- if (route.getId().equals(routeId)) {
- return route;
- }
- }
- return null;
- }
- }
-}
diff --git a/media/java/android/media/routing/MediaRouter.java b/media/java/android/media/routing/MediaRouter.java
deleted file mode 100644
index 4f6d324..0000000
--- a/media/java/android/media/routing/MediaRouter.java
+++ /dev/null
@@ -1,1886 +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.media.routing;
-
-import android.annotation.DrawableRes;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.Presentation;
-import android.app.Service;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ServiceInfo;
-import android.graphics.drawable.Drawable;
-import android.hardware.display.DisplayManager;
-import android.media.AudioAttributes;
-import android.media.AudioManager;
-import android.media.AudioTrack;
-import android.media.VolumeProvider;
-import android.media.session.MediaController;
-import android.media.session.MediaSession;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IInterface;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.view.Display;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Media router allows applications to discover, connect to, control,
- * and send content to nearby media devices known as destinations.
- * <p>
- * There are generally two participants involved in media routing: an
- * application that wants to send media content to a destination and a
- * {@link MediaRouteService media route service} that provides the
- * service of transporting that content where it needs to go on behalf of the
- * application.
- * </p><p>
- * To send media content to a destination, the application must ask the system
- * to discover available routes to destinations that provide certain capabilities,
- * establish a connection to a route, then send messages through the connection to
- * control the routing of audio and video streams, launch remote applications,
- * and invoke other functions of the destination.
- * </p><p>
- * Media router objects are thread-safe.
- * </p>
- *
- * <h3>Destinations</h3>
- * <p>
- * The media devices to which an application may send media content are referred
- * to in the API as destinations. Each destination therefore represents a single
- * independent device such as a speaker or TV set. Destinations are given meaningful
- * names and descriptions to help the user associate them with devices in their
- * environment.
- * </p><p>
- * Destinations may be local or remote and may be accessed through various means,
- * often wirelessly. The user may install media route services to enable
- * media applications to connect to a variety of destinations with different
- * capabilities.
- * </p>
- *
- * <h3>Routes</h3>
- * <p>
- * Routes represent possible usages or means of reaching and interacting with
- * a destination. Since destinations may support many different features, they may
- * each offer multiple routes for applications to choose from based on their needs.
- * For example, one route might express the ability to stream locally rendered audio
- * and video to the device; another route might express the ability to send a URL for
- * the destination to download from the network and play all by itself.
- * </p><p>
- * Routes are discovered according to the set of capabilities that
- * an application or the system is seeking to use at a particular time. For example,
- * if an application wants to stream music to a destination then it will ask the
- * {@link MediaRouter} to find routes to destinations can stream music and ignore
- * all other destinations that cannot.
- * </p><p>
- * In general, the application will inspect the set of routes that have been
- * offered then connect to the most appropriate route for its desired purpose.
- * </p>
- *
- * <h3>Route Selection</h3>
- * <p>
- * When the user open the media route chooser activity, the system will display
- * a list of nearby media destinations which have been discovered. After the
- * choice is made the application may connect to one of the routes offered by
- * this destination and begin communicating with the destination.
- * </p><p>
- * Destinations are located through a process called discovery. During discovery,
- * the system will start installed {@link MediaRouteService media route services}
- * to scan the network for nearby devices that offer the kinds of capabilities that the
- * application is seeking to use. The application specifies the capabilities it requires by
- * adding {@link MediaRouteSelector media route selectors} to the media router
- * using the {@link #addSelector} method. Only destinations that provide routes
- * which satisfy at least one of these media route selectors will be discovered.
- * </p><p>
- * Once the user has selected a destination, the application will be given a chance
- * to choose one of the routes to which it would like to connect. The application
- * may switch to a different route from the same destination at a later time but
- * in order to connect to a new destination, the application must once again launch
- * the media route chooser activity to ask the user to choose a destination.
- * </p>
- *
- * <h3>Route Protocols</h3>
- * <p>
- * Route protocols express capabilities offered by routes. Each media route selector
- * must specify at least one required protocol by which the routes will be selected.
- * </p><p>
- * The framework provides several predefined <code>MediaRouteProtocols</code> which are
- * defined in the <code>android-support-media-protocols.jar</code> support library.
- * Applications must statically link this library to make use of these protocols.
- * </p><p>
- * The static library approach is used to enable ongoing extension and refinement
- * of protocols in the SDK and interoperability with the media router implementation
- * for older platform versions which is offered by the framework support library.
- * </p><p>
- * Media route services may also define custom media route protocols of their own
- * to enable applications to access specialized capabilities of certain destinations
- * assuming they have linked in the required protocol code.
- * </p><p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code> for more information.
- * </p>
- *
- * <h3>Connections</h3>
- * <p>
- * After connecting to a media route, the application can send commands to
- * the route using any of the protocols that it requested. If the route supports live
- * audio or video streaming then the application can create an {@link AudioTrack} or
- * {@link Presentation} to route locally generated content to the destination.
- * </p>
- *
- * <h3>Delegation</h3>
- * <p>
- * The creator of the media router is responsible for establishing the policy for
- * discovering and connecting to destinations. UI components may observe the state
- * of the media router by {@link #createDelegate creating} a {@link Delegate}.
- * </p><p>
- * The media router should also be attached to the {@link MediaSession media session}
- * that is handling media playback lifecycle. This will allow
- * authorized {@link MediaController media controllers}, possibly running in other
- * processes, to provide UI to examine and change the media destination by
- * {@link MediaController#createMediaRouterDelegate creating} a {@link Delegate}
- * for the media router associated with the session.
- * </p>
- */
-public final class MediaRouter {
- private final DisplayManager mDisplayManager;
-
- private final Object mLock = new Object();
-
- private RoutingCallback mRoutingCallback;
- private Handler mRoutingCallbackHandler;
-
- private boolean mReleased;
- private int mDiscoveryState;
- private int mConnectionState;
- private final ArrayList<MediaRouteSelector> mSelectors =
- new ArrayList<MediaRouteSelector>();
- private final ArrayMap<DestinationInfo, List<RouteInfo>> mDiscoveredDestinations =
- new ArrayMap<DestinationInfo, List<RouteInfo>>();
- private RouteInfo mSelectedRoute;
- private ConnectionInfo mConnection;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = { DISCOVERY_STATE_STOPPED, DISCOVERY_STATE_STARTED })
- public @interface DiscoveryState { }
-
- /**
- * Discovery state: Discovery is not currently in progress.
- */
- public static final int DISCOVERY_STATE_STOPPED = 0;
-
- /**
- * Discovery state: Discovery is being performed.
- */
- public static final int DISCOVERY_STATE_STARTED = 1;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, value = { DISCOVERY_FLAG_BACKGROUND })
- public @interface DiscoveryFlags { }
-
- /**
- * Discovery flag: Indicates that the client has requested passive discovery in
- * the background. The media route service should try to use less power and rely
- * more on its internal caches to minimize its impact.
- */
- public static final int DISCOVERY_FLAG_BACKGROUND = 1 << 0;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = { DISCOVERY_ERROR_UNKNOWN, DISCOVERY_ERROR_ABORTED,
- DISCOVERY_ERROR_NO_CONNECTIVITY })
- public @interface DiscoveryError { }
-
- /**
- * Discovery error: Unknown error; refer to the error message for details.
- */
- public static final int DISCOVERY_ERROR_UNKNOWN = 0;
-
- /**
- * Discovery error: The media router or media route service has decided not to
- * handle the discovery request for some reason.
- */
- public static final int DISCOVERY_ERROR_ABORTED = 1;
-
- /**
- * Discovery error: The media route service is unable to perform discovery
- * due to a lack of connectivity such as because the radio is disabled.
- */
- public static final int DISCOVERY_ERROR_NO_CONNECTIVITY = 2;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = { CONNECTION_STATE_DISCONNECTED, CONNECTION_STATE_CONNECTING,
- CONNECTION_STATE_CONNECTED })
- public @interface ConnectionState { }
-
- /**
- * Connection state: No destination has been selected. Media content should
- * be sent to the default output.
- */
- public static final int CONNECTION_STATE_DISCONNECTED = 0;
-
- /**
- * Connection state: The application is in the process of connecting to
- * a route offered by the selected destination.
- */
- public static final int CONNECTION_STATE_CONNECTING = 1;
-
- /**
- * Connection state: The application has connected to a route offered by
- * the selected destination.
- */
- public static final int CONNECTION_STATE_CONNECTED = 2;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, value = { CONNECTION_FLAG_BARGE })
- public @interface ConnectionFlags { }
-
- /**
- * Connection flag: Indicates that the client has requested to barge in and evict
- * other clients that might have already connected to the destination and that
- * would otherwise prevent this client from connecting. When this flag is not
- * set, the media route service should be polite and report
- * {@link MediaRouter#CONNECTION_ERROR_BUSY} in case the destination is
- * already occupied and cannot accept additional connections.
- */
- public static final int CONNECTION_FLAG_BARGE = 1 << 0;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = { CONNECTION_ERROR_UNKNOWN, CONNECTION_ERROR_ABORTED,
- CONNECTION_ERROR_UNAUTHORIZED, CONNECTION_ERROR_UNAUTHORIZED,
- CONNECTION_ERROR_BUSY, CONNECTION_ERROR_TIMEOUT, CONNECTION_ERROR_BROKEN })
- public @interface ConnectionError { }
-
- /**
- * Connection error: Unknown error; refer to the error message for details.
- */
- public static final int CONNECTION_ERROR_UNKNOWN = 0;
-
- /**
- * Connection error: The media router or media route service has decided not to
- * handle the connection request for some reason.
- */
- public static final int CONNECTION_ERROR_ABORTED = 1;
-
- /**
- * Connection error: The device has refused the connection from this client.
- * This error should be avoided because the media route service should attempt
- * to filter out devices that the client cannot access as it performs discovery
- * on behalf of that client.
- */
- public static final int CONNECTION_ERROR_UNAUTHORIZED = 2;
-
- /**
- * Connection error: The device is unreachable over the network.
- */
- public static final int CONNECTION_ERROR_UNREACHABLE = 3;
-
- /**
- * Connection error: The device is already busy serving another client and
- * the connection request did not ask to barge in.
- */
- public static final int CONNECTION_ERROR_BUSY = 4;
-
- /**
- * Connection error: A timeout occurred during connection.
- */
- public static final int CONNECTION_ERROR_TIMEOUT = 5;
-
- /**
- * Connection error: The connection to the device was severed unexpectedly.
- */
- public static final int CONNECTION_ERROR_BROKEN = 6;
-
- /**
- * Connection error: The connection was terminated because a different client barged
- * in and took control of the destination.
- */
- public static final int CONNECTION_ERROR_BARGED = 7;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = { DISCONNECTION_REASON_APPLICATION_REQUEST,
- DISCONNECTION_REASON_USER_REQUEST, DISCONNECTION_REASON_ERROR })
- public @interface DisconnectionReason { }
-
- /**
- * Disconnection reason: The application requested disconnection itself.
- */
- public static final int DISCONNECTION_REASON_APPLICATION_REQUEST = 0;
-
- /**
- * Disconnection reason: The user requested disconnection.
- */
- public static final int DISCONNECTION_REASON_USER_REQUEST = 1;
-
- /**
- * Disconnection reason: An error occurred.
- */
- public static final int DISCONNECTION_REASON_ERROR = 2;
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, value = { ROUTE_FEATURE_LIVE_AUDIO, ROUTE_FEATURE_LIVE_VIDEO })
- public @interface RouteFeatures { }
-
- /**
- * Route feature: Live audio.
- * <p>
- * A route that supports live audio streams audio rendered by the application
- * to the destination.
- * </p><p>
- * To take advantage of live audio routing, the application must render its
- * media using the audio attributes specified by {@link #getPreferredAudioAttributes}.
- * </p>
- *
- * @see #getPreferredAudioAttributes
- * @see android.media.AudioAttributes
- */
- public static final int ROUTE_FEATURE_LIVE_AUDIO = 1 << 0;
-
- /**
- * Route feature: Live video.
- * <p>
- * A route that supports live video streams video rendered by the application
- * to the destination.
- * </p><p>
- * To take advantage of live video routing, the application must render its
- * media to a {@link android.app.Presentation presentation window} on the
- * display specified by {@link #getPreferredPresentationDisplay}.
- * </p>
- *
- * @see #getPreferredPresentationDisplay
- * @see android.app.Presentation
- */
- public static final int ROUTE_FEATURE_LIVE_VIDEO = 1 << 1;
-
- /**
- * Creates a media router.
- *
- * @param context The context with which the router is associated.
- */
- public MediaRouter(@NonNull Context context) {
- if (context == null) {
- throw new IllegalArgumentException("context must not be null");
- }
-
- mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
- }
-
- /** @hide */
- public IMediaRouter getBinder() {
- // todo
- return null;
- }
-
- /**
- * Disconnects from the selected destination and releases the media router.
- * <p>
- * This method should be called by the application when it no longer requires
- * the media router to ensure that all bound resources may be cleaned up.
- * </p>
- */
- public void release() {
- synchronized (mLock) {
- mReleased = true;
- // todo
- }
- }
-
- /**
- * Returns true if the media router has been released.
- */
- public boolean isReleased() {
- synchronized (mLock) {
- return mReleased;
- }
- }
-
- /**
- * Gets the current route discovery state.
- *
- * @return The current discovery state: one of {@link #DISCOVERY_STATE_STOPPED},
- * {@link #DISCOVERY_STATE_STARTED}.
- */
- public @DiscoveryState int getDiscoveryState() {
- synchronized (mLock) {
- return mDiscoveryState;
- }
- }
-
- /**
- * Gets the current route connection state.
- *
- * @return The current state: one of {@link #CONNECTION_STATE_DISCONNECTED},
- * {@link #CONNECTION_STATE_CONNECTING} or {@link #CONNECTION_STATE_CONNECTED}.
- */
- public @ConnectionState int getConnectionState() {
- synchronized (mLock) {
- return mConnectionState;
- }
- }
-
- /**
- * Creates a media router delegate through which the destination of the media
- * router may be controlled.
- * <p>
- * This is the point of entry for UI code that initiates discovery and
- * connection to routes.
- * </p>
- */
- public @NonNull Delegate createDelegate() {
- return null; // todo
- }
-
- /**
- * Sets a callback to participate in route discovery, filtering, and connection
- * establishment.
- *
- * @param callback The callback to set, or null if none.
- * @param handler The handler to receive callbacks, or null to use the current thread.
- */
- public void setRoutingCallback(@Nullable RoutingCallback callback,
- @Nullable Handler handler) {
- synchronized (mLock) {
- if (callback == null) {
- mRoutingCallback = null;
- mRoutingCallbackHandler = null;
- } else {
- mRoutingCallback = callback;
- mRoutingCallbackHandler = handler != null ? handler : new Handler();
- }
- }
- }
-
- /**
- * Adds a media route selector to use to find destinations that have
- * routes with the specified capabilities during route discovery.
- */
- public void addSelector(@NonNull MediaRouteSelector selector) {
- if (selector == null) {
- throw new IllegalArgumentException("selector must not be null");
- }
-
- synchronized (mLock) {
- if (!mSelectors.contains(selector)) {
- mSelectors.add(selector);
- // todo
- }
- }
- }
-
- /**
- * Removes a media route selector.
- */
- public void removeSelector(@NonNull MediaRouteSelector selector) {
- if (selector == null) {
- throw new IllegalArgumentException("selector must not be null");
- }
-
- synchronized (mLock) {
- if (mSelectors.remove(selector)) {
- // todo
- }
- }
- }
-
- /**
- * Removes all media route selectors.
- * <p>
- * Note that at least one selector must be added in order to perform discovery.
- * </p>
- */
- public void clearSelectors() {
- synchronized (mLock) {
- if (!mSelectors.isEmpty()) {
- mSelectors.clear();
- // todo
- }
- }
- }
-
- /**
- * Gets a list of all media route selectors to consider during discovery.
- */
- public @NonNull List<MediaRouteSelector> getSelectors() {
- synchronized (mLock) {
- return new ArrayList<MediaRouteSelector>(mSelectors);
- }
- }
-
- /**
- * Gets the connection to the currently selected route.
- *
- * @return The connection to the currently selected route, or null if not connected.
- */
- public @NonNull ConnectionInfo getConnection() {
- synchronized (mLock) {
- return mConnection;
- }
- }
-
- /**
- * Gets the list of discovered destinations.
- * <p>
- * This list is only valid while discovery is running and is null otherwise.
- * </p>
- *
- * @return The list of discovered destinations, or null if discovery is not running.
- */
- public @NonNull List<DestinationInfo> getDiscoveredDestinations() {
- synchronized (mLock) {
- if (mDiscoveryState == DISCOVERY_STATE_STARTED) {
- return new ArrayList<DestinationInfo>(mDiscoveredDestinations.keySet());
- }
- return null;
- }
- }
-
- /**
- * Gets the list of discovered routes for a particular destination.
- * <p>
- * This list is only valid while discovery is running and is null otherwise.
- * </p>
- *
- * @param destination The destination for which to get the list of discovered routes.
- * @return The list of discovered routes for the destination, or null if discovery
- * is not running.
- */
- public @NonNull List<RouteInfo> getDiscoveredRoutes(@NonNull DestinationInfo destination) {
- if (destination == null) {
- throw new IllegalArgumentException("destination must not be null");
- }
- synchronized (mLock) {
- if (mDiscoveryState == DISCOVERY_STATE_STARTED) {
- List<RouteInfo> routes = mDiscoveredDestinations.get(destination);
- if (routes != null) {
- return new ArrayList<RouteInfo>(routes);
- }
- }
- return null;
- }
- }
-
- /**
- * Gets the destination that has been selected.
- *
- * @return The selected destination, or null if disconnected.
- */
- public @Nullable DestinationInfo getSelectedDestination() {
- synchronized (mLock) {
- return mSelectedRoute != null ? mSelectedRoute.getDestination() : null;
- }
- }
-
- /**
- * Gets the route that has been selected.
- *
- * @return The selected destination, or null if disconnected.
- */
- public @Nullable RouteInfo getSelectedRoute() {
- synchronized (mLock) {
- return mSelectedRoute;
- }
- }
-
- /**
- * Gets the preferred audio attributes that should be used to stream live audio content
- * based on the connected route.
- * <p>
- * Use an {@link AudioTrack} to send audio content to the destination with these
- * audio attributes.
- * </p><p>
- * The preferred audio attributes may change when a connection is established but it
- * will remain constant until disconnected.
- * </p>
- *
- * @return The preferred audio attributes to use. When connected, returns the
- * route's audio attributes or null if it does not support live audio streaming.
- * Otherwise returns audio attributes associated with {@link AudioAttributes#USAGE_MEDIA}.
- */
- public @Nullable AudioAttributes getPreferredAudioAttributes() {
- synchronized (mLock) {
- if (mConnection != null) {
- return mConnection.getAudioAttributes();
- }
- return new AudioAttributes.Builder()
- .setLegacyStreamType(AudioManager.STREAM_MUSIC)
- .build();
- }
- }
-
- /**
- * Gets the preferred presentation display that should be used to stream live video content
- * based on the connected route.
- * <p>
- * Use a {@link Presentation} to send video content to the destination with this display.
- * </p><p>
- * The preferred presentation display may change when a connection is established but it
- * will remain constant until disconnected.
- * </p>
- *
- * @return The preferred presentation display to use. When connected, returns
- * the route's presentation display or null if it does not support live video
- * streaming. Otherwise returns the first available
- * {@link DisplayManager#DISPLAY_CATEGORY_PRESENTATION presentation display},
- * such as a mirrored wireless or HDMI display or null if none.
- */
- public @Nullable Display getPreferredPresentationDisplay() {
- synchronized (mLock) {
- if (mConnection != null) {
- return mConnection.getPresentationDisplay();
- }
- Display[] displays = mDisplayManager.getDisplays(
- DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
- return displays.length != 0 ? displays[0] : null;
- }
- }
-
- /**
- * Gets the preferred volume provider that should be used to control the volume
- * of content rendered on the currently selected route.
- * <p>
- * The preferred volume provider may change when a connection is established but it
- * will remain the same until disconnected.
- * </p>
- *
- * @return The preferred volume provider to use, or null if the currently
- * selected route does not support remote volume adjustment or if the connection
- * is not yet established. If no route is selected, returns null to indicate
- * that system volume control should be used.
- */
- public @Nullable VolumeProvider getPreferredVolumeProvider() {
- synchronized (mLock) {
- if (mConnection != null) {
- return mConnection.getVolumeProvider();
- }
- return null;
- }
- }
-
- /**
- * Requests to pause streaming of live audio or video routes.
- * Should be called when the application is going into the background and is
- * no longer rendering content locally.
- * <p>
- * This method does nothing unless a connection has been established.
- * </p>
- */
- public void pauseStream() {
- // todo
- }
-
- /**
- * Requests to resume streaming of live audio or video routes.
- * May be called when the application is returning to the foreground and is
- * about to resume rendering content locally.
- * <p>
- * This method does nothing unless a connection has been established.
- * </p>
- */
- public void resumeStream() {
- // todo
- }
-
- /**
- * This class is used by UI components to let the user discover and
- * select a destination to which the media router should connect.
- * <p>
- * This API has somewhat more limited functionality than the {@link MediaRouter}
- * itself because it is designed to allow applications to control
- * the destination of media router instances that belong to other processes.
- * </p><p>
- * To control the destination of your own media router, call
- * {@link #createDelegate} to obtain a local delegate object.
- * </p><p>
- * To control the destination of a media router that belongs to another process,
- * first obtain a {@link MediaController} that is associated with the media playback
- * that is occurring in that process, then call
- * {@link MediaController#createMediaRouterDelegate} to obtain an instance of
- * its destination controls. Note that special permissions may be required to
- * obtain the {@link MediaController} instance in the first place.
- * </p>
- */
- public static final class Delegate {
- /**
- * Returns true if the media router has been released.
- */
- public boolean isReleased() {
- // todo
- return false;
- }
-
- /**
- * Gets the current route discovery state.
- *
- * @return The current discovery state: one of {@link #DISCOVERY_STATE_STOPPED},
- * {@link #DISCOVERY_STATE_STARTED}.
- */
- public @DiscoveryState int getDiscoveryState() {
- // todo
- return -1;
- }
-
- /**
- * Gets the current route connection state.
- *
- * @return The current state: one of {@link #CONNECTION_STATE_DISCONNECTED},
- * {@link #CONNECTION_STATE_CONNECTING} or {@link #CONNECTION_STATE_CONNECTED}.
- */
- public @ConnectionState int getConnectionState() {
- // todo
- return -1;
- }
-
- /**
- * Gets the currently selected destination.
- *
- * @return The destination information, or null if none.
- */
- public @Nullable DestinationInfo getSelectedDestination() {
- return null;
- }
-
- /**
- * Gets the list of discovered destinations.
- * <p>
- * This list is only valid while discovery is running and is null otherwise.
- * </p>
- *
- * @return The list of discovered destinations, or null if discovery is not running.
- */
- public @NonNull List<DestinationInfo> getDiscoveredDestinations() {
- return null;
- }
-
- /**
- * Adds a callback to receive state changes.
- *
- * @param callback The callback to set, or null if none.
- * @param handler The handler to receive callbacks, or null to use the current thread.
- */
- public void addStateCallback(@Nullable StateCallback callback,
- @Nullable Handler handler) {
- if (callback == null) {
- throw new IllegalArgumentException("callback must not be null");
- }
- if (handler == null) {
- handler = new Handler();
- }
- // todo
- }
-
- /**
- * Removes a callback for state changes.
- *
- * @param callback The callback to set, or null if none.
- */
- public void removeStateCallback(@Nullable StateCallback callback) {
- // todo
- }
-
- /**
- * Starts performing discovery.
- * <p>
- * Performing discovery is expensive. Make sure to call {@link #stopDiscovery}
- * as soon as possible once a new destination has been selected to allow the system
- * to stop services associated with discovery.
- * </p>
- *
- * @param flags The discovery flags, such as {@link MediaRouter#DISCOVERY_FLAG_BACKGROUND}.
- */
- public void startDiscovery(@DiscoveryFlags int flags) {
- // todo
- }
-
- /**
- * Stops performing discovery.
- */
- public void stopDiscovery() {
- // todo
- }
-
- /**
- * Connects to a destination during route discovery.
- * <p>
- * This method may only be called while route discovery is active and the
- * destination appears in the
- * {@link #getDiscoveredDestinations list of discovered destinations}.
- * If the media router is already connected to a route then it will first disconnect
- * from the current route then connect to the new route.
- * </p>
- *
- * @param destination The destination to which the media router should connect.
- * @param flags The connection flags, such as {@link MediaRouter#CONNECTION_FLAG_BARGE}.
- */
- public void connect(@NonNull DestinationInfo destination, @DiscoveryFlags int flags) {
- // todo
- }
-
- /**
- * Disconnects from the currently selected destination.
- * <p>
- * Does nothing if not currently connected.
- * </p>
- *
- * @param reason The reason for the disconnection: one of
- * {@link #DISCONNECTION_REASON_APPLICATION_REQUEST},
- * {@link #DISCONNECTION_REASON_USER_REQUEST}, or {@link #DISCONNECTION_REASON_ERROR}.
- */
- public void disconnect(@DisconnectionReason int reason) {
- // todo
- }
- }
-
- /**
- * Describes immutable properties of a connection to a route.
- */
- public static final class ConnectionInfo {
- private final RouteInfo mRoute;
- private final AudioAttributes mAudioAttributes;
- private final Display mPresentationDisplay;
- private final VolumeProvider mVolumeProvider;
- private final IBinder[] mProtocolBinders;
- private final Object[] mProtocolInstances;
- private final Bundle mExtras;
- private final ArrayList<Closeable> mCloseables;
-
- private static final Class<?>[] MEDIA_ROUTE_PROTOCOL_CTOR_PARAMETERS =
- new Class<?>[] { IBinder.class };
-
- ConnectionInfo(RouteInfo route,
- AudioAttributes audioAttributes, Display display,
- VolumeProvider volumeProvider, IBinder[] protocolBinders,
- Bundle extras, ArrayList<Closeable> closeables) {
- mRoute = route;
- mAudioAttributes = audioAttributes;
- mPresentationDisplay = display;
- mVolumeProvider = volumeProvider;
- mProtocolBinders = protocolBinders;
- mProtocolInstances = new Object[mProtocolBinders.length];
- mExtras = extras;
- mCloseables = closeables;
- }
-
- /**
- * Gets the route that is connected.
- */
- public @NonNull RouteInfo getRoute() {
- return mRoute;
- }
-
- /**
- * Gets the audio attributes which the client should use to stream audio
- * to the destination, or null if the route does not support live audio streaming.
- */
- public @Nullable AudioAttributes getAudioAttributes() {
- return mAudioAttributes;
- }
-
- /**
- * Gets the display which the client should use to stream video to the
- * destination using a {@link Presentation}, or null if the route does not
- * support live video streaming.
- */
- public @Nullable Display getPresentationDisplay() {
- return mPresentationDisplay;
- }
-
- /**
- * Gets the route's volume provider, or null if none.
- */
- public @Nullable VolumeProvider getVolumeProvider() {
- return mVolumeProvider;
- }
-
- /**
- * Gets the set of supported route features.
- */
- public @RouteFeatures int getFeatures() {
- return mRoute.getFeatures();
- }
-
- /**
- * Gets the list of supported route protocols.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- */
- public @NonNull List<String> getProtocols() {
- return mRoute.getProtocols();
- }
-
- /**
- * Gets an instance of a route protocol object that wraps the protocol binder
- * and provides easy access to the protocol's functionality.
- * <p>
- * This is a convenience method which invokes {@link #getProtocolBinder(String)}
- * using the name of the provided class then passes the resulting {@link IBinder}
- * to a single-argument constructor of that class.
- * </p><p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- */
- @SuppressWarnings("unchecked")
- public @Nullable <T> T getProtocolObject(Class<T> clazz) {
- int index = getProtocols().indexOf(clazz.getName());
- if (index < 0) {
- return null;
- }
- if (mProtocolInstances[index] == null && mProtocolBinders[index] != null) {
- final Constructor<T> ctor;
- try {
- ctor = clazz.getConstructor(MEDIA_ROUTE_PROTOCOL_CTOR_PARAMETERS);
- } catch (NoSuchMethodException ex) {
- throw new RuntimeException("Could not find public constructor "
- + "with IBinder argument in protocol class: " + clazz.getName(), ex);
- }
- try {
- mProtocolInstances[index] = ctor.newInstance(mProtocolBinders[index]);
- } catch (InstantiationException | IllegalAccessException
- | InvocationTargetException ex) {
- throw new RuntimeException("Could create instance of protocol class: "
- + clazz.getName(), ex);
- }
- }
- return (T)mProtocolInstances[index];
- }
-
- /**
- * Gets the {@link IBinder} that provides access to the specified route protocol
- * or null if the protocol is not supported.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- */
- public @Nullable IBinder getProtocolBinder(@NonNull String name) {
- int index = getProtocols().indexOf(name);
- return index >= 0 ? mProtocolBinders[index] : null;
- }
-
- /**
- * Gets the {@link IBinder} that provides access to the specified route protocol
- * at the given index in the protocol list or null if the protocol is not supported.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- */
- public @Nullable IBinder getProtocolBinder(int index) {
- return mProtocolBinders[index];
- }
-
- /**
- * Gets optional extra media route service or protocol specific information about
- * the connection. Use the service or protocol name as the prefix for
- * any extras to avoid namespace collisions.
- */
- public @Nullable Bundle getExtras() {
- return mExtras;
- }
-
- /**
- * Closes all closeables associated with the connection when the connection
- * is being torn down.
- */
- void close() {
- final int count = mCloseables.size();
- for (int i = 0; i < count; i++) {
- try {
- mCloseables.get(i).close();
- } catch (IOException ex) {
- }
- }
- }
-
- @Override
- public @NonNull String toString() {
- return "ConnectionInfo{ route=" + mRoute
- + ", audioAttributes=" + mAudioAttributes
- + ", presentationDisplay=" + mPresentationDisplay
- + ", volumeProvider=" + mVolumeProvider
- + ", protocolBinders=" + mProtocolBinders + " }";
- }
-
- /**
- * Builds {@link ConnectionInfo} objects.
- */
- public static final class Builder {
- private final RouteInfo mRoute;
- private AudioAttributes mAudioAttributes;
- private Display mPresentationDisplay;
- private VolumeProvider mVolumeProvider;
- private final IBinder[] mProtocols;
- private Bundle mExtras;
- private final ArrayList<Closeable> mCloseables = new ArrayList<Closeable>();
-
- /**
- * Creates a builder for connection information.
- *
- * @param route The route that is connected.
- */
- public Builder(@NonNull RouteInfo route) {
- if (route == null) {
- throw new IllegalArgumentException("route");
- }
- mRoute = route;
- mProtocols = new IBinder[route.getProtocols().size()];
- }
-
- /**
- * Sets the audio attributes which the client should use to stream audio
- * to the destination, or null if the route does not support live audio streaming.
- */
- public @NonNull Builder setAudioAttributes(
- @Nullable AudioAttributes audioAttributes) {
- mAudioAttributes = audioAttributes;
- return this;
- }
-
- /**
- * Sets the display which the client should use to stream video to the
- * destination using a {@link Presentation}, or null if the route does not
- * support live video streaming.
- */
- public @NonNull Builder setPresentationDisplay(@Nullable Display display) {
- mPresentationDisplay = display;
- return this;
- }
-
- /**
- * Sets the route's volume provider, or null if none.
- */
- public @NonNull Builder setVolumeProvider(@Nullable VolumeProvider provider) {
- mVolumeProvider = provider;
- return this;
- }
-
- /**
- * Sets the binder stub of a supported route protocol using
- * the protocol's fully qualified class name. The protocol must be one
- * of those that was indicated as being supported by the route.
- * <p>
- * If the stub implements {@link Closeable} then it will automatically
- * be closed when the client disconnects from the route.
- * </p><p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- */
- public @NonNull Builder setProtocolStub(@NonNull Class<?> clazz,
- @NonNull IInterface stub) {
- if (clazz == null) {
- throw new IllegalArgumentException("clazz must not be null");
- }
- if (stub == null) {
- throw new IllegalArgumentException("stub must not be null");
- }
- if (stub instanceof Closeable) {
- mCloseables.add((Closeable)stub);
- }
- return setProtocolBinder(clazz.getName(), stub.asBinder());
- }
-
- /**
- * Sets the binder interface of a supported route protocol by name.
- * The protocol must be one of those that was indicated as being supported
- * by the route.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- */
- public @NonNull Builder setProtocolBinder(@NonNull String name,
- @NonNull IBinder binder) {
- if (TextUtils.isEmpty(name)) {
- throw new IllegalArgumentException("name must not be null or empty");
- }
- if (binder == null) {
- throw new IllegalArgumentException("binder must not be null");
- }
- int index = mRoute.getProtocols().indexOf(name);
- if (index < 0) {
- throw new IllegalArgumentException("name must specify a protocol that "
- + "the route actually declared that it supports: "
- + "name=" + name + ", protocols=" + mRoute.getProtocols());
- }
- mProtocols[index] = binder;
- return this;
- }
-
- /**
- * Sets optional extra media route service or protocol specific information about
- * the connection. Use the service or protocol name as the prefix for
- * any extras to avoid namespace collisions.
- */
- public @NonNull Builder setExtras(@Nullable Bundle extras) {
- mExtras = extras;
- return this;
- }
-
- /**
- * Builds the {@link ConnectionInfo} object.
- */
- public @NonNull ConnectionInfo build() {
- return new ConnectionInfo(mRoute,
- mAudioAttributes, mPresentationDisplay,
- mVolumeProvider, mProtocols, mExtras, mCloseables);
- }
- }
- }
-
- /**
- * Describes one particular way of routing media content to a destination
- * according to the capabilities specified by a media route selector on behalf
- * of an application.
- */
- public static final class RouteInfo {
- private final String mId;
- private final DestinationInfo mDestination;
- private final MediaRouteSelector mSelector;
- private final int mFeatures;
- private final ArrayList<String> mProtocols;
- private final Bundle mExtras;
-
- RouteInfo(String id, DestinationInfo destination, MediaRouteSelector selector,
- int features, ArrayList<String> protocols, Bundle extras) {
- mId = id;
- mDestination = destination;
- mSelector = selector;
- mFeatures = features;
- mProtocols = protocols;
- mExtras = extras;
- }
-
- /**
- * Gets the route's stable identifier.
- * <p>
- * The id is intended to uniquely identify the route among all routes that
- * are offered by a particular destination in such a way that the client can
- * refer to it at a later time.
- * </p>
- */
- public @NonNull String getId() {
- return mId;
- }
-
- /**
- * Gets the destination that is offering this route.
- */
- public @NonNull DestinationInfo getDestination() {
- return mDestination;
- }
-
- /**
- * Gets the media route selector provided by the client for which this
- * route was created.
- * <p>
- * It is implied that this route supports all of the required capabilities
- * that were expressed in the selector.
- * </p>
- */
- public @NonNull MediaRouteSelector getSelector() {
- return mSelector;
- }
-
- /**
- * Gets the set of supported route features.
- */
- public @RouteFeatures int getFeatures() {
- return mFeatures;
- }
-
- /**
- * Gets the list of supported route protocols.
- * <p>
- * Refer to <code>android.support.media.protocols.MediaRouteProtocol</code>
- * for more information.
- * </p>
- */
- public @NonNull List<String> getProtocols() {
- return mProtocols;
- }
-
- /**
- * Gets optional extra information about the route, or null if none.
- */
- public @Nullable Bundle getExtras() {
- return mExtras;
- }
-
- @Override
- public @NonNull String toString() {
- return "RouteInfo{ id=" + mId + ", destination=" + mDestination
- + ", features=0x" + Integer.toHexString(mFeatures)
- + ", selector=" + mSelector + ", protocols=" + mProtocols
- + ", extras=" + mExtras + " }";
- }
-
- /**
- * Builds {@link RouteInfo} objects.
- */
- public static final class Builder {
- private final DestinationInfo mDestination;
- private final String mId;
- private final MediaRouteSelector mSelector;
- private int mFeatures;
- private final ArrayList<String> mProtocols = new ArrayList<String>();
- private Bundle mExtras;
-
- /**
- * Creates a builder for route information.
- *
- * @param id The route's stable identifier.
- * @param destination The destination of this route.
- * @param selector The media route selector provided by the client for which
- * this route was created. This must be one of the selectors that was
- * included in the discovery request.
- */
- public Builder(@NonNull String id, @NonNull DestinationInfo destination,
- @NonNull MediaRouteSelector selector) {
- if (TextUtils.isEmpty(id)) {
- throw new IllegalArgumentException("id must not be null or empty");
- }
- if (destination == null) {
- throw new IllegalArgumentException("destination must not be null");
- }
- if (selector == null) {
- throw new IllegalArgumentException("selector must not be null");
- }
- mDestination = destination;
- mId = id;
- mSelector = selector;
- }
-
- /**
- * Sets the set of supported route features.
- */
- public @NonNull Builder setFeatures(@RouteFeatures int features) {
- mFeatures = features;
- return this;
- }
-
- /**
- * Adds a supported route protocol using its fully qualified class name.
- * <p>
- * If the protocol was not requested by the client in its selector
- * then it will be silently discarded.
- * </p>
- */
- public @NonNull <T extends IInterface> Builder addProtocol(@NonNull Class<T> clazz) {
- if (clazz == null) {
- throw new IllegalArgumentException("clazz must not be null");
- }
- return addProtocol(clazz.getName());
- }
-
- /**
- * Adds a supported route protocol by name.
- * <p>
- * If the protocol was not requested by the client in its selector
- * then it will be silently discarded.
- * </p>
- */
- public @NonNull Builder addProtocol(@NonNull String name) {
- if (TextUtils.isEmpty(name)) {
- throw new IllegalArgumentException("name must not be null");
- }
- if (mSelector.containsProtocol(name)) {
- mProtocols.add(name);
- }
- return this;
- }
-
- /**
- * Sets optional extra information about the route, or null if none.
- */
- public @NonNull Builder setExtras(@Nullable Bundle extras) {
- mExtras = extras;
- return this;
- }
-
- /**
- * Builds the {@link RouteInfo} object.
- * <p>
- * Ensures that all required protocols have been supplied.
- * </p>
- */
- public @NonNull RouteInfo build() {
- int missingFeatures = mSelector.getRequiredFeatures() & ~mFeatures;
- if (missingFeatures != 0) {
- throw new IllegalStateException("The media route selector "
- + "specified required features which this route does "
- + "not appear to support so it should not have been published: "
- + "missing 0x" + Integer.toHexString(missingFeatures));
- }
- for (String protocol : mSelector.getRequiredProtocols()) {
- if (!mProtocols.contains(protocol)) {
- throw new IllegalStateException("The media route selector "
- + "specified required protocols which this route "
- + "does not appear to support so it should not have "
- + "been published: missing " + protocol);
- }
- }
- return new RouteInfo(mId, mDestination, mSelector,
- mFeatures, mProtocols, mExtras);
- }
- }
- }
-
- /**
- * Describes a destination for media content such as a device,
- * an individual port on a device, or a group of devices.
- */
- public static final class DestinationInfo {
- private final String mId;
- private final ServiceMetadata mService;
- private final CharSequence mName;
- private final CharSequence mDescription;
- private final int mIconResourceId;
- private final Bundle mExtras;
-
- DestinationInfo(String id, ServiceMetadata service,
- CharSequence name, CharSequence description,
- int iconResourceId, Bundle extras) {
- mId = id;
- mService = service;
- mName = name;
- mDescription = description;
- mIconResourceId = iconResourceId;
- mExtras = extras;
- }
-
- /**
- * Gets the destination's stable identifier.
- * <p>
- * The id is intended to uniquely identify the destination among all destinations
- * provided by the media route service in such a way that the client can
- * refer to it at a later time. Ideally, the id should be resilient to
- * user-initiated actions such as changes to the name or description
- * of the destination.
- * </p>
- */
- public @NonNull String getId() {
- return mId;
- }
-
- /**
- * Gets metadata about the service that is providing access to this destination.
- */
- public @NonNull ServiceMetadata getServiceMetadata() {
- return mService;
- }
-
- /**
- * Gets the destination's name for display to the user.
- */
- public @NonNull CharSequence getName() {
- return mName;
- }
-
- /**
- * Gets the destination's description for display to the user, or null if none.
- */
- public @Nullable CharSequence getDescription() {
- return mDescription;
- }
-
- /**
- * Gets an icon resource from the service's package which is used
- * to identify the destination, or -1 if none.
- */
- public @DrawableRes int getIconResourceId() {
- return mIconResourceId;
- }
-
- /**
- * Loads the icon drawable, or null if none.
- */
- public @Nullable Drawable loadIcon(@NonNull PackageManager pm) {
- return mIconResourceId >= 0 ? mService.getDrawable(pm, mIconResourceId) : null;
- }
-
- /**
- * Gets optional extra information about the destination, or null if none.
- */
- public @Nullable Bundle getExtras() {
- return mExtras;
- }
-
- @Override
- public @NonNull String toString() {
- return "DestinationInfo{ id=" + mId + ", service=" + mService + ", name=" + mName
- + ", description=" + mDescription + ", iconResourceId=" + mIconResourceId
- + ", extras=" + mExtras + " }";
- }
-
- /**
- * Builds {@link DestinationInfo} objects.
- */
- public static final class Builder {
- private final String mId;
- private final ServiceMetadata mService;
- private final CharSequence mName;
- private CharSequence mDescription;
- private int mIconResourceId = -1;
- private Bundle mExtras;
-
- /**
- * Creates a builder for destination information.
- *
- * @param id The destination's stable identifier.
- * @param service Metatada about the service that is providing access to
- * this destination.
- * @param name The destination's name for display to the user.
- */
- public Builder(@NonNull String id, @NonNull ServiceMetadata service,
- @NonNull CharSequence name) {
- if (TextUtils.isEmpty(id)) {
- throw new IllegalArgumentException("id must not be null or empty");
- }
- if (service == null) {
- throw new IllegalArgumentException("service must not be null");
- }
- if (TextUtils.isEmpty(name)) {
- throw new IllegalArgumentException("name must not be null or empty");
- }
- mId = id;
- mService = service;
- mName = name;
- }
-
- /**
- * Sets the destination's description for display to the user, or null if none.
- */
- public @NonNull Builder setDescription(@Nullable CharSequence description) {
- mDescription = description;
- return this;
- }
-
- /**
- * Sets an icon resource from this package used to identify the destination,
- * or -1 if none.
- */
- public @NonNull Builder setIconResourceId(@DrawableRes int resid) {
- mIconResourceId = resid;
- return this;
- }
-
- /**
- * Gets optional extra information about the destination, or null if none.
- */
- public @NonNull Builder setExtras(@Nullable Bundle extras) {
- mExtras = extras;
- return this;
- }
-
- /**
- * Builds the {@link DestinationInfo} object.
- */
- public @NonNull DestinationInfo build() {
- return new DestinationInfo(mId, mService, mName, mDescription,
- mIconResourceId, mExtras);
- }
- }
- }
-
- /**
- * Describes metadata about a {@link MediaRouteService} which is providing
- * access to certain kinds of destinations.
- */
- public static final class ServiceMetadata {
- private final ServiceInfo mService;
- private CharSequence mLabel;
- private Drawable mIcon;
-
- ServiceMetadata(Service service) throws NameNotFoundException {
- mService = service.getPackageManager().getServiceInfo(
- new ComponentName(service, service.getClass()),
- PackageManager.GET_META_DATA);
- }
-
- ServiceMetadata(ServiceInfo service) {
- mService = service;
- }
-
- /**
- * Gets the service's component information including it name, label and icon.
- */
- public @NonNull ServiceInfo getService() {
- return mService;
- }
-
- /**
- * Gets the service's component name.
- */
- public @NonNull ComponentName getComponentName() {
- return new ComponentName(mService.packageName, mService.name);
- }
-
- /**
- * Gets the service's package name.
- */
- public @NonNull String getPackageName() {
- return mService.packageName;
- }
-
- /**
- * Gets the service's name for display to the user, or null if none.
- */
- public @NonNull CharSequence getLabel(@NonNull PackageManager pm) {
- if (mLabel == null) {
- mLabel = mService.loadLabel(pm);
- }
- return mLabel;
- }
-
- /**
- * Gets the icon drawable, or null if none.
- */
- public @Nullable Drawable getIcon(@NonNull PackageManager pm) {
- if (mIcon == null) {
- mIcon = mService.loadIcon(pm);
- }
- return mIcon;
- }
-
- // TODO: add service metadata
-
- Drawable getDrawable(PackageManager pm, int resid) {
- return pm.getDrawable(getPackageName(), resid, mService.applicationInfo);
- }
-
- @Override
- public @NonNull String toString() {
- return "ServiceInfo{ service=" + getComponentName().toShortString() + " }";
- }
- }
-
- /**
- * Describes a request to discover routes on behalf of an application.
- */
- public static final class DiscoveryRequest {
- private final ArrayList<MediaRouteSelector> mSelectors =
- new ArrayList<MediaRouteSelector>();
- private int mFlags;
-
- DiscoveryRequest(@NonNull List<MediaRouteSelector> selectors) {
- setSelectors(selectors);
- }
-
- /**
- * Sets the list of media route selectors to consider during discovery.
- */
- public void setSelectors(@NonNull List<MediaRouteSelector> selectors) {
- if (selectors == null) {
- throw new IllegalArgumentException("selectors");
- }
- mSelectors.clear();
- mSelectors.addAll(selectors);
- }
-
- /**
- * Gets the list of media route selectors to consider during discovery.
- */
- public @NonNull List<MediaRouteSelector> getSelectors() {
- return mSelectors;
- }
-
- /**
- * Gets discovery flags, such as {@link MediaRouter#DISCOVERY_FLAG_BACKGROUND}.
- */
- public @DiscoveryFlags int getFlags() {
- return mFlags;
- }
-
- /**
- * Sets discovery flags, such as {@link MediaRouter#DISCOVERY_FLAG_BACKGROUND}.
- */
- public void setFlags(@DiscoveryFlags int flags) {
- mFlags = flags;
- }
-
- @Override
- public @NonNull String toString() {
- return "DiscoveryRequest{ selectors=" + mSelectors
- + ", flags=0x" + Integer.toHexString(mFlags)
- + " }";
- }
- }
-
- /**
- * Describes a request to connect to a previously discovered route on
- * behalf of an application.
- */
- public static final class ConnectionRequest {
- private RouteInfo mRoute;
- private int mFlags;
- private Bundle mExtras;
-
- ConnectionRequest(@NonNull RouteInfo route) {
- setRoute(route);
- }
-
- /**
- * Gets the route to which to connect.
- */
- public @NonNull RouteInfo getRoute() {
- return mRoute;
- }
-
- /**
- * Sets the route to which to connect.
- */
- public void setRoute(@NonNull RouteInfo route) {
- if (route == null) {
- throw new IllegalArgumentException("route must not be null");
- }
- mRoute = route;
- }
-
- /**
- * Gets connection flags, such as {@link MediaRouter#CONNECTION_FLAG_BARGE}.
- */
- public @ConnectionFlags int getFlags() {
- return mFlags;
- }
-
- /**
- * Sets connection flags, such as {@link MediaRouter#CONNECTION_FLAG_BARGE}.
- */
- public void setFlags(@ConnectionFlags int flags) {
- mFlags = flags;
- }
-
- /**
- * Gets optional extras supplied by the application as part of the call to
- * connect, or null if none. The media route service may use this
- * information to configure the route during connection.
- */
- public @Nullable Bundle getExtras() {
- return mExtras;
- }
-
- /**
- * Sets optional extras supplied by the application as part of the call to
- * connect, or null if none. The media route service may use this
- * information to configure the route during connection.
- */
- public void setExtras(@Nullable Bundle extras) {
- mExtras = extras;
- }
-
- @Override
- public @NonNull String toString() {
- return "ConnectionRequest{ route=" + mRoute
- + ", flags=0x" + Integer.toHexString(mFlags)
- + ", extras=" + mExtras + " }";
- }
- }
-
- /**
- * Callback interface to specify policy for route discovery, filtering,
- * and connection establishment as well as observe media router state changes.
- */
- public static abstract class RoutingCallback extends StateCallback {
- /**
- * Called to prepare a discovery request object to specify the desired
- * media route selectors when the media router has been asked to start discovery.
- * <p>
- * By default, the discovery request contains all of the selectors which
- * have been added to the media router. Subclasses may override the list of
- * selectors by modifying the discovery request object before returning.
- * </p>
- *
- * @param request The discovery request object which may be modified by
- * this method to alter how discovery will be performed.
- * @param selectors The immutable list of media route selectors which were
- * added to the media router.
- * @return True to allow discovery to proceed or false to abort it.
- * By default, this methods returns true.
- */
- public boolean onPrepareDiscoveryRequest(@NonNull DiscoveryRequest request,
- @NonNull List<MediaRouteSelector> selectors) {
- return true;
- }
-
- /**
- * Called to prepare a connection request object to specify the desired
- * route and connection parameters when the media router has been asked to
- * connect to a particular destination.
- * <p>
- * By default, the connection request specifies the first available route
- * to the destination. Subclasses may override the route and destination
- * or set additional connection parameters by modifying the connection request
- * object before returning.
- * </p>
- *
- * @param request The connection request object which may be modified by
- * this method to alter how the connection will be established.
- * @param destination The destination to which the media router was asked
- * to connect.
- * @param routes The list of routes that belong to that destination sorted
- * in the same order as their matching media route selectors which were
- * used during discovery.
- * @return True to allow the connection to proceed or false to abort it.
- * By default, this methods returns true.
- */
- public boolean onPrepareConnectionRequest(
- @NonNull ConnectionRequest request,
- @NonNull DestinationInfo destination, @NonNull List<RouteInfo> routes) {
- return true;
- }
- }
-
- /**
- * Callback class to receive events from a {@link MediaRouter.Delegate}.
- */
- public static abstract class StateCallback {
- /**
- * Called when the media router has been released.
- */
- public void onReleased() { }
-
- /**
- * Called when the discovery state has changed.
- *
- * @param state The new discovery state: one of
- * {@link #DISCOVERY_STATE_STOPPED} or {@link #DISCOVERY_STATE_STARTED}.
- */
- public void onDiscoveryStateChanged(@DiscoveryState int state) { }
-
- /**
- * Called when the connection state has changed.
- *
- * @param state The new connection state: one of
- * {@link #CONNECTION_STATE_DISCONNECTED}, {@link #CONNECTION_STATE_CONNECTING}
- * or {@link #CONNECTION_STATE_CONNECTED}.
- */
- public void onConnectionStateChanged(@ConnectionState int state) { }
-
- /**
- * Called when the selected destination has changed.
- *
- * @param destination The new selected destination, or null if none.
- */
- public void onSelectedDestinationChanged(@Nullable DestinationInfo destination) { }
-
- /**
- * Called when route discovery has started.
- */
- public void onDiscoveryStarted() { }
-
- /**
- * Called when route discovery has stopped normally.
- * <p>
- * Abnormal termination is reported via {@link #onDiscoveryFailed}.
- * </p>
- */
- public void onDiscoveryStopped() { }
-
- /**
- * Called when discovery has failed in a non-recoverable manner.
- *
- * @param error The error code: one of
- * {@link MediaRouter#DISCOVERY_ERROR_UNKNOWN},
- * {@link MediaRouter#DISCOVERY_ERROR_ABORTED},
- * or {@link MediaRouter#DISCOVERY_ERROR_NO_CONNECTIVITY}.
- * @param message The localized error message, or null if none. This message
- * may be shown to the user.
- * @param extras Additional information about the error which a client
- * may use, or null if none.
- */
- public void onDiscoveryFailed(@DiscoveryError int error, @Nullable CharSequence message,
- @Nullable Bundle extras) { }
-
- /**
- * Called when a new destination is found or has changed during discovery.
- * <p>
- * Certain destinations may be omitted because they have been filtered
- * out by the media router's routing callback.
- * </p>
- *
- * @param destination The destination that was found.
- */
- public void onDestinationFound(@NonNull DestinationInfo destination) { }
-
- /**
- * Called when a destination is no longer reachable or is no longer
- * offering any routes that satisfy the discovery request.
- *
- * @param destination The destination that went away.
- */
- public void onDestinationLost(@NonNull DestinationInfo destination) { }
-
- /**
- * Called when a connection attempt begins.
- */
- public void onConnecting() { }
-
- /**
- * Called when the connection succeeds.
- */
- public void onConnected() { }
-
- /**
- * Called when the connection is terminated normally.
- * <p>
- * Abnormal termination is reported via {@link #onConnectionFailed}.
- * </p>
- */
- public void onDisconnected() { }
-
- /**
- * Called when a connection attempt or connection in
- * progress has failed in a non-recoverable manner.
- *
- * @param error The error code: one of
- * {@link MediaRouter#CONNECTION_ERROR_ABORTED},
- * {@link MediaRouter#CONNECTION_ERROR_UNAUTHORIZED},
- * {@link MediaRouter#CONNECTION_ERROR_UNREACHABLE},
- * {@link MediaRouter#CONNECTION_ERROR_BUSY},
- * {@link MediaRouter#CONNECTION_ERROR_TIMEOUT},
- * {@link MediaRouter#CONNECTION_ERROR_BROKEN},
- * or {@link MediaRouter#CONNECTION_ERROR_BARGED}.
- * @param message The localized error message, or null if none. This message
- * may be shown to the user.
- * @param extras Additional information about the error which a client
- * may use, or null if none.
- */
- public void onConnectionFailed(@ConnectionError int error,
- @Nullable CharSequence message, @Nullable Bundle extras) { }
- }
-}
diff --git a/media/java/android/media/routing/ParcelableConnectionInfo.aidl b/media/java/android/media/routing/ParcelableConnectionInfo.aidl
deleted file mode 100644
index 4a9ec94..0000000
--- a/media/java/android/media/routing/ParcelableConnectionInfo.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/* Copyright 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.media.routing;
-
-parcelable ParcelableConnectionInfo;
diff --git a/media/java/android/media/routing/ParcelableConnectionInfo.java b/media/java/android/media/routing/ParcelableConnectionInfo.java
deleted file mode 100644
index 45cfe9f..0000000
--- a/media/java/android/media/routing/ParcelableConnectionInfo.java
+++ /dev/null
@@ -1,71 +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.media.routing;
-
-import android.media.AudioAttributes;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Internal parcelable representation of a media route connection.
- */
-class ParcelableConnectionInfo implements Parcelable {
- public AudioAttributes audioAttributes;
- public int presentationDisplayId = -1;
- // todo: volume
- public IBinder[] protocolBinders;
- public Bundle extras;
-
- public static final Parcelable.Creator<ParcelableConnectionInfo> CREATOR =
- new Parcelable.Creator<ParcelableConnectionInfo>() {
- @Override
- public ParcelableConnectionInfo createFromParcel(Parcel source) {
- ParcelableConnectionInfo info = new ParcelableConnectionInfo();
- if (source.readInt() != 0) {
- info.audioAttributes = AudioAttributes.CREATOR.createFromParcel(source);
- }
- info.presentationDisplayId = source.readInt();
- info.protocolBinders = source.createBinderArray();
- info.extras = source.readBundle();
- return info;
- }
-
- @Override
- public ParcelableConnectionInfo[] newArray(int size) {
- return new ParcelableConnectionInfo[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- if (audioAttributes != null) {
- dest.writeInt(1);
- audioAttributes.writeToParcel(dest, flags);
- } else {
- dest.writeInt(0);
- }
- dest.writeInt(presentationDisplayId);
- dest.writeBinderArray(protocolBinders);
- dest.writeBundle(extras);
- }
-}
diff --git a/media/java/android/media/routing/ParcelableDestinationInfo.aidl b/media/java/android/media/routing/ParcelableDestinationInfo.aidl
deleted file mode 100644
index bf1c198..0000000
--- a/media/java/android/media/routing/ParcelableDestinationInfo.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/* Copyright 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.media.routing;
-
-parcelable ParcelableDestinationInfo;
diff --git a/media/java/android/media/routing/ParcelableDestinationInfo.java b/media/java/android/media/routing/ParcelableDestinationInfo.java
deleted file mode 100644
index eca5eec..0000000
--- a/media/java/android/media/routing/ParcelableDestinationInfo.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.media.routing;
-
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-/**
- * Internal parcelable representation of a media destination.
- */
-class ParcelableDestinationInfo implements Parcelable {
- public String id;
- public CharSequence name;
- public CharSequence description;
- public int iconResourceId;
- public Bundle extras;
-
- public static final Parcelable.Creator<ParcelableDestinationInfo> CREATOR =
- new Parcelable.Creator<ParcelableDestinationInfo>() {
- @Override
- public ParcelableDestinationInfo createFromParcel(Parcel source) {
- ParcelableDestinationInfo info = new ParcelableDestinationInfo();
- info.id = source.readString();
- info.name = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
- info.description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
- info.iconResourceId = source.readInt();
- info.extras = source.readBundle();
- return info;
- }
-
- @Override
- public ParcelableDestinationInfo[] newArray(int size) {
- return new ParcelableDestinationInfo[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(id);
- TextUtils.writeToParcel(name, dest, flags);
- TextUtils.writeToParcel(description, dest, flags);
- dest.writeInt(iconResourceId);
- dest.writeBundle(extras);
- }
-}
diff --git a/media/java/android/media/routing/ParcelableRouteInfo.aidl b/media/java/android/media/routing/ParcelableRouteInfo.aidl
deleted file mode 100644
index 126afaa..0000000
--- a/media/java/android/media/routing/ParcelableRouteInfo.aidl
+++ /dev/null
@@ -1,18 +0,0 @@
-/* Copyright 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.media.routing;
-
-parcelable ParcelableRouteInfo;
diff --git a/media/java/android/media/routing/ParcelableRouteInfo.java b/media/java/android/media/routing/ParcelableRouteInfo.java
deleted file mode 100644
index fb1a547..0000000
--- a/media/java/android/media/routing/ParcelableRouteInfo.java
+++ /dev/null
@@ -1,64 +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.media.routing;
-
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Internal parcelable representation of a media route.
- */
-class ParcelableRouteInfo implements Parcelable {
- public String id;
- public int selectorIndex; // index of selector within list used for discovery
- public int features;
- public String[] protocols;
- public Bundle extras;
-
- public static final Parcelable.Creator<ParcelableRouteInfo> CREATOR =
- new Parcelable.Creator<ParcelableRouteInfo>() {
- @Override
- public ParcelableRouteInfo createFromParcel(Parcel source) {
- ParcelableRouteInfo info = new ParcelableRouteInfo();
- info.id = source.readString();
- info.selectorIndex = source.readInt();
- info.features = source.readInt();
- info.protocols = source.createStringArray();
- info.extras = source.readBundle();
- return info;
- }
-
- @Override
- public ParcelableRouteInfo[] newArray(int size) {
- return new ParcelableRouteInfo[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(id);
- dest.writeInt(selectorIndex);
- dest.writeInt(features);
- dest.writeStringArray(protocols);
- dest.writeBundle(extras);
- }
-}
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index af3b72e..bd0019f 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -19,7 +19,6 @@
import android.content.pm.ParceledListSlice;
import android.media.AudioAttributes;
import android.media.MediaMetadata;
-import android.media.routing.IMediaRouter;
import android.media.session.ISessionController;
import android.media.session.PlaybackState;
import android.media.session.MediaSession;
@@ -35,7 +34,6 @@
ISessionController getController();
void setFlags(int flags);
void setActive(boolean active);
- void setMediaRouter(in IMediaRouter router);
void setMediaButtonReceiver(in PendingIntent mbr);
void setLaunchPendingIntent(in PendingIntent pi);
void destroy();
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index 8d58a60..285e5f7 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -20,8 +20,6 @@
import android.content.pm.ParceledListSlice;
import android.media.MediaMetadata;
import android.media.Rating;
-import android.media.routing.IMediaRouterDelegate;
-import android.media.routing.IMediaRouterStateCallback;
import android.media.session.ISessionControllerCallback;
import android.media.session.MediaSession;
import android.media.session.ParcelableVolumeInfo;
@@ -51,8 +49,6 @@
void adjustVolume(int direction, int flags, String packageName);
void setVolumeTo(int value, int flags, String packageName);
- IMediaRouterDelegate createMediaRouterDelegate(IMediaRouterStateCallback callback);
-
// These commands are for the TransportControls
void play();
void playFromMediaId(String mediaId, in Bundle extras);
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index dd81a22..2acee04 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -26,7 +26,6 @@
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.VolumeProvider;
-import android.media.routing.MediaRouter;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -120,17 +119,6 @@
}
/**
- * Creates a media router delegate through which the destination of the media
- * router may be observed and controlled.
- *
- * @return The media router delegate, or null if the media session does
- * not support media routing.
- */
- public @Nullable MediaRouter.Delegate createMediaRouterDelegate() {
- return new MediaRouter.Delegate();
- }
-
- /**
* Send the specified media button event to the session. Only media keys can
* be sent by this method, other keys will be ignored.
*
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index cee82b4..e1e9b79 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -29,7 +29,6 @@
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.VolumeProvider;
-import android.media.routing.MediaRouter;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -225,23 +224,6 @@
}
/**
- * Associates a {@link MediaRouter} with this session to control the destination
- * of media content.
- * <p>
- * A media router may only be associated with at most one session at a time.
- * </p>
- *
- * @param router The media router, or null to remove the current association.
- */
- public void setMediaRouter(@Nullable MediaRouter router) {
- try {
- mBinder.setMediaRouter(router != null ? router.getBinder() : null);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e);
- }
- }
-
- /**
* Set a pending intent for your media button receiver to allow restarting
* playback after the session has been stopped. If your app is started in
* this way an {@link Intent#ACTION_MEDIA_BUTTON} intent will be sent via
diff --git a/media/jni/android_media_MediaSync.h b/media/jni/android_media_MediaSync.h
index fa5e5e0..5823057 100644
--- a/media/jni/android_media_MediaSync.h
+++ b/media/jni/android_media_MediaSync.h
@@ -26,7 +26,7 @@
struct AudioPlaybackRate;
class AudioTrack;
-struct IGraphicBufferProducer;
+class IGraphicBufferProducer;
struct MediaClock;
class MediaSync;
diff --git a/media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplayProvider.java b/media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplayProvider.java
index e2df77c..4d3edb8 100644
--- a/media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplayProvider.java
+++ b/media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplayProvider.java
@@ -287,7 +287,7 @@
*/
public PendingIntent getSettingsPendingIntent() {
if (mSettingsPendingIntent == null) {
- Intent settingsIntent = new Intent(Settings.ACTION_WIFI_DISPLAY_SETTINGS);
+ Intent settingsIntent = new Intent(Settings.ACTION_CAST_SETTINGS);
settingsIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
| Intent.FLAG_ACTIVITY_CLEAR_TOP);
diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp
index 4e7c6be..26b41e8 100644
--- a/native/android/sensor.cpp
+++ b/native/android/sensor.cpp
@@ -35,9 +35,27 @@
using android::SensorManager;
using android::SensorEventQueue;
using android::String8;
+using android::String16;
/*****************************************************************************/
+android::Mutex android::SensorManager::sLock;
+std::map<String16, SensorManager*> android::SensorManager::sPackageInstances;
+
+ASensorManager* ASensorManager_getInstance()
+{
+ return ASensorManager_getInstanceForPackage(NULL);
+}
+
+ASensorManager* ASensorManager_getInstanceForPackage(const char* packageName)
+{
+ if (packageName) {
+ return &SensorManager::getInstanceForPackage(String16(packageName));
+ } else {
+ return &SensorManager::getInstanceForPackage(String16());
+ }
+}
+
int ASensorManager_getSensorList(ASensorManager* manager,
ASensorList* list)
{
diff --git a/native/graphics/jni/bitmap.cpp b/native/graphics/jni/bitmap.cpp
index 0521833..6d2de98 100644
--- a/native/graphics/jni/bitmap.cpp
+++ b/native/graphics/jni/bitmap.cpp
@@ -62,7 +62,7 @@
return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
}
- SkPixelRef* pixelRef = GraphicsJNI::getSkPixelRef(env, jbitmap);
+ SkPixelRef* pixelRef = GraphicsJNI::refSkPixelRef(env, jbitmap);
if (!pixelRef) {
return ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
}
@@ -71,9 +71,9 @@
void* addr = pixelRef->pixels();
if (NULL == addr) {
pixelRef->unlockPixels();
+ pixelRef->unref();
return ANDROID_BITMAP_RESULT_ALLOCATION_FAILED;
}
- pixelRef->ref();
if (addrPtr) {
*addrPtr = addr;
@@ -86,7 +86,7 @@
return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
}
- SkPixelRef* pixelRef = GraphicsJNI::getSkPixelRef(env, jbitmap);
+ SkPixelRef* pixelRef = GraphicsJNI::refSkPixelRef(env, jbitmap);
if (!pixelRef) {
return ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
}
@@ -98,6 +98,12 @@
pixelRef->notifyPixelsChanged();
pixelRef->unlockPixels();
+ // Awkward in that we need to double-unref as the call to get the SkPixelRef
+ // did a ref(), so we need to unref() for the local ref and for the previous
+ // AndroidBitmap_lockPixels(). However this keeps GraphicsJNI a bit safer
+ // if others start using it without knowing about android::Bitmap's "fun"
+ // ref counting mechanism(s).
+ pixelRef->unref();
pixelRef->unref();
return ANDROID_BITMAP_RESULT_SUCCESS;
diff --git a/packages/CaptivePortalLogin/res/layout/activity_captive_portal_login.xml b/packages/CaptivePortalLogin/res/layout/activity_captive_portal_login.xml
index a11bed0..2f0a411 100644
--- a/packages/CaptivePortalLogin/res/layout/activity_captive_portal_login.xml
+++ b/packages/CaptivePortalLogin/res/layout/activity_captive_portal_login.xml
@@ -10,6 +10,13 @@
android:layout_height="match_parent"
android:orientation="vertical" >
+ <TextView
+ android:id="@+id/url_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="20sp"
+ android:singleLine="true" />
+
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="match_parent"
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index b86fc4b..1019e6c 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -35,13 +35,13 @@
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.View;
import android.webkit.SslErrorHandler;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;
+import android.widget.TextView;
import java.io.IOException;
import java.net.HttpURLConnection;
@@ -221,6 +221,7 @@
}
private class MyWebViewClient extends WebViewClient {
+ private static final String INTERNAL_ASSETS = "file:///android_asset/";
private boolean firstPageLoad = true;
@Override
@@ -240,6 +241,12 @@
view.loadUrl(mURL.toString());
return;
}
+ // For internally generated pages, leave URL bar listing prior URL as this is the URL
+ // the page refers to.
+ if (!url.startsWith(INTERNAL_ASSETS)) {
+ final TextView myUrlBar = (TextView) findViewById(R.id.url_bar);
+ myUrlBar.setText(url);
+ }
testForCaptivePortal();
}
@@ -252,17 +259,15 @@
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
Log.w(TAG, "SSL error; displaying broken lock icon.");
- view.loadDataWithBaseURL("file:///android_asset/", SSL_ERROR_HTML, "text/HTML",
- "UTF-8", null);
+ view.loadDataWithBaseURL(INTERNAL_ASSETS, SSL_ERROR_HTML, "text/HTML", "UTF-8", null);
}
}
private class MyWebChromeClient extends WebChromeClient {
@Override
public void onProgressChanged(WebView view, int newProgress) {
- ProgressBar myProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
+ final ProgressBar myProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
myProgressBar.setProgress(newProgress);
- myProgressBar.setVisibility(newProgress == 100 ? View.GONE : View.VISIBLE);
}
}
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
index b5eda90..1c4b963 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
@@ -16,6 +16,7 @@
package com.android.keyguard;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
@@ -25,12 +26,15 @@
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
+import android.view.ViewHierarchyEncoder;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ViewFlipper;
import com.android.internal.widget.LockPatternUtils;
+import java.lang.Override;
+
/**
* Subclass of the current view flipper that allows us to overload dispatchTouchEvent() so
* we can emulate {@link WindowManager.LayoutParams#FLAG_SLIPPERY} within a view hierarchy.
@@ -268,5 +272,14 @@
R.styleable.KeyguardSecurityViewFlipper_Layout_layout_maxHeight, 0);
a.recycle();
}
+
+ /** @hide */
+ @Override
+ protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
+ super.encodeProperties(encoder);
+
+ encoder.addProperty("layout:maxWidth", maxWidth);
+ encoder.addProperty("layout:maxHeight", maxHeight);
+ }
}
}
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
index 595c9ed..0264f3d 100644
--- a/packages/SystemUI/res/layout/zen_mode_panel.xml
+++ b/packages/SystemUI/res/layout/zen_mode_panel.xml
@@ -52,7 +52,7 @@
android:layout_alignParentEnd="true"
android:background="@drawable/btn_borderless_rect"
android:clickable="true"
- android:contentDescription="@string/accessibility_desc_confirm"
+ android:contentDescription="@string/accessibility_desc_close"
android:scaleType="center"
android:src="@drawable/ic_close"
android:tint="@android:color/white" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8466a5a..8606a59 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -424,8 +424,8 @@
<string name="accessibility_desc_settings">Settings</string>
<!-- Content description for the recent apps panel (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_desc_recent_apps">Overview.</string>
- <!-- Content description for the confirm button in the zen mode panel introduction message. [CHAR LIMIT=NONE] -->
- <string name="accessibility_desc_confirm">Confirm</string>
+ <!-- Content description for the close button in the zen mode panel introduction message. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_desc_close">Close</string>
<!-- Content description of the user tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_user">User <xliff:g id="user" example="John Doe">%s</xliff:g>.</string>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index b5c1ca8..f352849 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -33,6 +33,7 @@
import android.widget.ImageView;
import android.widget.TextView;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile.DetailAdapter;
@@ -182,8 +183,11 @@
public void setExpanded(boolean expanded) {
if (mExpanded == expanded) return;
mExpanded = expanded;
+ MetricsLogger.visibility(mContext, MetricsLogger.QS_PANEL, mExpanded);
if (!mExpanded) {
closeDetail();
+ } else {
+ logTiles();
}
}
@@ -365,9 +369,11 @@
mDetailContent.removeAllViews();
mDetail.bringToFront();
mDetailContent.addView(r.detailView);
+ MetricsLogger.visible(mContext, detailAdapter.getMetricsCategory());
setDetailRecord(r);
listener = mHideGridContentWhenDone;
} else {
+ MetricsLogger.hidden(mContext, mDetailRecord.detailAdapter.getMetricsCategory());
mClosingDetail = true;
setGridContentVisibility(true);
listener = mTeardownDetailWhenDone;
@@ -387,9 +393,21 @@
}
}
mBrightnessView.setVisibility(newVis);
+ if (mGridContentVisible != visible) {
+ MetricsLogger.visibility(mContext, MetricsLogger.QS_PANEL, newVis);
+ }
mGridContentVisible = visible;
}
+ private void logTiles() {
+ for (int i = 0; i < mRecords.size(); i++) {
+ TileRecord tileRecord = mRecords.get(i);
+ if (tileRecord.tile.getState().visible) {
+ MetricsLogger.visible(mContext, tileRecord.tile.getMetricsCategory());
+ }
+ }
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = MeasureSpec.getSize(widthMeasureSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index b9574dc..452fd44 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -29,6 +29,7 @@
import android.view.View;
import android.view.ViewGroup;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.qs.QSTile.State;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.CastController;
@@ -66,9 +67,17 @@
private boolean mAnnounceNextStateChange;
abstract protected TState newTileState();
- abstract protected void handleClick();
abstract protected void handleUpdateState(TState state, Object arg);
+ /**
+ * Declare the category of this tile.
+ *
+ * Categories are defined in {@link com.android.internal.logging.MetricsLogger}
+ * or if there is no relevant existing category you may define one in
+ * {@link com.android.systemui.qs.QSTile}.
+ */
+ abstract public int getMetricsCategory();
+
protected QSTile(Host host) {
mHost = host;
mContext = host.getContext();
@@ -97,6 +106,7 @@
View createDetailView(Context context, View convertView, ViewGroup parent);
Intent getSettingsIntent();
void setToggleState(boolean state);
+ int getMetricsCategory();
}
// safe to call from any thread
@@ -160,6 +170,10 @@
handleRefreshState(null);
}
+ protected void handleClick() {
+ MetricsLogger.action(mContext, getMetricsCategory(), getMetricsPackage());
+ };
+
protected void handleSecondaryClick() {
// optional
}
@@ -168,6 +182,10 @@
// optional
}
+ protected String getMetricsPackage() {
+ return "";
+ }
+
protected void handleRefreshState(Object arg) {
handleUpdateState(mTmpState, arg);
final boolean changed = mTmpState.copyTo(mState);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index 2bc31fc..6744154 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -23,6 +23,7 @@
import android.net.ConnectivityManager;
import android.provider.Settings.Global;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.qs.GlobalSetting;
import com.android.systemui.qs.QSTile;
@@ -55,6 +56,7 @@
@Override
public void handleClick() {
+ super.handleClick();
setEnabled(!mState.value);
mEnable.setAllowAnimation(true);
mDisable.setAllowAnimation(true);
@@ -85,6 +87,11 @@
}
@Override
+ public int getMetricsCategory() {
+ return MetricsLogger.QS_AIRPLANEMODE;
+ }
+
+ @Override
protected String composeChangeAnnouncement() {
if (mState.value) {
return mContext.getString(R.string.accessibility_quick_settings_airplane_changed_on);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index b42b5f6..8eb624f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -25,6 +25,7 @@
import android.view.View;
import android.view.ViewGroup;
+import com.android.internal.logging.MetricsLogger;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.systemui.R;
import com.android.systemui.qs.QSDetailItems;
@@ -74,6 +75,7 @@
@Override
protected void handleClick() {
+ super.handleClick();
final boolean isEnabled = (Boolean)mState.value;
mController.setBluetoothEnabled(!isEnabled);
}
@@ -132,6 +134,11 @@
}
@Override
+ public int getMetricsCategory() {
+ return MetricsLogger.QS_BLUETOOTH;
+ }
+
+ @Override
protected String composeChangeAnnouncement() {
if (mState.value) {
return mContext.getString(R.string.accessibility_quick_settings_bluetooth_changed_on);
@@ -182,6 +189,11 @@
}
@Override
+ public int getMetricsCategory() {
+ return MetricsLogger.QS_BLUETOOTH_DETAILS;
+ }
+
+ @Override
public View createDetailView(Context context, View convertView, ViewGroup parent) {
mItems = QSDetailItems.convertOrInflate(context, convertView, parent);
mItems.setTagSuffix("Bluetooth");
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 5bf6fb5..a3d7bcc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -24,6 +24,7 @@
import android.view.View.OnAttachStateChangeListener;
import android.view.ViewGroup;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.qs.QSDetailItems;
import com.android.systemui.qs.QSDetailItems.Item;
@@ -85,6 +86,7 @@
@Override
protected void handleClick() {
+ super.handleClick();
showDetail(true);
}
@@ -113,6 +115,11 @@
}
@Override
+ public int getMetricsCategory() {
+ return MetricsLogger.QS_CAST;
+ }
+
+ @Override
protected String composeChangeAnnouncement() {
if (!mState.value) {
// We only announce when it's turned off to avoid vocal overflow.
@@ -164,6 +171,11 @@
}
@Override
+ public int getMetricsCategory() {
+ return MetricsLogger.QS_CAST_DETAILS;
+ }
+
+ @Override
public View createDetailView(Context context, View convertView, ViewGroup parent) {
mItems = QSDetailItems.convertOrInflate(context, convertView, parent);
mItems.setTagSuffix("Cast");
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 30f92b9..0026141 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -24,6 +24,7 @@
import android.view.View;
import android.view.ViewGroup;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.QSTileView;
@@ -75,6 +76,7 @@
@Override
protected void handleClick() {
+ super.handleClick();
if (mDataController.isMobileDataSupported()) {
showDetail(true);
} else {
@@ -118,6 +120,11 @@
state.label);
}
+ @Override
+ public int getMetricsCategory() {
+ return MetricsLogger.QS_CELLULAR;
+ }
+
// Remove the period from the network name
public static String removeTrailingPeriod(String string) {
if (string == null) return null;
@@ -227,6 +234,11 @@
}
@Override
+ public int getMetricsCategory() {
+ return MetricsLogger.QS_DATAUSAGEDETAIL;
+ }
+
+ @Override
public View createDetailView(Context context, View convertView, ViewGroup parent) {
final DataUsageDetailView v = (DataUsageDetailView) (convertView != null
? convertView
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index 4a33f55..6fa094e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -18,6 +18,7 @@
import android.provider.Settings.Secure;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
@@ -86,6 +87,7 @@
@Override
protected void handleClick() {
+ super.handleClick();
mSetting.setValue(mState.value ? 0 : 1);
mEnable.setAllowAnimation(true);
mDisable.setAllowAnimation(true);
@@ -115,6 +117,11 @@
}
@Override
+ public int getMetricsCategory() {
+ return MetricsLogger.QS_COLORINVERSION;
+ }
+
+ @Override
protected String composeChangeAnnouncement() {
if (mState.value) {
return mContext.getString(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 5145bc7..e708a72 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -29,6 +29,7 @@
import android.view.View.OnAttachStateChangeListener;
import android.view.ViewGroup;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
@@ -88,6 +89,7 @@
@Override
public void handleClick() {
+ super.handleClick();
if (mState.value) {
mController.setZen(Global.ZEN_MODE_OFF, null, TAG);
} else {
@@ -135,6 +137,11 @@
}
@Override
+ public int getMetricsCategory() {
+ return MetricsLogger.QS_DND;
+ }
+
+ @Override
protected String composeChangeAnnouncement() {
if (mState.value) {
return mContext.getString(R.string.accessibility_quick_settings_dnd_changed_on);
@@ -209,6 +216,11 @@
}
@Override
+ public int getMetricsCategory() {
+ return MetricsLogger.QS_DND_DETAILS;
+ }
+
+ @Override
public View createDetailView(Context context, View convertView, ViewGroup parent) {
final ZenModePanel zmp = convertView != null ? (ZenModePanel) convertView
: (ZenModePanel) LayoutInflater.from(context).inflate(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index cb78deb..a1f3cde 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -18,6 +18,7 @@
import android.app.ActivityManager;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.policy.FlashlightController;
@@ -59,6 +60,7 @@
@Override
protected void handleClick() {
+ super.handleClick();
if (ActivityManager.isUserAMonkey()) {
return;
}
@@ -84,6 +86,11 @@
}
@Override
+ public int getMetricsCategory() {
+ return MetricsLogger.QS_FLASHLIGHT;
+ }
+
+ @Override
protected String composeChangeAnnouncement() {
if (mState.value) {
return mContext.getString(R.string.accessibility_quick_settings_flashlight_changed_on);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 6063f80..b864ff4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.Intent;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.qs.UsageTracker;
@@ -68,6 +69,7 @@
@Override
protected void handleClick() {
+ super.handleClick();
final boolean isEnabled = (Boolean) mState.value;
mController.setHotspotEnabled(!isEnabled);
mEnable.setAllowAnimation(true);
@@ -97,6 +99,11 @@
}
@Override
+ public int getMetricsCategory() {
+ return MetricsLogger.QS_HOTSPOT;
+ }
+
+ @Override
protected String composeChangeAnnouncement() {
if (mState.value) {
return mContext.getString(R.string.accessibility_quick_settings_hotspot_changed_on);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
index 2736530..20b5f04 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
@@ -29,6 +29,7 @@
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.qs.QSTile;
import java.util.Arrays;
@@ -42,6 +43,7 @@
private PendingIntent mOnLongClick;
private String mOnLongClickUri;
private int mCurrentUserId;
+ private String mIntentPackage;
private IntentTile(Host host, String action) {
super(host);
@@ -82,6 +84,7 @@
@Override
protected void handleClick() {
+ super.handleClick();
sendIntent("click", mOnClick, mOnClickUri);
}
@@ -133,6 +136,17 @@
mOnClickUri = intent.getStringExtra("onClickUri");
mOnLongClick = intent.getParcelableExtra("onLongClick");
mOnLongClickUri = intent.getStringExtra("onLongClickUri");
+ mIntentPackage = intent.getStringExtra("package");
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsLogger.QS_INTENT;
+ }
+
+ @Override
+ protected String getMetricsPackage() {
+ return mIntentPackage == null ? "" : mIntentPackage;
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 11ec722..ab22ada 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -58,6 +59,7 @@
@Override
protected void handleClick() {
+ super.handleClick();
final boolean wasEnabled = (Boolean) mState.value;
mController.setLocationEnabled(!wasEnabled);
mEnable.setAllowAnimation(true);
@@ -87,6 +89,11 @@
}
@Override
+ public int getMetricsCategory() {
+ return MetricsLogger.QS_LOCATION;
+ }
+
+ @Override
protected String composeChangeAnnouncement() {
if (mState.value) {
return mContext.getString(R.string.accessibility_quick_settings_location_changed_on);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index f46b9a6..7e3fe76 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -18,6 +18,7 @@
import android.content.res.Configuration;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.policy.RotationLockController;
@@ -58,6 +59,7 @@
@Override
protected void handleClick() {
+ super.handleClick();
if (mController == null) return;
final boolean newState = !mState.value;
mController.setRotationLocked(newState);
@@ -92,6 +94,11 @@
R.string.accessibility_rotation_lock_off);
}
+ @Override
+ public int getMetricsCategory() {
+ return MetricsLogger.QS_ROTATIONLOCK;
+ }
+
/**
* Get the correct accessibility string based on the state
*
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index d589366..228c293 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -26,6 +26,7 @@
import android.view.View;
import android.view.ViewGroup;
+import com.android.internal.logging.MetricsLogger;
import com.android.settingslib.wifi.AccessPoint;
import com.android.systemui.R;
import com.android.systemui.qs.QSDetailItems;
@@ -93,6 +94,7 @@
@Override
protected void handleClick() {
+ super.handleClick();
mState.copyTo(mStateBeforeClick);
mController.setWifiEnabled(!mState.enabled);
}
@@ -159,6 +161,11 @@
}
@Override
+ public int getMetricsCategory() {
+ return MetricsLogger.QS_WIFI;
+ }
+
+ @Override
protected boolean shouldAnnouncementBeDelayed() {
return mStateBeforeClick.enabled == mState.enabled;
}
@@ -274,6 +281,11 @@
}
@Override
+ public int getMetricsCategory() {
+ return MetricsLogger.QS_WIFI_DETAILS;
+ }
+
+ @Override
public View createDetailView(Context context, View convertView, ViewGroup parent) {
if (DEBUG) Log.d(TAG, "createDetailView convertView=" + (convertView != null));
mAccessPoints = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 4542054..80fdd28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -157,7 +157,8 @@
public void setSystemUiVisibility(int vis, int mask) {
synchronized (mList) {
- mHandler.removeMessages(MSG_SET_SYSTEMUI_VISIBILITY);
+ // Don't coalesce these, since it might have one time flags set such as
+ // STATUS_BAR_UNHIDE which might get lost.
mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, vis, mask, null).sendToTarget();
}
}
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 351977b..471196c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -529,6 +529,7 @@
= new HashMap<>();
private HashSet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new HashSet<>();
private RankingMap mLatestRankingMap;
+ private boolean mNoAnimationOnNextBarModeChange;
@Override
public void start() {
@@ -1709,6 +1710,7 @@
* State is one or more of the DISABLE constants from StatusBarManager.
*/
public void disable(int state1, int state2, boolean animate) {
+ animate &= mStatusBarWindowState != WINDOW_STATE_HIDDEN;
mDisabledUnmodified1 = state1;
mDisabledUnmodified2 = state2;
state1 = adjustDisableFlags(state1);
@@ -1868,6 +1870,7 @@
public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
if (inPinnedMode) {
mStatusBarWindowManager.setHeadsUpShowing(true);
+ mStatusBarWindowManager.setForceStatusBarVisible(true);
} else {
Runnable endRunnable = new Runnable() {
@Override
@@ -2132,6 +2135,7 @@
// Shrink the window to the size of the status bar only
mStatusBarWindowManager.setStatusBarExpanded(false);
+ mStatusBarWindowManager.setForceStatusBarVisible(false);
mStatusBarView.setFocusable(true);
// Close any "App info" popups that might have snuck on-screen
@@ -2272,6 +2276,12 @@
setAreThereNotifications();
}
+ // ready to unhide
+ if ((vis & View.STATUS_BAR_UNHIDE) != 0) {
+ mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE;
+ mNoAnimationOnNextBarModeChange = true;
+ }
+
// update status bar mode
final int sbMode = computeBarMode(oldVal, newVal, mStatusBarView.getBarTransitions(),
View.STATUS_BAR_TRANSIENT, View.STATUS_BAR_TRANSLUCENT);
@@ -2303,10 +2313,6 @@
}
}
- // ready to unhide
- if ((vis & View.STATUS_BAR_UNHIDE) != 0) {
- mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE;
- }
if ((vis & View.NAVIGATION_BAR_UNHIDE) != 0) {
mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
}
@@ -2351,17 +2357,21 @@
private void checkBarModes() {
if (mDemoMode) return;
- checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarView.getBarTransitions());
+ checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarView.getBarTransitions(),
+ mNoAnimationOnNextBarModeChange);
if (mNavigationBarView != null) {
checkBarMode(mNavigationBarMode,
- mNavigationBarWindowState, mNavigationBarView.getBarTransitions());
+ mNavigationBarWindowState, mNavigationBarView.getBarTransitions(),
+ mNoAnimationOnNextBarModeChange);
}
+ mNoAnimationOnNextBarModeChange = false;
}
- private void checkBarMode(int mode, int windowState, BarTransitions transitions) {
+ private void checkBarMode(int mode, int windowState, BarTransitions transitions,
+ boolean noAnimation) {
final boolean powerSave = mBatteryController.isPowerSave();
- final boolean anim = (mScreenOn == null || mScreenOn) && windowState != WINDOW_STATE_HIDDEN
- && !powerSave;
+ final boolean anim = !noAnimation && (mScreenOn == null || mScreenOn)
+ && windowState != WINDOW_STATE_HIDDEN && !powerSave;
if (powerSave && getBarState() == StatusBarState.SHADE) {
mode = MODE_WARNING;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index 84a9f64..e7e4384 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -166,6 +166,7 @@
private void apply(State state) {
applyKeyguardFlags(state);
+ applyForceStatusBarVisibleFlag(state);
applyFocusableFlag(state);
adjustScreenOrientation(state);
applyHeight(state);
@@ -178,6 +179,16 @@
}
}
+ private void applyForceStatusBarVisibleFlag(State state) {
+ if (state.forceStatusBarVisible) {
+ mLpChanged.privateFlags |= WindowManager
+ .LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
+ } else {
+ mLpChanged.privateFlags &= ~WindowManager
+ .LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
+ }
+ }
+
private void applyModalFlag(State state) {
if (state.headsUpShowing) {
mLpChanged.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
@@ -240,6 +251,11 @@
apply(mCurrentState);
}
+ public void setForceStatusBarVisible(boolean forceStatusBarVisible) {
+ mCurrentState.forceStatusBarVisible = forceStatusBarVisible;
+ apply(mCurrentState);
+ }
+
private static class State {
boolean keyguardShowing;
boolean keyguardOccluded;
@@ -250,6 +266,7 @@
boolean keyguardFadingAway;
boolean qsExpanded;
boolean headsUpShowing;
+ boolean forceStatusBarVisible;
/**
* The {@link BaseStatusBar} state from the status bar.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 194bcfa..ad27c6e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -43,6 +43,7 @@
import android.view.ViewGroup;
import android.widget.BaseAdapter;
+import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.UserIcons;
import com.android.systemui.BitmapHelper;
import com.android.systemui.GuestResumeSessionReceiver;
@@ -548,6 +549,11 @@
@Override
public void setToggleState(boolean state) {
}
+
+ @Override
+ public int getMetricsCategory() {
+ return MetricsLogger.QS_USERDETAIL;
+ }
};
private final KeyguardMonitor.Callback mCallback = new KeyguardMonitor.Callback() {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index 7115897..1e34663 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -788,6 +788,7 @@
public void onConfigurationChanged() {
updateWindowWidthH();
mSpTexts.update();
+ mZenFooter.onConfigurationChanged();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
index 8aded45..ccb2b5a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
@@ -36,10 +36,11 @@
private static final String TAG = Util.logTag(ZenFooter.class);
private final Context mContext;
+ private final SpTexts mSpTexts;
private TextView mSummaryLine1;
private TextView mSummaryLine2;
- private View mEndNowButton;
+ private TextView mEndNowButton;
private int mZen = -1;
private ZenModeConfig mConfig;
private ZenModeController mController;
@@ -47,6 +48,7 @@
public ZenFooter(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
+ mSpTexts = new SpTexts(mContext);
setLayoutTransition(new LayoutTransition());
}
@@ -55,7 +57,10 @@
super.onFinishInflate();
mSummaryLine1 = (TextView) findViewById(R.id.volume_zen_summary_line_1);
mSummaryLine2 = (TextView) findViewById(R.id.volume_zen_summary_line_2);
- mEndNowButton = findViewById(R.id.volume_zen_end_now);
+ mEndNowButton = (TextView) findViewById(R.id.volume_zen_end_now);
+ mSpTexts.add(mSummaryLine1);
+ mSpTexts.add(mSummaryLine2);
+ mSpTexts.add(mEndNowButton);
}
public void init(final ZenModeController controller) {
@@ -122,4 +127,8 @@
Util.setText(mSummaryLine2, line2);
}
+ public void onConfigurationChanged() {
+ mSpTexts.update();
+ }
+
}
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 58d0fce..a49fb76 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -1857,7 +1857,7 @@
jintArray limits)
{
if (kLogApi) {
- ALOGD("nScriptForEach, con(%p), s(%p), slot(%i) ains(%p) aout(%lli)", (RsContext)con, (void *)script, slot, ains, aout);
+ ALOGD("nScriptForEach, con(%p), s(%p), slot(%i) ains(%p) aout(%" PRId64 ")", (RsContext)con, (void *)script, slot, ains, aout);
}
jint in_len = 0;
diff --git a/services/core/java/com/android/server/AssetAtlasService.java b/services/core/java/com/android/server/AssetAtlasService.java
index 26f4232..ebc810f 100644
--- a/services/core/java/com/android/server/AssetAtlasService.java
+++ b/services/core/java/com/android/server/AssetAtlasService.java
@@ -119,7 +119,6 @@
// long0: SkBitmap*, the native bitmap object
// long1: x position
// long2: y position
- // long3: rotated, 1 if the bitmap must be rotated, 0 otherwise
private long[] mAtlasMap;
/**
@@ -236,7 +235,7 @@
/**
* Renders a list of bitmaps into the atlas. The position of each bitmap
* was decided by the packing algorithm and will be honored by this
- * method. If need be this method will also rotate bitmaps.
+ * method.
*
* @param buffer The buffer to render the atlas entries into
* @param atlas The atlas to pack the bitmaps into
@@ -280,16 +279,11 @@
canvas.save();
canvas.translate(entry.x, entry.y);
- if (entry.rotated) {
- canvas.translate(bitmap.getHeight(), 0.0f);
- canvas.rotate(90.0f);
- }
canvas.drawBitmap(bitmap, 0.0f, 0.0f, null);
canvas.restore();
atlasMap[mapIndex++] = bitmap.refSkPixelRef();
atlasMap[mapIndex++] = entry.x;
atlasMap[mapIndex++] = entry.y;
- atlasMap[mapIndex++] = entry.rotated ? 1 : 0;
}
}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 1019faa..32b91d2 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -512,7 +512,8 @@
private void onBluetoothGattServiceUp() {
if (DBG) Log.d(TAG,"BluetoothGatt Service is Up");
try{
- if (isBleAppPresent() == false && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
+ if (isBleAppPresent() == false && mBluetooth != null
+ && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
mBluetooth.onLeServiceUp();
// waive WRITE_SECURE_SETTINGS permission check
@@ -531,32 +532,26 @@
*/
private void sendBrEdrDownCallback() {
if (DBG) Log.d(TAG,"Calling sendBrEdrDownCallback callbacks");
- int n = mCallbacks.beginBroadcast();
+
+ if(mBluetooth == null) {
+ Log.w(TAG, "Bluetooth handle is null");
+ return;
+ }
if (isBleAppPresent() == false) {
try {
mBluetooth.onBrEdrDown();
} catch(RemoteException e) {
- Log.e(TAG,"Unable to call onBrEdrDown", e);
+ Log.e(TAG, "Call to onBrEdrDown() failed.", e);
}
- }
- else{//need to stay at BLE ON. disconnect all Gatt connections
+ } else {
+ // Need to stay at BLE ON. Disconnect all Gatt connections
try{
- mBluetoothGatt.unregAll();//disconnectAll();
+ mBluetoothGatt.unregAll();
} catch(RemoteException e) {
- Log.e(TAG,"Unable to disconn all", e);
+ Log.e(TAG, "Unable to disconnect all apps.", e);
}
}
-
- Log.d(TAG,"Broadcasting onBrEdrDown() to " + n + " receivers.");
- for (int i=0; i <n; i++) {
- try {
- mCallbacks.getBroadcastItem(i).onBrEdrDown();
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to call sendBrEdrDownCallback() on callback #" + i, e);
- }
- }
- mCallbacks.finishBroadcast();
}
/** @hide*/
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 8d1d124..1dc2d7e71 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -190,7 +190,7 @@
/** Set of ifaces that are costly. */
private HashSet<String> mMeteredIfaces = Sets.newHashSet();
- private Context mContext;
+ final private Context mContext;
private int mNetworkPreference;
// 0 is full bad, 100 is full good
private int mDefaultInetConditionPublished = 0;
@@ -344,6 +344,11 @@
*/
private static final int EVENT_PROMPT_UNVALIDATED = 29;
+ /**
+ * used internally to (re)configure mobile data always-on settings.
+ */
+ private static final int EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON = 30;
+
/** Handler used for internal events. */
final private InternalHandler mHandler;
/** Handler used for incoming {@link NetworkStateTracker} events. */
@@ -374,7 +379,7 @@
private PacManager mPacManager = null;
- private SettingsObserver mSettingsObserver;
+ final private SettingsObserver mSettingsObserver;
private UserManager mUserManager;
@@ -555,13 +560,12 @@
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
if (DBG) log("ConnectivityService starting up");
- NetworkCapabilities netCap = new NetworkCapabilities();
- netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
- netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
- mDefaultRequest = new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId());
- NetworkRequestInfo nri = new NetworkRequestInfo(null, mDefaultRequest, new Binder(),
- NetworkRequestInfo.REQUEST);
- mNetworkRequests.put(mDefaultRequest, nri);
+ mDefaultRequest = createInternetRequestForTransport(-1);
+ mNetworkRequests.put(mDefaultRequest, new NetworkRequestInfo(
+ null, mDefaultRequest, new Binder(), NetworkRequestInfo.REQUEST));
+
+ mDefaultMobileDataRequest = createInternetRequestForTransport(
+ NetworkCapabilities.TRANSPORT_CELLULAR);
HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread");
handlerThread.start();
@@ -696,8 +700,8 @@
mInetLog = new ArrayList();
}
- mSettingsObserver = new SettingsObserver(mHandler, EVENT_APPLY_GLOBAL_HTTP_PROXY);
- mSettingsObserver.observe(mContext);
+ mSettingsObserver = new SettingsObserver(mContext, mHandler);
+ registerSettingsCallbacks();
mDataConnectionStats = new DataConnectionStats(mContext);
mDataConnectionStats.startMonitoring();
@@ -707,6 +711,44 @@
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
}
+ private NetworkRequest createInternetRequestForTransport(int transportType) {
+ NetworkCapabilities netCap = new NetworkCapabilities();
+ netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+ if (transportType > -1) {
+ netCap.addTransportType(transportType);
+ }
+ return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId());
+ }
+
+ private void handleMobileDataAlwaysOn() {
+ final boolean enable = (Settings.Global.getInt(
+ mContext.getContentResolver(), Settings.Global.MOBILE_DATA_ALWAYS_ON, 0) == 1);
+ final boolean isEnabled = (mNetworkRequests.get(mDefaultMobileDataRequest) != null);
+ if (enable == isEnabled) {
+ return; // Nothing to do.
+ }
+
+ if (enable) {
+ handleRegisterNetworkRequest(new NetworkRequestInfo(
+ null, mDefaultMobileDataRequest, new Binder(), NetworkRequestInfo.REQUEST));
+ } else {
+ handleReleaseNetworkRequest(mDefaultMobileDataRequest, Process.SYSTEM_UID);
+ }
+ }
+
+ private void registerSettingsCallbacks() {
+ // Watch for global HTTP proxy changes.
+ mSettingsObserver.observe(
+ Settings.Global.getUriFor(Settings.Global.HTTP_PROXY),
+ EVENT_APPLY_GLOBAL_HTTP_PROXY);
+
+ // Watch for whether or not to keep mobile data always on.
+ mSettingsObserver.observe(
+ Settings.Global.getUriFor(Settings.Global.MOBILE_DATA_ALWAYS_ON),
+ EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON);
+ }
+
private synchronized int nextNetworkRequestId() {
return mNextNetworkRequestId++;
}
@@ -1491,6 +1533,9 @@
mContext.registerReceiver(mUserPresentReceiver, filter);
}
+ // Configure whether mobile data is always on.
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON));
+
mHandler.sendMessage(mHandler.obtainMessage(EVENT_SYSTEM_READY));
mPermissionMonitor.startMonitoring();
@@ -2107,12 +2152,10 @@
+ nri.request + " because their intents matched.");
handleReleaseNetworkRequest(existingRequest.request, getCallingUid());
}
- handleRegisterNetworkRequest(msg);
+ handleRegisterNetworkRequest(nri);
}
- private void handleRegisterNetworkRequest(Message msg) {
- final NetworkRequestInfo nri = (NetworkRequestInfo) (msg.obj);
-
+ private void handleRegisterNetworkRequest(NetworkRequestInfo nri) {
mNetworkRequests.put(nri.request, nri);
// TODO: This logic may be better replaced with a call to rematchNetworkAndRequests
@@ -2423,7 +2466,7 @@
}
case EVENT_REGISTER_NETWORK_REQUEST:
case EVENT_REGISTER_NETWORK_LISTENER: {
- handleRegisterNetworkRequest(msg);
+ handleRegisterNetworkRequest((NetworkRequestInfo) msg.obj);
break;
}
case EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT: {
@@ -2446,6 +2489,10 @@
handlePromptUnvalidated((Network) msg.obj);
break;
}
+ case EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON: {
+ handleMobileDataAlwaysOn();
+ break;
+ }
case EVENT_SYSTEM_READY: {
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
nai.networkMonitor.systemReady = true;
@@ -2837,23 +2884,36 @@
}
private static class SettingsObserver extends ContentObserver {
- private int mWhat;
- private Handler mHandler;
- SettingsObserver(Handler handler, int what) {
- super(handler);
+ final private HashMap<Uri, Integer> mUriEventMap;
+ final private Context mContext;
+ final private Handler mHandler;
+
+ SettingsObserver(Context context, Handler handler) {
+ super(null);
+ mUriEventMap = new HashMap<Uri, Integer>();
+ mContext = context;
mHandler = handler;
- mWhat = what;
}
- void observe(Context context) {
- ContentResolver resolver = context.getContentResolver();
- resolver.registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.HTTP_PROXY), false, this);
+ void observe(Uri uri, int what) {
+ mUriEventMap.put(uri, what);
+ final ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(uri, false, this);
}
@Override
public void onChange(boolean selfChange) {
- mHandler.obtainMessage(mWhat).sendToTarget();
+ Slog.wtf(TAG, "Should never be reached.");
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ final Integer what = mUriEventMap.get(uri);
+ if (what != null) {
+ mHandler.obtainMessage(what.intValue()).sendToTarget();
+ } else {
+ loge("No matching event to send for URI=" + uri);
+ }
}
}
@@ -3643,6 +3703,10 @@
// Note: if mDefaultRequest is changed, NetworkMonitor needs to be updated.
private final NetworkRequest mDefaultRequest;
+ // Request used to optionally keep mobile data active even when higher
+ // priority networks like Wi-Fi are active.
+ private final NetworkRequest mDefaultMobileDataRequest;
+
private NetworkAgentInfo getDefaultNetwork() {
return mNetworkForRequestId.get(mDefaultRequest.requestId);
}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 759a6be..e856a93 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -1695,6 +1695,7 @@
vis = 0;
}
mImeWindowVis = vis;
+ mInputShown = ((mImeWindowVis & InputMethodService.IME_VISIBLE) != 0);
mBackDisposition = backDisposition;
final boolean iconVisibility = ((vis & (InputMethodService.IME_ACTIVE)) != 0)
&& (mWindowManagerService.isHardKeyboardAvailable()
@@ -2038,11 +2039,9 @@
if (!mIWindowManager.inputMethodClientHasFocus(client)) {
if (DEBUG) Slog.w(TAG, "Ignoring hideSoftInput of uid "
+ uid + ": " + client);
- setImeWindowVisibilityStatusHiddenLocked();
return false;
}
} catch (RemoteException e) {
- setImeWindowVisibilityStatusHiddenLocked();
return false;
}
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index a07591c9..ac55292 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -488,7 +488,8 @@
try {
// Restricted users are not allowed to create VPNs, they are tied to Owner
UserInfo user = mgr.getUserInfo(mUserHandle);
- if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
+ if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
+ new UserHandle(mUserHandle))) {
throw new SecurityException("Restricted users cannot establish VPNs");
}
@@ -896,7 +897,8 @@
}
UserManager mgr = UserManager.get(mContext);
UserInfo user = mgr.getUserInfo(mUserHandle);
- if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN)) {
+ if (user.isRestricted() || mgr.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN,
+ new UserHandle(mUserHandle))) {
throw new SecurityException("Restricted users cannot establish VPNs");
}
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 7cccef2..3dc282b 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -52,6 +52,7 @@
import android.content.pm.RegisteredServicesCacheListener;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
+import android.database.ContentObserver;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.BatteryStats;
@@ -99,6 +100,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
@@ -157,7 +159,19 @@
/**
* How long to wait before considering an active sync to have timed-out, and cancelling it.
*/
- private static final long ACTIVE_SYNC_TIMEOUT_MILLIS = 30L * 60 * 1000; // 30 mins.
+ private static final long ACTIVE_SYNC_TIMEOUT_MILLIS = 30L * 60 * 1000; // 30 mins
+
+ /**
+ * How long to delay each queued {@link SyncHandler} message that may have occurred before boot
+ * or befor the device became provisioned.
+ */
+ private static final long PER_SYNC_BOOT_DELAY_MILLIS = 3000L; // 3 seconds
+
+ /**
+ * The maximum amount of time we're willing to delay syncs out of boot, after device has been
+ * provisioned, etc.
+ */
+ private static final long MAX_SYNC_BOOT_DELAY_MILLIS = 120000L; // 2 minutes
private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*/";
private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm";
@@ -198,6 +212,9 @@
// its accessor, getConnManager().
private ConnectivityManager mConnManagerDoNotUseDirectly;
+ /** Track whether the device has already been provisioned. */
+ private boolean mProvisioned;
+
protected SyncAdaptersCache mSyncAdapters;
private final AppIdleMonitor mAppIdleMonitor;
@@ -242,6 +259,7 @@
private BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ mBootCompleted = true;
mSyncHandler.onBootCompleted();
}
};
@@ -491,12 +509,41 @@
mSyncStorageEngine.addStatusChangeListener(
ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, new ISyncStatusObserver.Stub() {
- @Override
- public void onStatusChanged(int which) {
- // force the sync loop to run if the settings change
- sendCheckAlarmsMessage();
+ @Override
+ public void onStatusChanged(int which) {
+ // force the sync loop to run if the settings change
+ sendCheckAlarmsMessage();
+ }
+ });
+
+ mProvisioned = isDeviceProvisioned();
+ if (!mProvisioned) {
+ final ContentResolver resolver = context.getContentResolver();
+ ContentObserver provisionedObserver =
+ new ContentObserver(null /* current thread */) {
+ public void onChange(boolean selfChange) {
+ mProvisioned |= isDeviceProvisioned();
+ if (mProvisioned) {
+ mSyncHandler.onDeviceProvisioned();
+ resolver.unregisterContentObserver(this);
+ }
+ }
+ };
+
+ synchronized (mSyncHandler) {
+ resolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+ false /* notifyForDescendents */,
+ provisionedObserver);
+
+ // The device *may* have been provisioned while we were registering above observer.
+ // Check again to make sure.
+ mProvisioned |= isDeviceProvisioned();
+ if (mProvisioned) {
+ resolver.unregisterContentObserver(provisionedObserver);
+ }
}
- });
+ }
if (!factoryTest) {
// Register for account list updates for all users
@@ -510,6 +557,10 @@
mSyncRandomOffsetMillis = mSyncStorageEngine.getSyncRandomOffset() * 1000;
}
+ private boolean isDeviceProvisioned() {
+ final ContentResolver resolver = mContext.getContentResolver();
+ return (Settings.Global.getInt(resolver, Settings.Global.DEVICE_PROVISIONED, 0) != 0);
+ }
/**
* Return a random value v that satisfies minValue <= v < maxValue. The difference between
* maxValue and minValue must be less than Integer.MAX_VALUE.
@@ -2000,20 +2051,36 @@
public final SyncTimeTracker mSyncTimeTracker = new SyncTimeTracker();
private final HashMap<String, PowerManager.WakeLock> mWakeLocks = Maps.newHashMap();
- private List<Message> mBootQueue = new ArrayList<Message>();
+ private List<Message> mUnreadyQueue = new ArrayList<Message>();
- public void onBootCompleted() {
+ void onBootCompleted() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Boot completed, clearing boot queue.");
}
doDatabaseCleanup();
synchronized(this) {
// Dispatch any stashed messages.
- for (Message message : mBootQueue) {
- sendMessage(message);
+ maybeEmptyUnreadyQueueLocked();
+ }
+ }
+
+ void onDeviceProvisioned() {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "mProvisioned=" + mProvisioned);
+ }
+ synchronized (this) {
+ maybeEmptyUnreadyQueueLocked();
+ }
+ }
+
+ private void maybeEmptyUnreadyQueueLocked() {
+ if (mProvisioned && mBootCompleted) {
+ // Dispatch any stashed messages.
+ for (int i=0; i<mUnreadyQueue.size(); i++) {
+ sendMessageDelayed(mUnreadyQueue.get(i),
+ Math.max(PER_SYNC_BOOT_DELAY_MILLIS * i, MAX_SYNC_BOOT_DELAY_MILLIS));
}
- mBootQueue = null;
- mBootCompleted = true;
+ mUnreadyQueue = null;
}
}
@@ -2030,20 +2097,23 @@
}
/**
- * Stash any messages that come to the handler before boot is complete.
- * {@link #onBootCompleted()} will disable this and dispatch all the messages collected.
+ * Stash any messages that come to the handler before boot is complete or before the device
+ * is properly provisioned (i.e. out of set-up wizard).
+ * {@link #onBootCompleted()} and {@link #onDeviceProvisioned(boolean)} both need to come
+ * in before we start syncing.
* @param msg Message to dispatch at a later point.
* @return true if a message was enqueued, false otherwise. This is to avoid losing the
* message if we manage to acquire the lock but by the time we do boot has completed.
*/
private boolean tryEnqueueMessageUntilReadyToRun(Message msg) {
synchronized (this) {
- if (!mBootCompleted) {
+ if (!mBootCompleted || !mProvisioned) {
// Need to copy the message bc looper will recycle it.
- mBootQueue.add(Message.obtain(msg));
+ mUnreadyQueue.add(Message.obtain(msg));
return true;
+ } else {
+ return false;
}
- return false;
}
}
@@ -2100,7 +2170,7 @@
}
cancelActiveSync(expiredContext.mSyncOperation.target,
expiredContext.mSyncOperation.extras);
- nextPendingSyncTime = maybeStartNextSyncLocked();
+ nextPendingSyncTime = maybeStartNextSyncH();
break;
case SyncHandler.MESSAGE_CANCEL: {
@@ -2111,7 +2181,7 @@
+ payload + " bundle: " + extras);
}
cancelActiveSyncLocked(payload, extras);
- nextPendingSyncTime = maybeStartNextSyncLocked();
+ nextPendingSyncTime = maybeStartNextSyncH();
break;
}
@@ -2120,17 +2190,17 @@
Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_SYNC_FINISHED");
}
SyncHandlerMessagePayload payload = (SyncHandlerMessagePayload) msg.obj;
- if (!isSyncStillActive(payload.activeSyncContext)) {
+ if (!isSyncStillActiveH(payload.activeSyncContext)) {
Log.d(TAG, "handleSyncHandlerMessage: dropping since the "
+ "sync is no longer active: "
+ payload.activeSyncContext);
break;
}
- runSyncFinishedOrCanceledLocked(payload.syncResult,
+ runSyncFinishedOrCanceledH(payload.syncResult,
payload.activeSyncContext);
// since a sync just finished check if it is time to start a new sync
- nextPendingSyncTime = maybeStartNextSyncLocked();
+ nextPendingSyncTime = maybeStartNextSyncH();
break;
case SyncHandler.MESSAGE_SERVICE_CONNECTED: {
@@ -2140,7 +2210,7 @@
+ msgData.activeSyncContext);
}
// check that this isn't an old message
- if (isSyncStillActive(msgData.activeSyncContext)) {
+ if (isSyncStillActiveH(msgData.activeSyncContext)) {
runBoundToAdapter(
msgData.activeSyncContext,
msgData.adapter);
@@ -2156,7 +2226,7 @@
+ currentSyncContext);
}
// check that this isn't an old message
- if (isSyncStillActive(currentSyncContext)) {
+ if (isSyncStillActiveH(currentSyncContext)) {
// cancel the sync if we have a syncadapter, which means one is
// outstanding
try {
@@ -2174,10 +2244,10 @@
// which is a soft error
SyncResult syncResult = new SyncResult();
syncResult.stats.numIoExceptions++;
- runSyncFinishedOrCanceledLocked(syncResult, currentSyncContext);
+ runSyncFinishedOrCanceledH(syncResult, currentSyncContext);
// since a sync just finished check if it is time to start a new sync
- nextPendingSyncTime = maybeStartNextSyncLocked();
+ nextPendingSyncTime = maybeStartNextSyncH();
}
break;
@@ -2190,7 +2260,7 @@
}
mAlarmScheduleTime = null;
try {
- nextPendingSyncTime = maybeStartNextSyncLocked();
+ nextPendingSyncTime = maybeStartNextSyncH();
} finally {
mHandleAlarmWakeLock.release();
}
@@ -2201,7 +2271,7 @@
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "handleSyncHandlerMessage: MESSAGE_CHECK_ALARMS");
}
- nextPendingSyncTime = maybeStartNextSyncLocked();
+ nextPendingSyncTime = maybeStartNextSyncH();
break;
}
} finally {
@@ -2393,7 +2463,7 @@
0 : (earliestFuturePollTime - nowAbsolute));
}
- private long maybeStartNextSyncLocked() {
+ private long maybeStartNextSyncH() {
final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
if (isLoggable) Log.v(TAG, "maybeStartNextSync");
@@ -2612,7 +2682,7 @@
}
if (toReschedule != null) {
- runSyncFinishedOrCanceledLocked(null, toReschedule);
+ runSyncFinishedOrCanceledH(null, toReschedule);
scheduleSyncOperation(toReschedule.mSyncOperation);
}
synchronized (mSyncQueue) {
@@ -2845,14 +2915,14 @@
false /* no config settings */)) {
continue;
}
- runSyncFinishedOrCanceledLocked(null /* no result since this is a cancel */,
+ runSyncFinishedOrCanceledH(null /* no result since this is a cancel */,
activeSyncContext);
}
}
}
- private void runSyncFinishedOrCanceledLocked(SyncResult syncResult,
- ActiveSyncContext activeSyncContext) {
+ private void runSyncFinishedOrCanceledH(SyncResult syncResult,
+ ActiveSyncContext activeSyncContext) {
boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
final SyncOperation syncOperation = activeSyncContext.mSyncOperation;
@@ -3257,7 +3327,7 @@
}
}
- private boolean isSyncStillActive(ActiveSyncContext activeSyncContext) {
+ private boolean isSyncStillActiveH(ActiveSyncContext activeSyncContext) {
for (ActiveSyncContext sync : mActiveSyncContexts) {
if (sync == activeSyncContext) {
return true;
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 6f59b54..7f961ae 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -82,7 +82,7 @@
appToken.linkToDeath(device, 0);
} catch (RemoteException ex) {
mVirtualDisplayDevices.remove(appToken);
- device.destroyLocked();
+ device.destroyLocked(false);
return null;
}
@@ -110,7 +110,7 @@
public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken) {
VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
if (device != null) {
- device.destroyLocked();
+ device.destroyLocked(true);
appToken.unlinkToDeath(device, 0);
}
@@ -147,7 +147,7 @@
if (device != null) {
Slog.i(TAG, "Virtual display device released because application token died: "
+ device.mOwnerPackageName);
- device.destroyLocked();
+ device.destroyLocked(false);
sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
}
}
@@ -205,19 +205,19 @@
@Override
public void binderDied() {
synchronized (getSyncRoot()) {
- if (mSurface != null) {
- handleBinderDiedLocked(mAppToken);
- }
+ handleBinderDiedLocked(mAppToken);
}
}
- public void destroyLocked() {
+ public void destroyLocked(boolean binderAlive) {
if (mSurface != null) {
mSurface.release();
mSurface = null;
}
SurfaceControl.destroyDisplay(getDisplayTokenLocked());
- mCallback.dispatchDisplayStopped();
+ if (binderAlive) {
+ mCallback.dispatchDisplayStopped();
+ }
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index e434f39..8c12060 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -214,6 +214,10 @@
// values which denotes the device type in HDMI Spec 1.4.
static final String PROPERTY_DEVICE_TYPE = "ro.hdmi.device_type";
+ // TODO(OEM): Set this to false to keep the playback device in sleep upon hotplug event.
+ // True by default.
+ static final String PROPERTY_WAKE_ON_HOTPLUG = "ro.hdmi.wake_on_hotplug";
+
// Set to false to allow playback device to go to suspend mode even
// when it's an active source. True by default.
static final String PROPERTY_KEEP_AWAKE = "persist.sys.hdmi.keep_awake";
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 89ffe45..fd3364a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -34,6 +34,9 @@
final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
private static final String TAG = "HdmiCecLocalDevicePlayback";
+ private static final boolean WAKE_ON_HOTPLUG =
+ SystemProperties.getBoolean(Constants.PROPERTY_WAKE_ON_HOTPLUG, true);
+
private boolean mIsActiveSource = false;
// Used to keep the device awake while it is the active source. For devices that
@@ -130,7 +133,7 @@
assertRunOnServiceThread();
mCecMessageCache.flushAll();
// We'll not clear mIsActiveSource on the hotplug event to pass CETC 11.2.2-2 ~ 3.
- if (connected && mService.isPowerStandbyOrTransient()) {
+ if (WAKE_ON_HOTPLUG && connected && mService.isPowerStandbyOrTransient()) {
mService.wakeUp();
}
if (!connected) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 5ac027d..e650456 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -486,6 +486,7 @@
HdmiLogger.debug("Input not ready for device: %X; buffering the command", info.getId());
mDelayedMessageBuffer.add(message);
} else {
+ updateDevicePowerStatus(logicalAddress, HdmiControlManager.POWER_STATUS_ON);
ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress);
ActiveSourceHandler.create(this, null).process(activeSource, info.getDeviceType());
}
@@ -1613,6 +1614,8 @@
super.disableDevice(initiatedByCec, callback);
clearDeviceInfoList();
+ getActiveSource().invalidate();
+ setActivePath(Constants.INVALID_PHYSICAL_ADDRESS);
checkIfPendingActionsCleared();
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 09d0501..dca762c 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -27,9 +27,6 @@
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.VolumeProvider;
-import android.media.routing.IMediaRouter;
-import android.media.routing.IMediaRouterDelegate;
-import android.media.routing.IMediaRouterStateCallback;
import android.media.session.ISession;
import android.media.session.ISessionCallback;
import android.media.session.ISessionController;
@@ -718,11 +715,6 @@
}
@Override
- public void setMediaRouter(IMediaRouter router) {
- mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE);
- }
-
- @Override
public void setMediaButtonReceiver(PendingIntent pi) {
mMediaButtonReceiver = pi;
}
@@ -1209,13 +1201,6 @@
public boolean isTransportControlEnabled() {
return MediaSessionRecord.this.isTransportControlEnabled();
}
-
- @Override
- public IMediaRouterDelegate createMediaRouterDelegate(
- IMediaRouterStateCallback callback) {
- // todo
- return null;
- }
}
private class MessageHandler extends Handler {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f30a567..a120c1f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -11853,6 +11853,7 @@
synchronized (mPackages) {
if (deletedPs != null) {
if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {
+ clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL);
if (outInfo != null) {
mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName);
outInfo.removedAppId = mSettings.removePackageLPw(packageName);
@@ -11883,7 +11884,6 @@
}
}
clearPackagePreferredActivitiesLPw(deletedPs.name, UserHandle.USER_ALL);
- clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL);
}
// make sure to preserve per-user disabled state if this removal was just
// a downgrade of a system app to the factory package
@@ -12744,13 +12744,16 @@
/** This method takes a specific user id as well as UserHandle.USER_ALL. */
void clearIntentFilterVerificationsLPw(String packageName, int userId) {
if (userId == UserHandle.USER_ALL) {
- mSettings.removeIntentFilterVerificationLPw(packageName, sUserManager.getUserIds());
- for (int oneUserId : sUserManager.getUserIds()) {
- scheduleWritePackageRestrictionsLocked(oneUserId);
+ if (mSettings.removeIntentFilterVerificationLPw(packageName,
+ sUserManager.getUserIds())) {
+ for (int oneUserId : sUserManager.getUserIds()) {
+ scheduleWritePackageRestrictionsLocked(oneUserId);
+ }
}
} else {
- mSettings.removeIntentFilterVerificationLPw(packageName, userId);
- scheduleWritePackageRestrictionsLocked(userId);
+ if (mSettings.removeIntentFilterVerificationLPw(packageName, userId)) {
+ scheduleWritePackageRestrictionsLocked(userId);
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index d476bfde..fd70ce1 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1067,19 +1067,22 @@
return result;
}
- void removeIntentFilterVerificationLPw(String packageName, int userId) {
+ boolean removeIntentFilterVerificationLPw(String packageName, int userId) {
PackageSetting ps = mPackages.get(packageName);
if (ps == null) {
Slog.w(PackageManagerService.TAG, "No package known for name: " + packageName);
- return;
+ return false;
}
ps.clearDomainVerificationStatusForUser(userId);
+ return true;
}
- void removeIntentFilterVerificationLPw(String packageName, int[] userIds) {
+ boolean removeIntentFilterVerificationLPw(String packageName, int[] userIds) {
+ boolean result = false;
for (int userId : userIds) {
- removeIntentFilterVerificationLPw(packageName, userId);
+ result |= removeIntentFilterVerificationLPw(packageName, userId);
}
+ return result;
}
boolean setDefaultBrowserPackageNameLPr(String packageName, int userId) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index e79a2061..e6f5e3d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -234,6 +234,14 @@
mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
initDefaultGuestRestrictions();
readUserListLocked();
+ sInstance = this;
+ }
+ }
+ }
+
+ void systemReady() {
+ synchronized (mInstallLock) {
+ synchronized (mPackagesLock) {
// Prune out any partially created/partially removed users.
ArrayList<UserInfo> partials = new ArrayList<UserInfo>();
for (int i = 0; i < mUsers.size(); i++) {
@@ -248,12 +256,8 @@
+ " (name=" + ui.name + ")");
removeUserStateLocked(ui.id);
}
- sInstance = this;
}
}
- }
-
- void systemReady() {
userForeground(UserHandle.USER_OWNER);
mAppOpsService = IAppOpsService.Stub.asInterface(
ServiceManager.getService(Context.APP_OPS_SERVICE));
diff --git a/services/core/java/com/android/server/policy/BarController.java b/services/core/java/com/android/server/policy/BarController.java
index e972ec7..5877b3e 100644
--- a/services/core/java/com/android/server/policy/BarController.java
+++ b/services/core/java/com/android/server/policy/BarController.java
@@ -58,6 +58,9 @@
private int mTransientBarState;
private boolean mPendingShow;
private long mLastTranslucent;
+ private boolean mShowTransparent;
+ private boolean mSetUnHideFlagWhenNextTransparent;
+ private boolean mNoAnimationOnNextShow;
public BarController(String tag, int transientFlag, int unhideFlag, int translucentFlag,
int statusBarManagerId, int translucentWmFlag) {
@@ -74,6 +77,14 @@
mWin = win;
}
+ public void setShowTransparent(boolean transparent) {
+ if (transparent != mShowTransparent) {
+ mShowTransparent = transparent;
+ mSetUnHideFlagWhenNextTransparent = transparent;
+ mNoAnimationOnNextShow = true;
+ }
+ }
+
public void showTransient() {
if (mWin != null) {
setTransientBarState(TRANSIENT_BAR_SHOW_REQUESTED);
@@ -135,7 +146,9 @@
}
final boolean wasVis = mWin.isVisibleLw();
final boolean wasAnim = mWin.isAnimatingLw();
- final boolean change = show ? mWin.showLw(true) : mWin.hideLw(true);
+ final boolean change = show ? mWin.showLw(!mNoAnimationOnNextShow)
+ : mWin.hideLw(!mNoAnimationOnNextShow);
+ mNoAnimationOnNextShow = false;
final int state = computeStateLw(wasVis, wasAnim, mWin, change);
final boolean stateChanged = updateStateLw(state);
return change || stateChanged;
@@ -233,6 +246,13 @@
setTransientBarState(TRANSIENT_BAR_NONE); // request denied
}
}
+ if (mShowTransparent) {
+ vis |= View.SYSTEM_UI_TRANSPARENT;
+ if (mSetUnHideFlagWhenNextTransparent) {
+ vis |= mUnhideFlag;
+ mSetUnHideFlagWhenNextTransparent = false;
+ }
+ }
if (mTransientBarState != TRANSIENT_BAR_NONE) {
vis |= mTransientFlag; // ignore clear requests until transition completes
vis &= ~View.SYSTEM_UI_FLAG_LOW_PROFILE; // never show transient bars in low profile
diff --git a/services/core/java/com/android/server/policy/GlobalActions.java b/services/core/java/com/android/server/policy/GlobalActions.java
index b431b33..3cee927 100644
--- a/services/core/java/com/android/server/policy/GlobalActions.java
+++ b/services/core/java/com/android/server/policy/GlobalActions.java
@@ -1138,7 +1138,7 @@
public GlobalActionsDialog(Context context, AlertParams params) {
super(context, getDialogTheme(context));
- mContext = context;
+ mContext = getContext();
mAlert = new AlertController(mContext, this, getWindow());
mAdapter = (MyAdapter) params.mAdapter;
mWindowTouchSlop = ViewConfiguration.get(context).getScaledWindowTouchSlop();
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 51503ec..185ef03 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -93,7 +93,7 @@
import android.view.KeyCharacterMap.FallbackAction;
import android.view.KeyEvent;
import android.view.MotionEvent;
-import android.view.PhoneWindow;
+import com.android.internal.policy.PhoneWindow;
import android.view.Surface;
import android.view.View;
import android.view.ViewConfiguration;
@@ -476,6 +476,7 @@
boolean mTopIsFullscreen;
boolean mForceStatusBar;
boolean mForceStatusBarFromKeyguard;
+ private boolean mForceStatusBarTransparent;
boolean mHideLockScreen;
boolean mForcingShowNavBar;
int mForcingShowNavBarLayer;
@@ -4129,6 +4130,7 @@
mAppsThatDismissKeyguard.clear();
mForceStatusBar = false;
mForceStatusBarFromKeyguard = false;
+ mForceStatusBarTransparent = false;
mForcingShowNavBar = false;
mForcingShowNavBarLayer = -1;
@@ -4154,8 +4156,13 @@
mForcingShowNavBar = true;
mForcingShowNavBarLayer = win.getSurfaceLayer();
}
- if (attrs.type == TYPE_STATUS_BAR && (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
- mForceStatusBarFromKeyguard = true;
+ if (attrs.type == TYPE_STATUS_BAR) {
+ if ((attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
+ mForceStatusBarFromKeyguard = true;
+ }
+ if ((attrs.privateFlags & PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT) != 0) {
+ mForceStatusBarTransparent = true;
+ }
}
boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW
@@ -4302,7 +4309,15 @@
if (DEBUG_LAYOUT) Slog.i(TAG, "force=" + mForceStatusBar
+ " forcefkg=" + mForceStatusBarFromKeyguard
+ " top=" + mTopFullscreenOpaqueWindowState);
- if (mForceStatusBar || mForceStatusBarFromKeyguard) {
+ boolean shouldBeTransparent = mForceStatusBarTransparent
+ && !mForceStatusBar
+ && !mForceStatusBarFromKeyguard;
+ if (!shouldBeTransparent) {
+ mStatusBarController.setShowTransparent(false /* transparent */);
+ } else if (!mStatusBar.isVisibleLw()) {
+ mStatusBarController.setShowTransparent(true /* transparent */);
+ }
+ if (mForceStatusBar || mForceStatusBarFromKeyguard || mForceStatusBarTransparent) {
if (DEBUG_LAYOUT) Slog.v(TAG, "Showing status bar: forced");
if (mStatusBarController.setBarShowingLw(true)) {
changes |= FINISH_LAYOUT_REDO_LAYOUT;
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index c48367e..fd98010 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -539,9 +539,11 @@
};
private void playWirelessChargingStartedSound() {
+ final boolean enabled = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.CHARGING_SOUNDS_ENABLED, 1) != 0;
final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.WIRELESS_CHARGING_STARTED_SOUND);
- if (soundPath != null) {
+ if (enabled && soundPath != null) {
final Uri soundUri = Uri.parse("file://" + soundPath);
if (soundUri != null) {
final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 6f01ca0..67c198f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2897,7 +2897,7 @@
boolean callerIsDeviceOwnerAdmin = isCallerDeviceOwnerOrInitializer(callingUid);
boolean doNotAskCredentialsOnBoot =
- (flags & DevicePolicyManager.DO_NOT_ASK_CREDENTIALS_ON_BOOT) != 0;
+ (flags & DevicePolicyManager.RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT) != 0;
if (callerIsDeviceOwnerAdmin && doNotAskCredentialsOnBoot) {
setDoNotAskCredentialsOnBoot();
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2d265e2..925a609 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -28,6 +28,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.content.res.Resources.Theme;
import android.os.Build;
import android.os.Environment;
import android.os.FactoryTest;
@@ -291,7 +292,7 @@
private void createSystemContext() {
ActivityThread activityThread = ActivityThread.systemMain();
mSystemContext = activityThread.getSystemContext();
- mSystemContext.setTheme(android.R.style.Theme_DeviceDefault_Light_DarkActionBar);
+ mSystemContext.setTheme(android.R.style.Theme_Material_DayNight_DarkActionBar);
}
/**
@@ -1026,6 +1027,12 @@
w.getDefaultDisplay().getMetrics(metrics);
context.getResources().updateConfiguration(config, metrics);
+ // The system context's theme may be configuration-dependent.
+ final Theme systemTheme = context.getTheme();
+ if (systemTheme.getChangingConfigurations() != 0) {
+ systemTheme.rebase();
+ }
+
try {
// TODO: use boot phase
mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService());
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index c1c5c56..176f54b1 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -294,8 +294,10 @@
@Override
public String toString() {
- StringBuilder sb = new StringBuilder("Device: ");
+ StringBuilder sb = new StringBuilder("Device Info: ");
sb.append(mDeviceInfo);
+ sb.append(" Status: ");
+ sb.append(mDeviceStatus);
sb.append(" UID: ");
sb.append(mUid);
return sb.toString();
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 2897c61..fcdb6d6 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -754,7 +754,7 @@
public boolean activeServiceSupportsAssist() {
enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
synchronized (this) {
- return mImpl != null && mImpl.mInfo.getSupportsAssist();
+ return mImpl != null && mImpl.mInfo != null && mImpl.mInfo.getSupportsAssist();
}
}
@@ -762,7 +762,8 @@
public boolean activeServiceSupportsLaunchFromKeyguard() throws RemoteException {
enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE);
synchronized (this) {
- return mImpl != null && mImpl.mInfo.getSupportsLaunchFromKeyguard();
+ return mImpl != null && mImpl.mInfo != null
+ && mImpl.mInfo.getSupportsLaunchFromKeyguard();
}
}
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 0424548..e682697 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -179,7 +179,6 @@
/**
* Returns VideoProvider of the primary call. This can be null.
- * @hide
*/
public VideoProvider getVideoProvider() {
return null;
@@ -187,7 +186,6 @@
/**
* Returns video state of the primary call.
- * @hide
*/
public int getVideoState() {
return VideoProfile.VideoState.AUDIO_ONLY;
@@ -373,7 +371,6 @@
* {@link VideoProfile.VideoState#RX_ENABLED}.
*
* @param videoState The new video state.
- * @hide
*/
public final void setVideoState(Connection c, int videoState) {
Log.d(this, "setVideoState Conference: %s Connection: %s VideoState: %s",
@@ -387,7 +384,6 @@
* Sets the video connection provider.
*
* @param videoProvider The video provider.
- * @hide
*/
public final void setVideoProvider(Connection c, Connection.VideoProvider videoProvider) {
Log.d(this, "setVideoProvider Conference: %s Connection: %s VideoState: %s",
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 4bc639b..0bf9118 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -106,38 +106,32 @@
/**
* Local device supports receiving video.
- * @hide
*/
public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 0x00000100;
/**
* Local device supports transmitting video.
- * @hide
*/
public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 0x00000200;
/**
* Local device supports bidirectional video calling.
- * @hide
*/
public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL =
CAPABILITY_SUPPORTS_VT_LOCAL_RX | CAPABILITY_SUPPORTS_VT_LOCAL_TX;
/**
* Remote device supports receiving video.
- * @hide
*/
public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 0x00000400;
/**
* Remote device supports transmitting video.
- * @hide
*/
public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 0x00000800;
/**
* Remote device supports bidirectional video calling.
- * @hide
*/
public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL =
CAPABILITY_SUPPORTS_VT_REMOTE_RX | CAPABILITY_SUPPORTS_VT_REMOTE_TX;
@@ -187,14 +181,12 @@
/**
* Call can be upgraded to a video call.
- * @hide
*/
public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 0x00080000;
/**
* For video calls, indicates whether the outgoing video for the call can be paused using
* the {@link android.telecom.VideoProfile.VideoState#PAUSED} VideoState.
- * @hide
*/
public static final int CAPABILITY_CAN_PAUSE_VIDEO = 0x00100000;
@@ -1041,7 +1033,6 @@
* {@link VideoProfile.VideoState#RX_ENABLED}.
*
* @param videoState The new video state.
- * @hide
*/
public final void setVideoState(int videoState) {
checkImmutable();
@@ -1105,7 +1096,6 @@
/**
* Sets the video connection provider.
* @param videoProvider The video provider.
- * @hide
*/
public final void setVideoProvider(VideoProvider videoProvider) {
checkImmutable();
@@ -1414,7 +1404,6 @@
* a request to accept.
*
* @param videoState The video state in which to answer the connection.
- * @hide
*/
public void onAnswer(int videoState) {}
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index 71b481b..f5cceea 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -50,7 +50,6 @@
* @param handle The handle (e.g., phone number) to which the {@link Connection} is to connect.
* @param extras Application-specific extra data.
* @param videoState Determines the video state for the connection.
- * @hide
*/
public ConnectionRequest(
PhoneAccountHandle accountHandle,
@@ -95,7 +94,6 @@
* {@link VideoProfile.VideoState#RX_ENABLED}.
*
* @return The video state for the connection.
- * @hide
*/
public int getVideoState() {
return mVideoState;
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 4185651..13eb016 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -147,7 +147,6 @@
}
@Override
- /** @hide */
public void answerVideo(String callId, int videoState) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = callId;
diff --git a/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java
index fd0c06d..d3df151 100644
--- a/telecomm/java/android/telecom/DefaultDialerManager.java
+++ b/telecomm/java/android/telecom/DefaultDialerManager.java
@@ -20,6 +20,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.net.Uri;
import android.provider.Settings;
import android.text.TextUtils;
@@ -151,14 +152,14 @@
for (ResolveInfo resolveInfo : resolveInfoList) {
final ActivityInfo activityInfo = resolveInfo.activityInfo;
- if (activityInfo == null) {
- continue;
+ if (activityInfo != null && !packageNames.contains(activityInfo.packageName)) {
+ packageNames.add(activityInfo.packageName);
}
- packageNames.add(activityInfo.packageName);
}
- // TODO: Filter for apps that don't handle DIAL intent with tel scheme
- return packageNames;
+ final Intent dialIntentWithTelScheme = new Intent(Intent.ACTION_DIAL);
+ dialIntentWithTelScheme.setData(Uri.fromParts(PhoneAccount.SCHEME_TEL, "", null));
+ return filterByIntent(context, packageNames, dialIntentWithTelScheme);
}
/**
@@ -182,6 +183,36 @@
|| packageName.equals(tm.getSystemDialerPackage());
}
+ /**
+ * Filter a given list of package names for those packages that contain an activity that has
+ * an intent filter for a given intent.
+ *
+ * @param context A valid context
+ * @param packageNames List of package names to filter.
+ * @return The filtered list.
+ */
+ private static List<String> filterByIntent(Context context, List<String> packageNames,
+ Intent intent) {
+ if (packageNames == null || packageNames.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ final List<String> result = new ArrayList<>();
+ final List<ResolveInfo> resolveInfoList =
+ context.getPackageManager().queryIntentActivities(intent, 0);
+ final int length = resolveInfoList.size();
+ for (int i = 0; i < length; i++) {
+ final ActivityInfo info = resolveInfoList.get(i).activityInfo;
+ if (info != null && packageNames.contains(info.packageName)
+ && !result.contains(info.packageName)) {
+ result.add(info.packageName);
+ }
+ }
+
+ return result;
+ }
+
+
private static TelecomManager getTelecomManager(Context context) {
return (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 1431eb8..ebd3f12 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1143,8 +1143,12 @@
public void placeCall(Uri address, Bundle extras) {
ITelecomService service = getTelecomService();
if (service != null) {
+ if (address == null) {
+ Log.w(TAG, "Cannot place call to empty address.");
+ }
try {
- service.placeCall(address, extras, mContext.getOpPackageName());
+ service.placeCall(address, extras == null ? new Bundle() : extras,
+ mContext.getOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelecomService#placeCall", e);
}
diff --git a/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java b/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java
index dd823ae..b54f9be 100644
--- a/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java
+++ b/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java
@@ -30,6 +30,7 @@
import junit.framework.Assert;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -102,7 +103,11 @@
// otherwise raise an
// exception with the first error encountered.
assertNull(getStackTrace(err), err);
- assertTrue("App crashed after launch.", processStillUp(packageName));
+ try {
+ assertTrue("App crashed after launch.", processStillUp(packageName));
+ } finally {
+ returnHome();
+ }
} else {
Log.d(TAG, "Missing argument, use " + PACKAGE_TO_LAUNCH +
" to specify the package to launch");
@@ -138,6 +143,19 @@
}
}
+ private void returnHome() {
+ Intent homeIntent = new Intent(Intent.ACTION_MAIN);
+ homeIntent.addCategory(Intent.CATEGORY_HOME);
+ homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ // Send the "home" intent and wait 2 seconds for us to get there
+ mContext.startActivity(homeIntent);
+ try {
+ Thread.sleep(mWorkspaceLaunchTimeout);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+
/**
* Launches and activity and queries for errors.
*
@@ -150,9 +168,6 @@
// the recommended way to see if this is a tv or not.
boolean isleanback = !mPackageManager.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)
&& !mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
- Intent homeIntent = new Intent(Intent.ACTION_MAIN);
- homeIntent.addCategory(Intent.CATEGORY_HOME);
- homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Intent intent;
if (isleanback) {
Log.d(TAG, "Leanback and relax! " + packageName);
@@ -173,14 +188,6 @@
// ignore
}
- // Send the "home" intent and wait 2 seconds for us to get there
- mContext.startActivity(homeIntent);
- try {
- Thread.sleep(mWorkspaceLaunchTimeout);
- } catch (InterruptedException e) {
- // ignore
- }
-
// See if there are any errors. We wait until down here to give ANRs as
// much time as
// possible to occur.
@@ -198,6 +205,12 @@
return null;
}
+ private boolean ensureForegroundActivity(RunningAppProcessInfo info) {
+ Log.d(TAG, String.format("ensureForegroundActivity: proc=%s, pid=%d, state=%d",
+ info.processName, info.pid, info.processState));
+ return info.processState == ActivityManager.PROCESS_STATE_TOP;
+ }
+
/**
* Determine if a given package is still running.
*
@@ -207,19 +220,32 @@
private boolean processStillUp(String packageName) {
String processName = getProcessName(packageName);
List<RunningAppProcessInfo> runningApps = mActivityManager.getRunningAppProcesses();
+ List<RunningAppProcessInfo> relatedProcs = new ArrayList<>();
for (RunningAppProcessInfo app : runningApps) {
if (app.processName.equalsIgnoreCase(processName)) {
- Log.d(TAG, "Found process " + app.processName);
+ if (!ensureForegroundActivity(app)) {
+ Log.w(TAG, "Found process but it's not top activity.");
+ return false;
+ }
return true;
}
for (String relatedPackage : app.pkgList) {
- if (relatedPackage.equalsIgnoreCase(processName)) {
- Log.d(TAG, "Found process " + app.processName);
- return true;
+ if (relatedPackage.equalsIgnoreCase(packageName)) {
+ relatedProcs.add(app);
}
}
}
- Log.d(TAG, "Failed to find process " + processName + " with package name "
+ // now that we are here, we've found no RAPI's directly matching processName, but
+ // potentially a List of them with one of related packages being processName
+ if (!relatedProcs.isEmpty()) {
+ for (RunningAppProcessInfo app : relatedProcs) {
+ if (ensureForegroundActivity(app)) {
+ return true;
+ }
+ }
+ Log.w(TAG, "Found related processes, but none has top activity.");
+ }
+ Log.w(TAG, "Failed to find process " + processName + " with package name "
+ packageName);
return false;
}
diff --git a/tests/HierarchyViewerTest/.gitignore b/tests/HierarchyViewerTest/.gitignore
new file mode 100644
index 0000000..75eec98
--- /dev/null
+++ b/tests/HierarchyViewerTest/.gitignore
@@ -0,0 +1,6 @@
+.gradle
+.idea
+*.iml
+gradle*
+build
+local.properties
diff --git a/tests/HierarchyViewerTest/Android.mk b/tests/HierarchyViewerTest/Android.mk
new file mode 100644
index 0000000..07b90f0
--- /dev/null
+++ b/tests/HierarchyViewerTest/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := HierarchyViewerTest
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+include $(BUILD_PACKAGE)
diff --git a/tests/HierarchyViewerTest/AndroidManifest.xml b/tests/HierarchyViewerTest/AndroidManifest.xml
new file mode 100644
index 0000000..65f2fd3
--- /dev/null
+++ b/tests/HierarchyViewerTest/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<!--
+ ~ 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
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.hierarchyviewer">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+
+ <activity
+ android:name=".MainActivity"
+ android:label="HvTest" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+ <instrumentation
+ android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.android.test.hierarchyviewer" />
+</manifest>
diff --git a/tests/HierarchyViewerTest/build.gradle b/tests/HierarchyViewerTest/build.gradle
new file mode 100644
index 0000000..e8cdfa2
--- /dev/null
+++ b/tests/HierarchyViewerTest/build.gradle
@@ -0,0 +1,31 @@
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.1.0+'
+
+ }
+}
+
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 21
+ buildToolsVersion "22.0.0"
+
+ defaultConfig {
+ minSdkVersion 21
+ targetSdkVersion 21
+ versionCode 1
+ versionName "1.0"
+ }
+
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src']
+ res.srcDirs = ['res']
+ }
+ }
+}
diff --git a/tests/HierarchyViewerTest/res/layout/activity_main.xml b/tests/HierarchyViewerTest/res/layout/activity_main.xml
new file mode 100644
index 0000000..410a776
--- /dev/null
+++ b/tests/HierarchyViewerTest/res/layout/activity_main.xml
@@ -0,0 +1,12 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/textView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:scaleX="10"
+ android:text="@string/test" />
+
+</RelativeLayout>
diff --git a/tests/HierarchyViewerTest/res/menu/menu_main.xml b/tests/HierarchyViewerTest/res/menu/menu_main.xml
new file mode 100644
index 0000000..9b78a1e
--- /dev/null
+++ b/tests/HierarchyViewerTest/res/menu/menu_main.xml
@@ -0,0 +1,5 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
+ <item android:id="@+id/action_settings" android:title="Settings"
+ android:orderInCategory="100" android:showAsAction="never" />
+</menu>
diff --git a/tests/HierarchyViewerTest/res/values/strings.xml b/tests/HierarchyViewerTest/res/values/strings.xml
new file mode 100644
index 0000000..800ee1c
--- /dev/null
+++ b/tests/HierarchyViewerTest/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="test">Hello World</string>
+</resources>
\ No newline at end of file
diff --git a/tests/HierarchyViewerTest/run_tests.sh b/tests/HierarchyViewerTest/run_tests.sh
new file mode 100644
index 0000000..094bb4c
--- /dev/null
+++ b/tests/HierarchyViewerTest/run_tests.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+# Runs the tests in this apk
+adb install $OUT/data/app/HierarchyViewerTest/HierarchyViewerTest.apk
+adb shell am instrument -w com.android.test.hierarchyviewer/android.test.InstrumentationTestRunner
diff --git a/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/Decoder.java b/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/Decoder.java
new file mode 100644
index 0000000..c6f1470
--- /dev/null
+++ b/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/Decoder.java
@@ -0,0 +1,101 @@
+package com.android.test.hierarchyviewer;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.HashMap;
+import java.util.Map;
+
+public class Decoder {
+ // Prefixes for simple primitives. These match the JNI definitions.
+ public static final byte SIG_BOOLEAN = 'Z';
+ public static final byte SIG_BYTE = 'B';
+ public static final byte SIG_SHORT = 'S';
+ public static final byte SIG_INT = 'I';
+ public static final byte SIG_LONG = 'J';
+ public static final byte SIG_FLOAT = 'F';
+ public static final byte SIG_DOUBLE = 'D';
+
+ // Prefixes for some commonly used objects
+ public static final byte SIG_STRING = 'R';
+
+ public static final byte SIG_MAP = 'M'; // a map with an short key
+ public static final short SIG_END_MAP = 0;
+
+ private final ByteBuffer mBuf;
+
+ public Decoder(byte[] buf) {
+ this(ByteBuffer.wrap(buf));
+ }
+
+ public Decoder(ByteBuffer buf) {
+ mBuf = buf;
+ }
+
+ public boolean hasRemaining() {
+ return mBuf.hasRemaining();
+ }
+
+ public Object readObject() {
+ byte sig = mBuf.get();
+
+ switch (sig) {
+ case SIG_BOOLEAN:
+ return mBuf.get() == 0 ? Boolean.FALSE : Boolean.TRUE;
+ case SIG_BYTE:
+ return mBuf.get();
+ case SIG_SHORT:
+ return mBuf.getShort();
+ case SIG_INT:
+ return mBuf.getInt();
+ case SIG_LONG:
+ return mBuf.getLong();
+ case SIG_FLOAT:
+ return mBuf.getFloat();
+ case SIG_DOUBLE:
+ return mBuf.getDouble();
+ case SIG_STRING:
+ return readString();
+ case SIG_MAP:
+ return readMap();
+ default:
+ throw new DecoderException(sig, mBuf.position() - 1);
+ }
+ }
+
+ private String readString() {
+ short len = mBuf.getShort();
+ byte[] b = new byte[len];
+ mBuf.get(b, 0, len);
+ return new String(b, Charset.forName("utf-8"));
+ }
+
+ private Map<Short, Object> readMap() {
+ Map<Short, Object> m = new HashMap<Short, Object>();
+
+ while (true) {
+ Object o = readObject();
+ if (!(o instanceof Short)) {
+ throw new DecoderException("Expected short key, got " + o.getClass());
+ }
+
+ Short key = (Short)o;
+ if (key == SIG_END_MAP) {
+ break;
+ }
+
+ m.put(key, readObject());
+ }
+
+ return m;
+ }
+
+ public static class DecoderException extends RuntimeException {
+ public DecoderException(byte seen, int pos) {
+ super(String.format("Unexpected byte %c seen at position %d", (char)seen, pos));
+ }
+
+ public DecoderException(String msg) {
+ super(msg);
+ }
+ }
+}
diff --git a/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/MainActivity.java b/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/MainActivity.java
new file mode 100644
index 0000000..3a67273
--- /dev/null
+++ b/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/MainActivity.java
@@ -0,0 +1,44 @@
+package com.android.test.hierarchyviewer;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+
+
+public class MainActivity extends Activity {
+ private static final String TAG = "Main";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ View textView = findViewById(R.id.textView);
+ Log.d(TAG, "x, y = " + textView.getX() + ", " + textView.getY());
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.menu_main, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ int id = item.getItemId();
+
+ //noinspection SimplifiableIfStatement
+ if (id == R.id.action_settings) {
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/MainActivityTest.java b/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/MainActivityTest.java
new file mode 100644
index 0000000..ea3710d
--- /dev/null
+++ b/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/MainActivityTest.java
@@ -0,0 +1,81 @@
+package com.android.test.hierarchyviewer;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.View;
+
+import java.io.ByteArrayOutputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Map;
+
+public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
+ private MainActivity mActivity;
+ private View mTextView;
+
+
+ public MainActivityTest() {
+ super(MainActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mActivity = getActivity();
+ mTextView = mActivity.findViewById(R.id.textView);
+ }
+
+ private byte[] encode(View view) throws ClassNotFoundException, NoSuchMethodException,
+ IllegalAccessException, InstantiationException, InvocationTargetException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(1024 * 1024);
+
+ Object encoder = createEncoder(baos);
+ invokeMethod(View.class, view, "encode", encoder);
+ invokeMethod(encoder.getClass(), encoder, "endStream");
+
+ return baos.toByteArray();
+ }
+
+ private Object invokeMethod(Class targetClass, Object target, String methodName, Object... params)
+ throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
+ Class[] paramClasses = new Class[params.length];
+ for (int i = 0; i < params.length; i++) {
+ paramClasses[i] = params[i].getClass();
+ }
+ Method method = targetClass.getDeclaredMethod(methodName, paramClasses);
+ method.setAccessible(true);
+ return method.invoke(target, params);
+ }
+
+ private Object createEncoder(ByteArrayOutputStream baos) throws ClassNotFoundException,
+ NoSuchMethodException, IllegalAccessException, InvocationTargetException,
+ InstantiationException {
+ Class clazz = Class.forName("android.view.ViewHierarchyEncoder");
+ Constructor constructor = clazz.getConstructor(ByteArrayOutputStream.class);
+ return constructor.newInstance(baos);
+ }
+
+ public void testTextView() throws Exception {
+ byte[] data = encode(mTextView);
+ assertNotNull(data);
+ assertTrue(data.length > 0);
+
+ ViewDumpParser parser = new ViewDumpParser();
+ parser.parse(data);
+
+ List<Map<Short, Object>> views = parser.getViews();
+ Map<String, Short> propertyNameTable = parser.getIds();
+
+ assertEquals(1, views.size());
+ assertNotNull(propertyNameTable);
+
+ Map<Short, Object> textViewProperties = views.get(0);
+ assertEquals("android.widget.TextView",
+ textViewProperties.get(propertyNameTable.get("meta:__name__")));
+
+ assertEquals(mActivity.getString(R.string.test),
+ textViewProperties.get(propertyNameTable.get("text:text")));
+ }
+}
diff --git a/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/ViewDumpParser.java b/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/ViewDumpParser.java
new file mode 100644
index 0000000..0111bc6
--- /dev/null
+++ b/tests/HierarchyViewerTest/src/com/android/test/hierarchyviewer/ViewDumpParser.java
@@ -0,0 +1,73 @@
+package com.android.test.hierarchyviewer;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+public class ViewDumpParser {
+ private Map<String, Short> mIds;
+ private List<Map<Short,Object>> mViews;
+
+ public void parse(byte[] data) {
+ Decoder d = new Decoder(ByteBuffer.wrap(data));
+
+ mViews = new ArrayList<>(100);
+ while (d.hasRemaining()) {
+ Object o = d.readObject();
+ if (o instanceof Map) {
+ //noinspection unchecked
+ mViews.add((Map<Short, Object>) o);
+ }
+ }
+
+ if (mViews.isEmpty()) {
+ return;
+ }
+
+ // the last one is the property map
+ Map<Short,Object> idMap = mViews.remove(mViews.size() - 1);
+ mIds = reverse(idMap);
+ }
+
+ public String getFirstView() {
+ if (mViews.isEmpty()) {
+ return null;
+ }
+
+ Map<Short, Object> props = mViews.get(0);
+ Object name = getProperty(props, "__name__");
+ Object hash = getProperty(props, "__hash__");
+
+ if (name instanceof String && hash instanceof Integer) {
+ return String.format(Locale.US, "%s@%x", name, hash);
+ } else {
+ return null;
+ }
+ }
+
+ private Object getProperty(Map<Short, Object> props, String key) {
+ return props.get(mIds.get(key));
+ }
+
+ private static Map<String, Short> reverse(Map<Short, Object> m) {
+ Map<String, Short> r = new HashMap<String, Short>(m.size());
+
+ for (Map.Entry<Short, Object> e : m.entrySet()) {
+ r.put((String)e.getValue(), e.getKey());
+ }
+
+ return r;
+ }
+
+ public List<Map<Short, Object>> getViews() {
+ return mViews;
+ }
+
+ public Map<String, Short> getIds() {
+ return mIds;
+ }
+
+}
diff --git a/tests/OneMedia/Android.mk b/tests/OneMedia/Android.mk
index b7d7f98..9fc6403 100644
--- a/tests/OneMedia/Android.mk
+++ b/tests/OneMedia/Android.mk
@@ -9,9 +9,6 @@
LOCAL_PACKAGE_NAME := OneMedia
LOCAL_CERTIFICATE := platform
-LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-media-protocols
-
LOCAL_JAVA_LIBRARIES += org.apache.http.legacy
LOCAL_PROGUARD_ENABLED := disabled
diff --git a/tests/OneMedia/AndroidManifest.xml b/tests/OneMedia/AndroidManifest.xml
index ef3fad5..c6824ec 100644
--- a/tests/OneMedia/AndroidManifest.xml
+++ b/tests/OneMedia/AndroidManifest.xml
@@ -27,15 +27,6 @@
android:name="com.android.onemedia.OnePlayerService"
android:exported="true"
android:process="com.android.onemedia.service" />
- <service
- android:name=".provider.OneMediaRouteProvider"
- android:permission="android.permission.BIND_MEDIA_ROUTE_SERVICE"
- android:exported="true"
- android:process="com.android.onemedia.provider">
- <intent-filter>
- <action android:name="android.media.routing.MediaRouteService" />
- </intent-filter>
- </service>
</application>
</manifest>
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
index 141a209..2455c9c 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
@@ -19,25 +19,17 @@
import android.content.Intent;
import android.graphics.Bitmap;
import android.media.MediaMetadata;
-import android.media.routing.MediaRouteSelector;
-import android.media.routing.MediaRouter;
-import android.media.routing.MediaRouter.ConnectionRequest;
-import android.media.routing.MediaRouter.DestinationInfo;
-import android.media.routing.MediaRouter.RouteInfo;
import android.media.session.MediaSession;
import android.media.session.MediaSession.QueueItem;
import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
import android.os.Bundle;
-import android.support.media.protocols.MediaPlayerProtocol;
-import android.support.media.protocols.MediaPlayerProtocol.MediaStatus;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
import android.view.KeyEvent;
import com.android.onemedia.playback.LocalRenderer;
-import com.android.onemedia.playback.OneMRPRenderer;
import com.android.onemedia.playback.Renderer;
import com.android.onemedia.playback.RequestUtils;
@@ -48,7 +40,6 @@
private static final String TAG = "PlayerSession";
protected MediaSession mSession;
- protected MediaRouter mRouter;
protected Context mContext;
protected Renderer mRenderer;
protected MediaSession.Callback mCallback;
@@ -84,22 +75,11 @@
.getSystemService(Context.MEDIA_SESSION_SERVICE);
Log.d(TAG, "Creating session for package " + mContext.getBasePackageName());
- mRouter = new MediaRouter(mContext);
- mRouter.addSelector(new MediaRouteSelector.Builder()
- .addRequiredProtocol(MediaPlayerProtocol.class)
- .build());
- mRouter.addSelector(new MediaRouteSelector.Builder()
- .setRequiredFeatures(MediaRouter.ROUTE_FEATURE_LIVE_AUDIO)
- .setOptionalFeatures(MediaRouter.ROUTE_FEATURE_LIVE_VIDEO)
- .build());
- mRouter.setRoutingCallback(new RoutingCallback(), null);
-
mSession = new MediaSession(mContext, "OneMedia");
mSession.setCallback(mCallback);
mSession.setPlaybackState(mPlaybackState);
mSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS
| MediaSession.FLAG_HANDLES_MEDIA_BUTTONS);
- mSession.setMediaRouter(mRouter);
mSession.setActive(true);
updateMetadata();
}
@@ -117,10 +97,6 @@
mSession.release();
mSession = null;
}
- if (mRouter != null) {
- mRouter.release();
- mRouter = null;
- }
}
public void setListener(Listener listener) {
@@ -278,63 +254,4 @@
mRenderer.onPause();
}
}
-
- private class RoutingCallback extends MediaRouter.RoutingCallback {
- @Override
- public void onConnectionStateChanged(int state) {
- if (state == MediaRouter.CONNECTION_STATE_CONNECTING) {
- if (mRenderer != null) {
- mRenderer.onStop();
- }
- mRenderer = null;
- updateState(PlaybackState.STATE_CONNECTING);
- return;
- }
-
- MediaRouter.ConnectionInfo connection = mRouter.getConnection();
- if (connection != null) {
- MediaPlayerProtocol protocol =
- connection.getProtocolObject(MediaPlayerProtocol.class);
- if (protocol != null) {
- Log.d(TAG, "Connected to route using media player protocol");
-
- protocol.setCallback(new PlayerCallback(), null);
- mRenderer = new OneMRPRenderer(protocol);
- updateState(PlaybackState.STATE_NONE);
- return;
- }
- }
-
- // Use local route
- mRenderer = new LocalRenderer(mContext, null);
- mRenderer.registerListener(mRenderListener);
- updateState(PlaybackState.STATE_NONE);
- }
- }
-
- private class PlayerCallback extends MediaPlayerProtocol.Callback {
- @Override
- public void onStatusUpdated(MediaStatus status, Bundle extras) {
- if (status != null) {
- Log.d(TAG, "Received status update: " + status.toBundle());
- switch (status.getPlayerState()) {
- case MediaStatus.PLAYER_STATE_BUFFERING:
- updateState(PlaybackState.STATE_BUFFERING);
- break;
- case MediaStatus.PLAYER_STATE_IDLE:
- updateState(PlaybackState.STATE_STOPPED);
- break;
- case MediaStatus.PLAYER_STATE_PAUSED:
- updateState(PlaybackState.STATE_PAUSED);
- break;
- case MediaStatus.PLAYER_STATE_PLAYING:
- updateState(PlaybackState.STATE_PLAYING);
- break;
- case MediaStatus.PLAYER_STATE_UNKNOWN:
- updateState(PlaybackState.STATE_NONE);
- break;
- }
- }
- }
- }
}
diff --git a/tests/OneMedia/src/com/android/onemedia/playback/OneMRPRenderer.java b/tests/OneMedia/src/com/android/onemedia/playback/OneMRPRenderer.java
deleted file mode 100644
index 55eb92c..0000000
--- a/tests/OneMedia/src/com/android/onemedia/playback/OneMRPRenderer.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.android.onemedia.playback;
-
-import android.os.Bundle;
-import android.support.media.protocols.MediaPlayerProtocol;
-import android.support.media.protocols.MediaPlayerProtocol.MediaInfo;
-
-/**
- * Renderer for communicating with the OneMRP route
- */
-public class OneMRPRenderer extends Renderer {
- private final MediaPlayerProtocol mProtocol;
-
- public OneMRPRenderer(MediaPlayerProtocol protocol) {
- super(null, null);
- mProtocol = protocol;
- }
-
- @Override
- public void setContent(Bundle request) {
- MediaInfo mediaInfo = new MediaInfo(request.getString(RequestUtils.EXTRA_KEY_SOURCE),
- MediaInfo.STREAM_TYPE_BUFFERED, "audio/mp3");
- mProtocol.load(mediaInfo, true, 0, null);
- }
-
- @Override
- public boolean onStop() {
- mProtocol.stop(null);
- return true;
- }
-
- @Override
- public boolean onPlay() {
- mProtocol.play(null);
- return true;
- }
-
- @Override
- public boolean onPause() {
- mProtocol.pause(null);
- return true;
- }
-
- @Override
- public long getSeekPosition() {
- return -1;
- }
-}
diff --git a/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java b/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java
deleted file mode 100644
index 5845e48..0000000
--- a/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java
+++ /dev/null
@@ -1,270 +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.onemedia.provider;
-
-import android.media.routing.MediaRouteSelector;
-import android.media.routing.MediaRouteService;
-import android.media.routing.MediaRouter.ConnectionInfo;
-import android.media.routing.MediaRouter.ConnectionRequest;
-import android.media.routing.MediaRouter.DestinationInfo;
-import android.media.routing.MediaRouter.DiscoveryRequest;
-import android.media.routing.MediaRouter.RouteInfo;
-import android.media.session.PlaybackState;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Process;
-import android.support.media.protocols.MediaPlayerProtocol;
-import android.support.media.protocols.MediaPlayerProtocol.MediaInfo;
-import android.support.media.protocols.MediaPlayerProtocol.MediaStatus;
-import android.os.Looper;
-import android.os.ResultReceiver;
-import android.os.SystemClock;
-import android.util.Log;
-
-import com.android.onemedia.playback.LocalRenderer;
-import com.android.onemedia.playback.Renderer;
-import com.android.onemedia.playback.RequestUtils;
-
-import java.util.ArrayList;
-
-/**
- * Test of MediaRouteProvider. Show a dummy provider with a simple interface for
- * playing music.
- */
-public class OneMediaRouteProvider extends MediaRouteService {
- private static final String TAG = "OneMRP";
- private static final boolean DEBUG = true;
-
- private static final String TEST_DESTINATION_ID = "testDestination";
- private static final String TEST_ROUTE_ID = "testRoute";
-
- private Renderer mRenderer;
- private RenderListener mRenderListener;
- private PlaybackState mPlaybackState;
- private Handler mHandler;
-
- private OneStub mStub;
-
- @Override
- public void onCreate() {
- mHandler = new Handler();
- mRenderer = new LocalRenderer(this, null);
- mRenderListener = new RenderListener();
- PlaybackState.Builder bob = new PlaybackState.Builder();
- bob.setActions(PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY);
- mPlaybackState = bob.build();
-
- mRenderer.registerListener(mRenderListener);
- }
-
- @Override
- public ClientSession onCreateClientSession(ClientInfo client) {
- if (client.getUid() != Process.myUid()) {
- // for testing purposes, only allow connections from this application
- // since this provider is not fully featured
- return null;
- }
- return new OneSession(client);
- }
-
- private final class OneSession extends ClientSession {
- private final ClientInfo mClient;
-
- public OneSession(ClientInfo client) {
- mClient = client;
- }
-
- @Override
- public boolean onStartDiscovery(DiscoveryRequest req, DiscoveryCallback callback) {
- for (MediaRouteSelector selector : req.getSelectors()) {
- if (isMatch(selector)) {
- DestinationInfo destination = new DestinationInfo.Builder(
- TEST_DESTINATION_ID, getServiceMetadata(), "OneMedia")
- .setDescription("Test route from OneMedia app.")
- .build();
- ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>();
- routes.add(new RouteInfo.Builder(
- TEST_ROUTE_ID, destination, selector).build());
- callback.onDestinationFound(destination, routes);
- return true;
- }
- }
- return false;
- }
-
- @Override
- public void onStopDiscovery() {
- }
-
- @Override
- public boolean onConnect(ConnectionRequest req, ConnectionCallback callback) {
- if (req.getRoute().getId().equals(TEST_ROUTE_ID)) {
- mStub = new OneStub();
- ConnectionInfo connection = new ConnectionInfo.Builder(req.getRoute())
- .setProtocolStub(MediaPlayerProtocol.class, mStub)
- .build();
- callback.onConnected(connection);
- return true;
- }
- return false;
- }
-
- @Override
- public void onDisconnect() {
- mStub = null;
- }
-
- private boolean isMatch(MediaRouteSelector selector) {
- if (!selector.containsProtocol(MediaPlayerProtocol.class)) {
- return false;
- }
- for (String protocol : selector.getRequiredProtocols()) {
- if (!protocol.equals(MediaPlayerProtocol.class.getName())) {
- return false;
- }
- }
- return true;
- }
- }
-
- private final class OneStub extends MediaPlayerProtocol.Stub {
- MediaInfo mMediaInfo;
-
- public OneStub() {
- super(mHandler);
- }
-
- @Override
- public void onLoad(MediaInfo mediaInfo, boolean autoplay, long playPosition,
- Bundle extras) {
- if (DEBUG) {
- Log.d(TAG, "Attempting to play " + mediaInfo.getContentId());
- }
- // look up the route and send a play command to it
- mMediaInfo = mediaInfo;
- Bundle bundle = new Bundle();
- bundle.putString(RequestUtils.EXTRA_KEY_SOURCE, mediaInfo.getContentId());
- mRenderer.setContent(bundle);
- }
-
- @Override
- public void onPlay(Bundle extras) {
- mRenderer.onPlay();
- }
-
- @Override
- public void onPause(Bundle extras) {
- mRenderer.onPause();
- }
- }
-
- private class RenderListener implements Renderer.Listener {
-
- @Override
- public void onError(int type, int extra, Bundle extras, Throwable error) {
- Log.d(TAG, "Sending onError with type " + type + " and extra " + extra);
- sendStatusUpdate(PlaybackState.STATE_ERROR);
- }
-
- @Override
- public void onStateChanged(int newState) {
- long position = -1;
- if (mRenderer != null) {
- position = mRenderer.getSeekPosition();
- }
- int pbState;
- float rate = 0;
- String errorMsg = null;
- switch (newState) {
- case Renderer.STATE_ENDED:
- case Renderer.STATE_STOPPED:
- pbState = PlaybackState.STATE_STOPPED;
- break;
- case Renderer.STATE_INIT:
- case Renderer.STATE_PREPARING:
- pbState = PlaybackState.STATE_BUFFERING;
- break;
- case Renderer.STATE_ERROR:
- pbState = PlaybackState.STATE_ERROR;
- break;
- case Renderer.STATE_PAUSED:
- pbState = PlaybackState.STATE_PAUSED;
- break;
- case Renderer.STATE_PLAYING:
- pbState = PlaybackState.STATE_PLAYING;
- rate = 1;
- break;
- default:
- pbState = PlaybackState.STATE_ERROR;
- errorMsg = "unknown state";
- break;
- }
- PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState);
- bob.setState(pbState, position, rate, SystemClock.elapsedRealtime());
- bob.setErrorMessage(errorMsg);
- mPlaybackState = bob.build();
-
- sendStatusUpdate(mPlaybackState.getState());
- }
-
- @Override
- public void onBufferingUpdate(int percent) {
- }
-
- @Override
- public void onFocusLost() {
- Log.d(TAG, "Focus lost, pausing");
- // Don't update state here, we'll get a separate call to
- // onStateChanged when it pauses
- mRenderer.onPause();
- }
-
- @Override
- public void onNextStarted() {
- }
-
- private void sendStatusUpdate(int state) {
- if (mStub != null) {
- MediaStatus status = new MediaStatus(1, mStub.mMediaInfo);
- switch (state) {
- case PlaybackState.STATE_BUFFERING:
- case PlaybackState.STATE_FAST_FORWARDING:
- case PlaybackState.STATE_REWINDING:
- case PlaybackState.STATE_SKIPPING_TO_NEXT:
- case PlaybackState.STATE_SKIPPING_TO_PREVIOUS:
- status.setPlayerState(MediaStatus.PLAYER_STATE_BUFFERING);
- break;
- case PlaybackState.STATE_CONNECTING:
- case PlaybackState.STATE_STOPPED:
- status.setPlayerState(MediaStatus.PLAYER_STATE_IDLE);
- break;
- case PlaybackState.STATE_PAUSED:
- status.setPlayerState(MediaStatus.PLAYER_STATE_PAUSED);
- break;
- case PlaybackState.STATE_PLAYING:
- status.setPlayerState(MediaStatus.PLAYER_STATE_PLAYING);
- break;
- case PlaybackState.STATE_NONE:
- case PlaybackState.STATE_ERROR:
- default:
- status.setPlayerState(MediaStatus.PLAYER_STATE_UNKNOWN);
- break;
- }
- mStub.sendStatusUpdatedEvent(status, null);
- }
- }
- }
-}
diff --git a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java
index 4bd83e9..41d94b7 100644
--- a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java
@@ -27,6 +27,7 @@
import android.content.res.Resources.NotFoundException;
import android.content.res.Resources.Theme;
+import android.content.res.Resources.ThemeKey;
import android.util.AttributeSet;
import android.util.TypedValue;
@@ -110,22 +111,16 @@
private static boolean setupResources(Theme thisTheme) {
// Key is a space-separated list of theme ids applied that have been merged into the
// BridgeContext's theme to make thisTheme.
- String[] appliedStyles = thisTheme.getKey().split(" ");
+ final ThemeKey key = thisTheme.getKey();
+ final int[] resId = key.mResId;
+ final boolean[] force = key.mForce;
+
boolean changed = false;
- for (String s : appliedStyles) {
- if (s.isEmpty()) {
- continue;
- }
- // See the definition of force parameter in Theme.applyStyle().
- boolean force = false;
- if (s.charAt(s.length() - 1) == '!') {
- force = true;
- s = s.substring(0, s.length() - 1);
- }
- int styleId = Integer.parseInt(s, 16);
- StyleResourceValue style = resolveStyle(styleId);
+ for (int i = 0, N = key.mCount; i < N; i++) {
+ StyleResourceValue style = resolveStyle(resId[i]);
if (style != null) {
- RenderSessionImpl.getCurrentContext().getRenderResources().applyStyle(style, force);
+ RenderSessionImpl.getCurrentContext().getRenderResources().applyStyle(
+ style, force[i]);
changed = true;
}
diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
index 9eea663..87762a6 100644
--- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
@@ -16,19 +16,15 @@
package android.view;
-import com.android.ide.common.rendering.api.LayoutlibCallback;
import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.LayoutlibCallback;
import com.android.ide.common.rendering.api.MergeCookie;
import com.android.ide.common.rendering.api.ResourceReference;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.BridgeConstants;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
-import com.android.layoutlib.bridge.android.support.RecyclerViewUtil;
-import com.android.layoutlib.bridge.android.support.RecyclerViewUtil.LayoutManagerType;
import com.android.layoutlib.bridge.impl.ParserFactory;
-import com.android.layoutlib.bridge.impl.RenderSessionImpl;
import com.android.resources.ResourceType;
import com.android.util.Pair;
@@ -233,22 +229,6 @@
if (viewKey != null) {
bc.addViewKey(view, viewKey);
}
- if (RenderSessionImpl.isInstanceOf(view, RecyclerViewUtil.CN_RECYCLER_VIEW)) {
- String type = attrs.getAttributeValue(BridgeConstants.NS_RESOURCES,
- BridgeConstants.ATTR_LAYOUT_MANAGER_TYPE);
- if (type != null) {
- LayoutManagerType layoutManagerType = LayoutManagerType.getByLogicalName(type);
- if (layoutManagerType == null) {
- layoutManagerType = LayoutManagerType.getByClassName(type);
- }
- if (layoutManagerType == null) {
- // add the classname itself.
- bc.addCookie(view, type);
- } else {
- bc.addCookie(view, layoutManagerType);
- }
- }
- }
}
}
diff --git a/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java b/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java
index ec3a8d6..30512aa 100644
--- a/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java
+++ b/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java
@@ -19,6 +19,7 @@
import com.android.layoutlib.bridge.impl.ResourceHelper;
import android.graphics.Canvas;
+import android.graphics.Canvas_Delegate;
import android.graphics.LinearGradient;
import android.graphics.Outline;
import android.graphics.Paint;
@@ -125,6 +126,9 @@
private static void sideShadow(Canvas canvas, Paint edgePaint,
RectF edgeShadowRect, float dx, float dy, int rotations) {
+ if (isRectEmpty(edgeShadowRect)) {
+ return;
+ }
int saved = canvas.save();
canvas.translate(dx, dy);
canvas.rotate(rotations * PERPENDICULAR_ANGLE);
@@ -153,4 +157,15 @@
canvas.drawPath(path, paint);
canvas.restoreToCount(saved);
}
+
+ /**
+ * Differs from {@link RectF#isEmpty()} as this first converts the rect to int and then checks.
+ * <p/>
+ * This is required because {@link Canvas_Delegate#native_drawRect(long, float, float, float,
+ * float, long)} casts the co-ordinates to int and we want to ensure that it doesn't end up
+ * drawing empty rectangles, which results in IllegalArgumentException.
+ */
+ private static boolean isRectEmpty(RectF rect) {
+ return (int) rect.left >= (int) rect.right || (int) rect.top >= (int) rect.bottom;
+ }
}
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 094778d..eb5f597 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
@@ -93,6 +93,8 @@
import java.util.List;
import java.util.Map;
+import static com.android.layoutlib.bridge.android.RenderParamsFlags.FLAG_KEY_APPLICATION_PACKAGE;
+
/**
* Custom implementation of Context/Activity to handle non compiled resources.
*/
@@ -306,7 +308,7 @@
// check if this is a style resource
if (value instanceof StyleResourceValue) {
// get the id that will represent this style.
- outValue.resourceId = getDynamicIdByStyle((StyleResourceValue)value);
+ outValue.resourceId = getDynamicIdByStyle((StyleResourceValue) value);
return true;
}
@@ -812,6 +814,14 @@
}
+ @Override
+ public String getPackageName() {
+ if (mApplicationInfo.packageName == null) {
+ mApplicationInfo.packageName = mLayoutlibCallback.getFlag(FLAG_KEY_APPLICATION_PACKAGE);
+ }
+ return mApplicationInfo.packageName;
+ }
+
// ------------- private new methods
/**
@@ -1225,12 +1235,6 @@
}
@Override
- public String getPackageName() {
- // pass
- return null;
- }
-
- @Override
public String getBasePackageName() {
// pass
return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
index c44a57c..8899e53 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
@@ -164,7 +164,8 @@
}
@Override
- public void showInputMethodPickerFromClient(IInputMethodClient arg0) throws RemoteException {
+ public void showInputMethodPickerFromClient(IInputMethodClient arg0,
+ int arg1) throws RemoteException {
// TODO Auto-generated method stub
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
index 2f45473..b98f96f 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
@@ -16,6 +16,7 @@
package com.android.layoutlib.bridge.android;
+import com.android.ide.common.rendering.api.RenderParams;
import com.android.ide.common.rendering.api.SessionParams.Key;
/**
@@ -31,10 +32,21 @@
new Key<String>("rootTag", String.class);
public static final Key<Boolean> FLAG_KEY_DISABLE_BITMAP_CACHING =
new Key<Boolean>("disableBitmapCaching", Boolean.class);
- public static final Key<Boolean> FLAG_KEY_RECYCLER_VIEW_SUPPORT =
- new Key<Boolean>("recyclerViewSupport", Boolean.class);
public static final Key<Boolean> FLAG_KEY_RENDER_ALL_DRAWABLE_STATES =
new Key<Boolean>("renderAllDrawableStates", Boolean.class);
+ /**
+ * To tell LayoutLib that the IDE supports RecyclerView.
+ * <p/>
+ * Default is false.
+ */
+ public static final Key<Boolean> FLAG_KEY_RECYCLER_VIEW_SUPPORT =
+ new Key<Boolean>("recyclerViewSupport", Boolean.class);
+ /**
+ * The application package name. Used via
+ * {@link com.android.ide.common.rendering.api.LayoutlibCallback#getFlag(Key)}
+ */
+ public static final Key<String> FLAG_KEY_APPLICATION_PACKAGE =
+ new Key<String>("applicationPackage", String.class);
// Disallow instances.
private RenderParamsFlags() {}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
index 9273ac2..e4c7288 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
@@ -27,11 +27,12 @@
import android.content.Context;
import android.view.View;
-import android.widget.LinearLayout;
import java.lang.reflect.Method;
-import static com.android.layoutlib.bridge.util.ReflectionUtils.*;
+import static com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException;
+import static com.android.layoutlib.bridge.util.ReflectionUtils.getMethod;
+import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke;
/**
* Utility class for working with android.support.v7.widget.RecyclerView
@@ -39,17 +40,15 @@
@SuppressWarnings("SpellCheckingInspection") // for "recycler".
public class RecyclerViewUtil {
- /**
- * Used by {@link LayoutManagerType}.
- * <p/>
- * Not declared inside the enum, since it needs to be accessible in the constructor.
- */
- private static final Object CONTEXT = new Object();
-
- public static final String CN_RECYCLER_VIEW = "android.support.v7.widget.RecyclerView";
+ private static final String RV_PKG_PREFIX = "android.support.v7.widget.";
+ public static final String CN_RECYCLER_VIEW = RV_PKG_PREFIX + "RecyclerView";
private static final String CN_LAYOUT_MANAGER = CN_RECYCLER_VIEW + "$LayoutManager";
private static final String CN_ADAPTER = CN_RECYCLER_VIEW + "$Adapter";
+ // LinearLayoutManager related constants.
+ private static final String CN_LINEAR_LAYOUT_MANAGER = RV_PKG_PREFIX + "LinearLayoutManager";
+ private static final Class<?>[] LLM_CONSTRUCTOR_SIGNATURE = new Class<?>[]{Context.class};
+
/**
* Tries to create an Adapter ({@code android.support.v7.widget.RecyclerView.Adapter} and a
* LayoutManager {@code RecyclerView.LayoutManager} and assign these to the {@code RecyclerView}
@@ -71,37 +70,33 @@
private static void setLayoutManager(@NonNull View recyclerView, @NonNull BridgeContext context,
@NonNull LayoutlibCallback callback) throws ReflectionException {
- Object cookie = context.getCookie(recyclerView);
- assert cookie == null || cookie instanceof LayoutManagerType || cookie instanceof String;
- if (!(cookie instanceof LayoutManagerType)) {
- if (cookie != null) {
- // TODO: When layoutlib API is updated, try to load the class with a null
- // constructor or a constructor taking one argument - the context.
- Bridge.getLog().warning(LayoutLog.TAG_UNSUPPORTED,
- "LayoutManager (" + cookie + ") not found, falling back to " +
- "LinearLayoutManager", null);
- }
- cookie = LayoutManagerType.getDefault();
+ if (getLayoutManager(recyclerView) == null) {
+ // Only set the layout manager if not already set by the recycler view.
+ Object layoutManager = createLayoutManager(context, callback);
+ setProperty(recyclerView, CN_LAYOUT_MANAGER, layoutManager, "setLayoutManager");
}
- Object layoutManager = createLayoutManager((LayoutManagerType) cookie, context, callback);
- setProperty(recyclerView, CN_LAYOUT_MANAGER, layoutManager, "setLayoutManager");
}
+ /** Creates a LinearLayoutManager using the provided context. */
@Nullable
- private static Object createLayoutManager(@Nullable LayoutManagerType type,
- @NonNull Context context, @NonNull LayoutlibCallback callback)
+ private static Object createLayoutManager(@NonNull Context context,
+ @NonNull LayoutlibCallback callback)
throws ReflectionException {
- if (type == null) {
- type = LayoutManagerType.getDefault();
- }
try {
- return callback.loadView(type.getClassName(), type.getSignature(), type.getArgs(context));
+ return callback.loadView(CN_LINEAR_LAYOUT_MANAGER, LLM_CONSTRUCTOR_SIGNATURE,
+ new Object[]{ context});
} catch (Exception e) {
throw new ReflectionException(e);
}
}
@Nullable
+ private static Object getLayoutManager(View recyclerview) throws ReflectionException {
+ Method getLayoutManager = getMethod(recyclerview.getClass(), "getLayoutManager");
+ return getLayoutManager != null ? invoke(getLayoutManager, recyclerview) : null;
+ }
+
+ @Nullable
private static Object createAdapter(@NonNull SessionParams params) throws ReflectionException {
Boolean ideSupport = params.getFlag(RenderParamsFlags.FLAG_KEY_RECYCLER_VIEW_SUPPORT);
if (ideSupport != Boolean.TRUE) {
@@ -145,74 +140,4 @@
}
throw new RuntimeException("invalid object/classname combination.");
}
-
- /** Supported LayoutManagers. */
- public enum LayoutManagerType {
- LINEAR_LAYOUT_MANGER("Linear",
- "android.support.v7.widget.LinearLayoutManager",
- new Class[]{Context.class}, new Object[]{CONTEXT}),
- GRID_LAYOUT_MANAGER("Grid",
- "android.support.v7.widget.GridLayoutManager",
- new Class[]{Context.class, int.class}, new Object[]{CONTEXT, 2}),
- STAGGERED_GRID_LAYOUT_MANAGER("StaggeredGrid",
- "android.support.v7.widget.StaggeredGridLayoutManager",
- new Class[]{int.class, int.class}, new Object[]{2, LinearLayout.VERTICAL});
-
- private String mLogicalName;
- private String mClassName;
- private Class[] mSignature;
- private Object[] mArgs;
-
- LayoutManagerType(String logicalName, String className, Class[] signature, Object[] args) {
- mLogicalName = logicalName;
- mClassName = className;
- mSignature = signature;
- mArgs = args;
- }
-
- String getClassName() {
- return mClassName;
- }
-
- Class[] getSignature() {
- return mSignature;
- }
-
- @NonNull
- Object[] getArgs(Context context) {
- Object[] args = new Object[mArgs.length];
- System.arraycopy(mArgs, 0, args, 0, mArgs.length);
- for (int i = 0; i < args.length; i++) {
- if (args[i] == CONTEXT) {
- args[i] = context;
- }
- }
- return args;
- }
-
- @NonNull
- public static LayoutManagerType getDefault() {
- return LINEAR_LAYOUT_MANGER;
- }
-
- @Nullable
- public static LayoutManagerType getByLogicalName(@NonNull String logicalName) {
- for (LayoutManagerType type : values()) {
- if (logicalName.equals(type.mLogicalName)) {
- return type;
- }
- }
- return null;
- }
-
- @Nullable
- public static LayoutManagerType getByClassName(@NonNull String className) {
- for (LayoutManagerType type : values()) {
- if (className.equals(type.mClassName)) {
- return type;
- }
- }
- return null;
- }
- }
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java
index 9f9b968..dc89d0c 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java
@@ -74,7 +74,7 @@
}
public static String getTime(int platformVersion) {
- if (platformVersion == 0) {
+ if (isGreaterOrEqual(platformVersion, LOLLIPOP_MR1)) {
return "5:10";
}
if (platformVersion < GINGERBREAD) {
@@ -117,7 +117,7 @@
}
public static String getWifiIconType(int platformVersion) {
- return platformVersion == 0 ? "xml" : "png";
+ return isGreaterOrEqual(platformVersion, LOLLIPOP) ? "xml" : "png";
}
/**
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
index 9450b6c..04aadff 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
@@ -21,6 +21,10 @@
import org.xmlpull.v1.XmlPullParserException;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.util.AttributeSet;
+import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -29,6 +33,21 @@
/** Navigation bar background color attribute name. */
private static final String ATTR_COLOR = "navigationBarColor";
+ /**
+ * Constructor to be used when creating the {@link NavigationBar} as a regular control.
+ * This is currently used by the theme editor.
+ */
+ public NavigationBar(Context context, AttributeSet attrs)
+ throws XmlPullParserException {
+ this((BridgeContext) context,
+ Density.getEnum(((BridgeContext) context).getMetrics().densityDpi),
+ LinearLayout.HORIZONTAL, // In this mode, it doesn't need to be render vertically
+ ((BridgeContext) context).getConfiguration().getLayoutDirection() ==
+ View.LAYOUT_DIRECTION_RTL,
+ (context.getApplicationInfo().flags & ApplicationInfo.FLAG_SUPPORTS_RTL) != 0,
+ context.getApplicationInfo().targetSdkVersion);
+ }
+
public NavigationBar(BridgeContext context, Density density, int orientation, boolean isRtl,
boolean rtlEnabled, int simulatedPlatformVersion) throws XmlPullParserException {
super(context, orientation, "/bars/navigation_bar.xml", "navigation_bar.xml",
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
index e5f1f68..a0ed0e8 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
@@ -25,7 +25,9 @@
import org.xmlpull.v1.XmlPullParserException;
+import android.content.Context;
import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.widget.ImageView;
@@ -39,7 +41,20 @@
private final int mSimulatedPlatformVersion;
/** Status bar background color attribute name. */
- private static final String ATTR_COLOR = "colorPrimaryDark";
+ private static final String ATTR_COLOR = "statusBarColor";
+
+ /**
+ * Constructor to be used when creating the {@link StatusBar} as a regular control. This
+ * is currently used by the theme editor.
+ */
+ public StatusBar(Context context, AttributeSet attrs) throws XmlPullParserException {
+ this((BridgeContext) context,
+ Density.getEnum(((BridgeContext) context).getMetrics().densityDpi),
+ LinearLayout.HORIZONTAL, // In this mode, it doesn't need to be render vertically
+ ((BridgeContext) context).getConfiguration().getLayoutDirection() ==
+ View.LAYOUT_DIRECTION_RTL,
+ context.getApplicationInfo().targetSdkVersion);
+ }
public StatusBar(BridgeContext context, Density density, int direction, boolean RtlEnabled,
int simulatedPlatformVersion) throws XmlPullParserException {
@@ -50,6 +65,7 @@
// FIXME: use FILL_H?
setGravity(Gravity.START | Gravity.TOP | Gravity.RIGHT);
+
int color = getThemeAttrColor(ATTR_COLOR, true);
setBackgroundColor(color == 0 ? Config.getStatusBarColor(simulatedPlatformVersion) : color);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
index 3dee1e2..26f9000 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
@@ -42,6 +42,10 @@
import java.util.Collections;
import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
/**
* Action to render a given Drawable provided through {@link DrawableParams#getDrawable()}.
*
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 d9572591..f6e5ef1 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
@@ -1075,7 +1075,7 @@
private void findStatusBar(RenderResources resources, DisplayMetrics metrics) {
boolean windowFullscreen = getBooleanThemeValue(resources,
- "windowFullscreen", false, !isThemeAppCompat(resources));
+ "windowFullscreen", false, true);
if (!windowFullscreen && !mWindowIsFloating) {
// default value
@@ -1210,15 +1210,15 @@
// between Theme.AppCompat.Light and Theme.AppCompat is Theme.Material (for v21).
boolean isThemeAppCompat = false;
for (int i = 0; i < 50; i++) {
+ if (defaultTheme == null) {
+ break;
+ }
// 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;
}
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class
index d252462..8af93eb 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class
index d109302..069f9f7 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class
index 816ecc8..36e2688 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class
index b034b75..ca438ad 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class
index f86b1d3..a98abf5 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class
index 8bbae90..7d8cc84 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class
index 8af745d..7e6113b 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
new file mode 100644
index 0000000..c9b76be
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml
new file mode 100644
index 0000000..2da2cb9
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml
@@ -0,0 +1,393 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:ignore="HardcodedText,LabelFor,TextFields,ContentDescription,RtlHardcoded">
+
+ <FrameLayout
+ android:id="@id/frameLayout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginEnd="311dp">
+
+ <TextView
+ android:id="@id/textView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left|top"
+ android:text="New Text" />
+ </FrameLayout>
+
+ <TextView
+ android:id="@id/textView2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_below="@id/frameLayout"
+ android:text="Large Text"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <TextView
+ android:id="@id/textView3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_toEndOf="@id/textView2"
+ android:text="Medium Text"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <TextView
+ android:id="@id/textView4"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@id/textView2"
+ android:layout_toEndOf="@id/textView2"
+ android:text="Small Text"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ <Button
+ android:id="@id/button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/textView3"
+ android:layout_toEndOf="@id/textView4"
+ android:text="New Button" />
+
+ <Button
+ android:id="@id/button2"
+ style="?android:attr/buttonStyleSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_toEndOf="@id/button"
+ android:text="New Button" />
+
+ <CheckBox
+ android:id="@id/checkBox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignEnd="@id/button"
+ android:layout_below="@id/button"
+ android:text="New CheckBox" />
+
+ <Switch
+ android:id="@id/switch1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_below="@id/textView2"
+ android:text="New Switch" />
+
+ <ImageButton
+ android:id="@id/imageButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/button"
+ android:layout_toEndOf="@id/switch1" />
+
+ <ImageView
+ android:id="@id/imageView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_launcher"
+ android:layout_below="@id/button"
+ android:layout_toEndOf="@id/imageButton" />
+
+ <GridLayout
+ android:id="@id/gridLayout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_below="@id/imageButton"
+ android:columnCount="2"
+ android:rowCount="2">
+
+ <ProgressBar
+ android:id="@id/progressBar"
+ style="?android:attr/progressBarStyleLarge"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_column="0"
+ android:layout_row="0" />
+
+ <ProgressBar
+ android:id="@id/progressBar2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_column="1"
+ android:layout_row="0" />
+
+ <ProgressBar
+ android:id="@id/progressBar3"
+ style="?android:attr/progressBarStyleSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_column="0"
+ android:layout_row="1" />
+
+ <ProgressBar
+ android:id="@id/progressBar4"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_column="1"
+ android:layout_row="1" />
+ </GridLayout>
+
+ <SeekBar
+ android:id="@id/seekBar"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@id/gridLayout"
+ android:layout_toEndOf="@id/gridLayout" />
+
+ <RatingBar
+ android:id="@id/ratingBar"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/switch2"
+ android:layout_toEndOf="@id/gridLayout" />
+
+ <Switch
+ android:id="@id/switch2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/seekBar"
+ android:layout_toEndOf="@id/switch1"
+ android:checked="true" />
+
+ <EditText
+ android:id="@id/editText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBottom="@id/ratingBar"
+ android:layout_alignParentStart="true"
+ android:text="plain text" />
+
+ <EditText
+ android:id="@id/editText2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_below="@id/ratingBar"
+ android:ems="3"
+ android:inputType="textPersonName"
+ android:text="Name" />
+
+ <EditText
+ android:id="@id/editText3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_toEndOf="@id/editText2"
+ android:ems="2"
+ android:inputType="textPassword"
+ android:text="password" />
+
+ <EditText
+ android:id="@id/editText4"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@id/editText3"
+ android:layout_toEndOf="@id/editText3"
+ android:ems="3"
+ android:inputType="numberPassword"
+ android:text="numeric password" />
+
+ <EditText
+ android:id="@id/editText5"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/editText3"
+ android:layout_toStartOf="@id/editText6"
+ android:ems="7"
+ android:inputType="textEmailAddress"
+ android:text="email@domain.com" />
+
+ <EditText
+ android:id="@id/editText6"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_below="@id/editText4"
+ android:ems="7"
+ android:inputType="phone"
+ android:text="+11235554344" />
+
+ <EditText
+ android:id="@id/editText7"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/editText"
+ android:layout_toEndOf="@id/editText4"
+ android:ems="10"
+ android:inputType="textPostalAddress"
+ android:text="1600 Amphitheatre" />
+
+ <EditText
+ android:id="@id/editText9"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/editText5"
+ android:layout_alignParentStart="true"
+ android:ems="3"
+ android:inputType="time"
+ android:text="12:12" />
+
+ <RadioGroup
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/editText5"
+ android:layout_toEndOf="@id/editText9"
+ android:orientation="horizontal">
+
+ <RadioButton
+ android:id="@id/radioButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="New RadioButton" />
+
+ <RadioButton
+ android:id="@id/radioButton2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="New RadioButton" />
+
+ </RadioGroup>
+
+ <CheckedTextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="CheckedTextView"
+ android:id="@id/checkedTextView"
+ android:layout_below="@id/button2"
+ android:layout_alignParentEnd="true"
+ android:layout_alignStart="@id/button2" />
+
+ <DialerFilter
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/checkBox"
+ android:layout_toStartOf="@id/quickContactBadge"
+ android:id="@id/dialerFilter"
+ android:layout_above="@id/ratingBar"
+ android:layout_toEndOf="@id/seekBar">
+
+ <EditText
+ android:id="@android:id/hint"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Hint" />
+
+ <EditText
+ android:id="@android:id/primary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/hint"
+ android:text="Primary" />
+ </DialerFilter>
+
+ <QuickContactBadge
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@id/quickContactBadge"
+ android:layout_below="@id/checkedTextView"
+ android:layout_alignParentEnd="true" />
+
+ <android.inputmethodservice.ExtractEditText
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="ExtractEditText"
+ android:id="@id/extractEditText"
+ android:layout_below="@id/editText9"
+ android:layout_alignParentEnd="true"
+ android:layout_alignStart="@id/checkedTextView" />
+
+ <ZoomControls
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@id/zoomControls"
+ android:layout_below="@id/editText9"
+ android:layout_alignParentStart="true" />
+
+ <TextureView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@id/textureView"
+ android:layout_below="@id/zoomControls"
+ android:layout_alignParentStart="true"
+ android:layout_alignBottom="@id/extractEditText"
+ android:layout_toStartOf="@id/editText3" />
+
+ <ListView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@id/listView"
+ android:layout_below="@id/textureView"
+ android:layout_alignParentStart="true"
+ android:layout_alignEnd="@id/textureView" />
+
+ <GridView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@id/gridView"
+ android:layout_below="@id/extractEditText"
+ android:layout_alignParentEnd="true"
+ android:layout_alignStart="@id/extractEditText" />
+
+ <ScrollView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@id/scrollView"
+ android:layout_below="@id/zoomControls"
+ android:layout_toRightOf="@id/listView"
+ android:layout_toLeftOf="@id/extractEditText">
+
+ <TabHost
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@id/tabHost">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <TabWidget
+ android:id="@android:id/tabs"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+ <FrameLayout
+ android:id="@android:id/tabcontent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:id="@id/linearLayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"/>
+
+ <LinearLayout
+ android:id="@id/linearLayout2"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"/>
+
+ </FrameLayout>
+ </LinearLayout>
+ </TabHost>
+</ScrollView>
+
+ <SearchView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@id/searchView"
+ android:layout_alignBottom="@id/zoomControls"
+ android:layout_toEndOf="@id/seekBar" />
+
+</RelativeLayout>
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/ids.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/ids.xml
new file mode 100644
index 0000000..1dc2fa0
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/ids.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <item type="id" name="button" />
+ <item type="id" name="button2" />
+ <item type="id" name="checkBox" />
+ <item type="id" name="checkedTextView" />
+ <item type="id" name="dialerFilter" />
+ <item type="id" name="editText" />
+ <item type="id" name="editText2" />
+ <item type="id" name="editText3" />
+ <item type="id" name="editText4" />
+ <item type="id" name="editText5" />
+ <item type="id" name="editText6" />
+ <item type="id" name="editText7" />
+ <item type="id" name="editText8" />
+ <item type="id" name="editText9" />
+ <item type="id" name="extractEditText" />
+ <item type="id" name="frameLayout" />
+ <item type="id" name="gridLayout" />
+ <item type="id" name="gridView" />
+ <item type="id" name="imageButton" />
+ <item type="id" name="imageView" />
+ <item type="id" name="linearLayout" />
+ <item type="id" name="linearLayout2" />
+ <item type="id" name="listView" />
+ <item type="id" name="progressBar" />
+ <item type="id" name="progressBar2" />
+ <item type="id" name="progressBar3" />
+ <item type="id" name="progressBar4" />
+ <item type="id" name="quickContactBadge" />
+ <item type="id" name="radioButton" />
+ <item type="id" name="radioButton2" />
+ <item type="id" name="ratingBar" />
+ <item type="id" name="scrollView" />
+ <item type="id" name="searchView" />
+ <item type="id" name="seekBar" />
+ <item type="id" name="spinner" />
+ <item type="id" name="switch1" />
+ <item type="id" name="switch2" />
+ <item type="id" name="tabHost" />
+ <item type="id" name="textView" />
+ <item type="id" name="textView2" />
+ <item type="id" name="textView3" />
+ <item type="id" name="textView4" />
+ <item type="id" name="textureView" />
+ <item type="id" name="zoomControls" />
+</resources>
\ No newline at end of file
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index ac23564..f2a039e 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -261,7 +261,7 @@
new ResourceRepository(new FolderWrapper(TEST_RES_DIR + APP_TEST_RES), false) {
@NonNull
@Override
- protected ResourceItem createResourceItem(String name) {
+ protected ResourceItem createResourceItem(@NonNull String name) {
return new ResourceItem(name);
}
};
@@ -275,14 +275,27 @@
ConfigGenerator.getEnumMap(attrs), getLayoutLog());
}
- /**
- * Create a new rendering session and test that rendering /layout/activity.xml on nexus 5
- * doesn't throw any exceptions.
- */
+ /** Text activity.xml */
@Test
- public void testRendering() throws ClassNotFoundException {
+ public void testActivity() throws ClassNotFoundException {
+ renderAndVerify("activity.xml", "activity.png");
+
+ }
+
+ /** Test allwidgets.xml */
+ @Test
+ public void testAllWidgets() throws ClassNotFoundException {
+ renderAndVerify("allwidgets.xml", "allwidgets.png");
+ }
+
+ /**
+ * Create a new rendering session and test that rendering given layout on nexus 5
+ * doesn't throw any exceptions and matches the provided image.
+ */
+ private void renderAndVerify(String layoutFileName, String goldenFileName)
+ throws ClassNotFoundException {
// Create the layout pull parser.
- LayoutPullParser parser = new LayoutPullParser(APP_TEST_RES + "/layout/activity.xml");
+ LayoutPullParser parser = new LayoutPullParser(APP_TEST_RES + "/layout/" + layoutFileName);
// Create LayoutLibCallback.
LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger());
layoutLibCallback.initResources();
@@ -301,7 +314,7 @@
session.getResult().getErrorMessage());
}
try {
- String goldenImagePath = APP_TEST_DIR + "/golden/activity.png";
+ String goldenImagePath = APP_TEST_DIR + "/golden/" + goldenFileName;
ImageUtils.requireSimilar(goldenImagePath, session.getImage());
} catch (IOException e) {
getLogger().error(e, e.getMessage());
@@ -309,7 +322,7 @@
}
/**
- * Uses Theme.Material and Target sdk version as 21.
+ * Uses Theme.Material and Target sdk version as 22.
*/
private SessionParams getSessionParams(LayoutPullParser layoutParser,
ConfigGenerator configGenerator, LayoutLibTestCallback layoutLibCallback) {
@@ -327,7 +340,7 @@
resourceResolver,
layoutLibCallback,
0,
- 21, // TODO: Make it more configurable to run tests for various versions.
+ 22, // TODO: Make it more configurable to run tests for various versions.
getLayoutLog());
}
@@ -381,17 +394,17 @@
}
@Override
- public void warning(String msgFormat, Object... args) {
+ public void warning(@NonNull String msgFormat, Object... args) {
failWithMsg(msgFormat, args);
}
@Override
- public void info(String msgFormat, Object... args) {
+ public void info(@NonNull String msgFormat, Object... args) {
// pass.
}
@Override
- public void verbose(String msgFormat, Object... args) {
+ public void verbose(@NonNull String msgFormat, Object... args) {
// pass.
}
};
@@ -399,7 +412,7 @@
return mLogger;
}
- private static void failWithMsg(String msgFormat, Object... args) {
- fail(msgFormat == null || args == null ? "" : String.format(msgFormat, args));
+ private static void failWithMsg(@NonNull String msgFormat, Object... args) {
+ fail(args == null ? "" : String.format(msgFormat, args));
}
}
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ConfigGenerator.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ConfigGenerator.java
index a5c3202..1191df6 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ConfigGenerator.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ConfigGenerator.java
@@ -21,12 +21,11 @@
import com.android.ide.common.resources.configuration.DensityQualifier;
import com.android.ide.common.resources.configuration.FolderConfiguration;
import com.android.ide.common.resources.configuration.KeyboardStateQualifier;
-import com.android.ide.common.resources.configuration.LanguageQualifier;
import com.android.ide.common.resources.configuration.LayoutDirectionQualifier;
+import com.android.ide.common.resources.configuration.LocaleQualifier;
import com.android.ide.common.resources.configuration.NavigationMethodQualifier;
import com.android.ide.common.resources.configuration.NetworkCodeQualifier;
import com.android.ide.common.resources.configuration.NightModeQualifier;
-import com.android.ide.common.resources.configuration.RegionQualifier;
import com.android.ide.common.resources.configuration.ScreenDimensionQualifier;
import com.android.ide.common.resources.configuration.ScreenOrientationQualifier;
import com.android.ide.common.resources.configuration.ScreenRatioQualifier;
@@ -158,10 +157,9 @@
config.setUiModeQualifier(new UiModeQualifier(UiMode.NORMAL));
config.setNightModeQualifier(new NightModeQualifier(NightMode.NOTNIGHT));
config.setCountryCodeQualifier(new CountryCodeQualifier());
- config.setLanguageQualifier(new LanguageQualifier());
config.setLayoutDirectionQualifier(new LayoutDirectionQualifier());
config.setNetworkCodeQualifier(new NetworkCodeQualifier());
- config.setRegionQualifier(new RegionQualifier());
+ config.setLocaleQualifier(new LocaleQualifier());
config.setVersionQualifier(new VersionQualifier());
return config;
}
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
index 0a5e798..5b648ef 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
@@ -23,8 +23,8 @@
import com.android.ide.common.rendering.api.LayoutlibCallback;
import com.android.ide.common.rendering.api.ResourceReference;
import com.android.ide.common.rendering.api.ResourceValue;
-import com.android.resources.ResourceType;
import com.android.ide.common.resources.IntArrayWrapper;
+import com.android.resources.ResourceType;
import com.android.util.Pair;
import com.android.utils.ILogger;
@@ -36,6 +36,8 @@
import com.google.android.collect.Maps;
+import static org.junit.Assert.fail;
+
@SuppressWarnings("deprecation") // For Pair
public class LayoutLibTestCallback extends LayoutlibCallback {
@@ -121,7 +123,7 @@
@Override
public ILayoutPullParser getParser(String layoutName) {
- org.junit.Assert.fail("This method shouldn't be called by this version of LayoutLib.");
+ fail("This method shouldn't be called by this version of LayoutLib.");
return null;
}