Merge "Add LockSettingsStorage tests" into lmp-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index 28013d7..7df6e6e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -113,9 +113,6 @@
field public static final java.lang.String RECEIVE_SMS = "android.permission.RECEIVE_SMS";
field public static final java.lang.String RECEIVE_WAP_PUSH = "android.permission.RECEIVE_WAP_PUSH";
field public static final java.lang.String RECORD_AUDIO = "android.permission.RECORD_AUDIO";
- field public static final java.lang.String REGISTER_CALL_PROVIDER = "android.permission.REGISTER_CALL_PROVIDER";
- field public static final java.lang.String REGISTER_CONNECTION_MANAGER = "android.permission.REGISTER_CONNECTION_MANAGER";
- field public static final java.lang.String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION";
field public static final java.lang.String REORDER_TASKS = "android.permission.REORDER_TASKS";
field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE";
@@ -28094,351 +28091,16 @@
package android.telecom {
- public final class AudioState implements android.os.Parcelable {
- ctor public AudioState(boolean, int, int);
- ctor public AudioState(android.telecom.AudioState);
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.telecom.AudioState> CREATOR;
- field public static final int ROUTE_BLUETOOTH = 2; // 0x2
- field public static final int ROUTE_EARPIECE = 1; // 0x1
- field public static final int ROUTE_SPEAKER = 8; // 0x8
- field public static final int ROUTE_WIRED_HEADSET = 4; // 0x4
- field public static final int ROUTE_WIRED_OR_EARPIECE = 5; // 0x5
- field public final boolean isMuted;
- field public final int route;
- field public final int supportedRouteMask;
- }
-
- public abstract class Conference {
- ctor public Conference(android.telecom.PhoneAccountHandle);
- method public final boolean addConnection(android.telecom.Connection);
- method public final void destroy();
- method public final android.telecom.AudioState getAudioState();
- method public final int getCapabilities();
- method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
- method public final java.util.List<android.telecom.Connection> getConnections();
- method public final android.telecom.DisconnectCause getDisconnectCause();
- method public final android.telecom.PhoneAccountHandle getPhoneAccountHandle();
- method public android.telecom.Connection getPrimaryConnection();
- method public final int getState();
- method public void onAudioStateChanged(android.telecom.AudioState);
- method public void onConnectionAdded(android.telecom.Connection);
- method public void onDisconnect();
- method public void onHold();
- method public void onMerge(android.telecom.Connection);
- method public void onMerge();
- method public void onPlayDtmfTone(char);
- method public void onSeparate(android.telecom.Connection);
- method public void onStopDtmfTone();
- method public void onSwap();
- method public void onUnhold();
- method public final void removeConnection(android.telecom.Connection);
- method public final void setActive();
- method public final void setCapabilities(int);
- method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
- method public final void setDisconnected(android.telecom.DisconnectCause);
- method public final void setOnHold();
- }
-
- public abstract class Connection {
- ctor public Connection();
- method public static android.telecom.Connection createCanceledConnection();
- method public static android.telecom.Connection createFailedConnection(android.telecom.DisconnectCause);
- method public final void destroy();
- method public final android.net.Uri getAddress();
- method public final int getAddressPresentation();
- method public final boolean getAudioModeIsVoip();
- method public final android.telecom.AudioState getAudioState();
- method public final int getCallCapabilities();
- method public final java.lang.String getCallerDisplayName();
- method public final int getCallerDisplayNamePresentation();
- method public final android.telecom.Conference getConference();
- method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
- method public final android.telecom.DisconnectCause getDisconnectCause();
- method public final int getState();
- method public final android.telecom.StatusHints getStatusHints();
- method public final boolean isRingbackRequested();
- method public void onAbort();
- method public void onAnswer();
- method public void onAudioStateChanged(android.telecom.AudioState);
- method public void onDisconnect();
- method public void onHold();
- method public void onPlayDtmfTone(char);
- method public void onPostDialContinue(boolean);
- method public void onReject();
- method public void onSeparate();
- method public void onStateChanged(int);
- method public void onStopDtmfTone();
- method public void onUnhold();
- method public final void setActive();
- method public final void setAddress(android.net.Uri, int);
- method public final void setAudioModeIsVoip(boolean);
- method public final void setCallCapabilities(int);
- method public final void setCallerDisplayName(java.lang.String, int);
- method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
- method public final void setConnectionService(android.telecom.ConnectionService);
- method public final void setDialing();
- method public final void setDisconnected(android.telecom.DisconnectCause);
- method public final void setInitialized();
- method public final void setInitializing();
- method public final void setOnHold();
- method public final void setPostDialWait(java.lang.String);
- method public final void setRingbackRequested(boolean);
- method public final void setRinging();
- method public final void setStatusHints(android.telecom.StatusHints);
- method public static java.lang.String stateToString(int);
- field public static final int STATE_ACTIVE = 4; // 0x4
- field public static final int STATE_DIALING = 3; // 0x3
- field public static final int STATE_DISCONNECTED = 6; // 0x6
- field public static final int STATE_HOLDING = 5; // 0x5
- field public static final int STATE_INITIALIZING = 0; // 0x0
- field public static final int STATE_NEW = 1; // 0x1
- field public static final int STATE_RINGING = 2; // 0x2
- }
-
- public final class ConnectionRequest implements android.os.Parcelable {
- ctor public ConnectionRequest(android.telecom.PhoneAccountHandle, android.net.Uri, android.os.Bundle);
- method public int describeContents();
- method public android.telecom.PhoneAccountHandle getAccountHandle();
- method public android.net.Uri getAddress();
- method public android.os.Bundle getExtras();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.telecom.ConnectionRequest> CREATOR;
- }
-
- public abstract class ConnectionService extends android.app.Service {
- ctor public ConnectionService();
- method public final void addConference(android.telecom.Conference);
- method public final void addExistingConnection(android.telecom.PhoneAccountHandle, android.telecom.Connection);
- method public final void conferenceRemoteConnections(android.telecom.RemoteConnection, android.telecom.RemoteConnection);
- method public final android.telecom.RemoteConnection createRemoteIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
- method public final android.telecom.RemoteConnection createRemoteOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
- method public final java.util.Collection<android.telecom.Connection> getAllConnections();
- method public final android.os.IBinder onBind(android.content.Intent);
- method public void onConference(android.telecom.Connection, android.telecom.Connection);
- method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
- method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
- method public void onRemoteConferenceAdded(android.telecom.RemoteConference);
- method public void onRemoteExistingConnectionAdded(android.telecom.RemoteConnection);
- field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.ConnectionService";
- }
-
- public final class DisconnectCause implements android.os.Parcelable {
- ctor public DisconnectCause(int);
- ctor public DisconnectCause(int, java.lang.String);
- ctor public DisconnectCause(int, java.lang.CharSequence, java.lang.CharSequence, java.lang.String);
- ctor public DisconnectCause(int, java.lang.CharSequence, java.lang.CharSequence, java.lang.String, int);
- method public int describeContents();
- method public int getCode();
- method public java.lang.CharSequence getDescription();
- method public java.lang.CharSequence getLabel();
- method public java.lang.String getReason();
- method public int getTone();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final int BUSY = 7; // 0x7
- field public static final int CANCELED = 4; // 0x4
- field public static final int CONNECTION_MANAGER_NOT_SUPPORTED = 10; // 0xa
- field public static final android.os.Parcelable.Creator<android.telecom.DisconnectCause> CREATOR;
- field public static final int ERROR = 1; // 0x1
- field public static final int LOCAL = 2; // 0x2
- field public static final int MISSED = 5; // 0x5
- field public static final int OTHER = 9; // 0x9
- field public static final int REJECTED = 6; // 0x6
- field public static final int REMOTE = 3; // 0x3
- field public static final int RESTRICTED = 8; // 0x8
- field public static final int UNKNOWN = 0; // 0x0
- }
-
- public class GatewayInfo implements android.os.Parcelable {
- method public int describeContents();
- method public android.net.Uri getGatewayAddress();
- method public java.lang.String getGatewayProviderPackageName();
- method public android.net.Uri getOriginalAddress();
- method public boolean isEmpty();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.telecom.GatewayInfo> CREATOR;
- }
-
- public class PhoneAccount implements android.os.Parcelable {
- method public static android.telecom.PhoneAccount.Builder builder(android.telecom.PhoneAccountHandle, java.lang.CharSequence);
- method public int describeContents();
- method public android.telecom.PhoneAccountHandle getAccountHandle();
- method public android.net.Uri getAddress();
- method public int getCapabilities();
- method public int getColor();
- method public android.graphics.drawable.Drawable getIcon(android.content.Context);
- method public android.graphics.Bitmap getIconBitmap();
- method public java.lang.String getIconPackageName();
- method public int getIconResId();
- method public java.lang.CharSequence getLabel();
- method public java.lang.CharSequence getShortDescription();
- method public android.net.Uri getSubscriptionAddress();
- method public java.util.List<java.lang.String> getSupportedUriSchemes();
- method public boolean hasCapabilities(int);
- method public boolean supportsUriScheme(java.lang.String);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1
- field public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 16; // 0x10
- field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
- field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
- field public static final int NO_COLOR = -1; // 0xffffffff
- field public static final java.lang.String SCHEME_SIP = "sip";
- field public static final java.lang.String SCHEME_TEL = "tel";
- field public static final java.lang.String SCHEME_VOICEMAIL = "voicemail";
- }
-
- public static class PhoneAccount.Builder {
- ctor public PhoneAccount.Builder(android.telecom.PhoneAccountHandle, java.lang.CharSequence);
- ctor public PhoneAccount.Builder(android.telecom.PhoneAccount);
- method public android.telecom.PhoneAccount build();
- method public android.telecom.PhoneAccount.Builder setAddress(android.net.Uri);
- method public android.telecom.PhoneAccount.Builder setCapabilities(int);
- method public android.telecom.PhoneAccount.Builder setColor(int);
- method public android.telecom.PhoneAccount.Builder setIconBitmap(android.graphics.Bitmap);
- method public android.telecom.PhoneAccount.Builder setIconPackageName(java.lang.String);
- method public android.telecom.PhoneAccount.Builder setIconResId(int);
- method public android.telecom.PhoneAccount.Builder setShortDescription(java.lang.CharSequence);
- method public android.telecom.PhoneAccount.Builder setSubscriptionAddress(android.net.Uri);
- method public android.telecom.PhoneAccount.Builder setSupportedUriSchemes(java.util.List<java.lang.String>);
- }
-
- public class PhoneAccountHandle implements android.os.Parcelable {
- ctor public PhoneAccountHandle(android.content.ComponentName, java.lang.String);
- method public int describeContents();
- method public android.content.ComponentName getComponentName();
- method public java.lang.String getId();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccountHandle> CREATOR;
- }
-
- public final class PhoneCapabilities {
- method public static java.lang.String toString(int);
- field public static final int ADD_CALL = 16; // 0x10
- field public static final int ALL = 12543; // 0x30ff
- field public static final int DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
- field public static final int HOLD = 1; // 0x1
- field public static final int MANAGE_CONFERENCE = 128; // 0x80
- field public static final int MERGE_CONFERENCE = 4; // 0x4
- field public static final int MUTE = 64; // 0x40
- field public static final int RESPOND_VIA_TEXT = 32; // 0x20
- field public static final int SEPARATE_FROM_CONFERENCE = 4096; // 0x1000
- field public static final int SUPPORT_HOLD = 2; // 0x2
- field public static final int SWAP_CONFERENCE = 8; // 0x8
- }
-
- public final class RemoteConference {
- method public void disconnect();
- method public final int getCallCapabilities();
- method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
- method public final java.util.List<android.telecom.RemoteConnection> getConnections();
- method public android.telecom.DisconnectCause getDisconnectCause();
- method public final int getState();
- method public void hold();
- method public void merge();
- method public void playDtmfTone(char);
- method public final void registerCallback(android.telecom.RemoteConference.Callback);
- method public void separate(android.telecom.RemoteConnection);
- method public void setAudioState(android.telecom.AudioState);
- method public void stopDtmfTone();
- method public void swap();
- method public void unhold();
- method public final void unregisterCallback(android.telecom.RemoteConference.Callback);
- }
-
- public static abstract class RemoteConference.Callback {
- ctor public RemoteConference.Callback();
- method public void onCapabilitiesChanged(android.telecom.RemoteConference, int);
- method public void onConferenceableConnectionsChanged(android.telecom.RemoteConference, java.util.List<android.telecom.RemoteConnection>);
- method public void onConnectionAdded(android.telecom.RemoteConference, android.telecom.RemoteConnection);
- method public void onConnectionRemoved(android.telecom.RemoteConference, android.telecom.RemoteConnection);
- method public void onDestroyed(android.telecom.RemoteConference);
- method public void onDisconnected(android.telecom.RemoteConference, android.telecom.DisconnectCause);
- method public void onStateChanged(android.telecom.RemoteConference, int, int);
- }
-
- public final class RemoteConnection {
- method public void abort();
- method public void answer();
- method public void disconnect();
- method public android.net.Uri getAddress();
- method public int getAddressPresentation();
- method public int getCallCapabilities();
- method public java.lang.CharSequence getCallerDisplayName();
- method public int getCallerDisplayNamePresentation();
- method public android.telecom.RemoteConference getConference();
- method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
- method public android.telecom.DisconnectCause getDisconnectCause();
- method public int getState();
- method public android.telecom.StatusHints getStatusHints();
- method public void hold();
- method public boolean isRingbackRequested();
- method public boolean isVoipAudioMode();
- method public void playDtmfTone(char);
- method public void postDialContinue(boolean);
- method public void registerCallback(android.telecom.RemoteConnection.Callback);
- method public void reject();
- method public void setAudioState(android.telecom.AudioState);
- method public void stopDtmfTone();
- method public void unhold();
- method public void unregisterCallback(android.telecom.RemoteConnection.Callback);
- }
-
- public static abstract class RemoteConnection.Callback {
- ctor public RemoteConnection.Callback();
- method public void onAddressChanged(android.telecom.RemoteConnection, android.net.Uri, int);
- method public void onCallCapabilitiesChanged(android.telecom.RemoteConnection, int);
- method public void onCallerDisplayNameChanged(android.telecom.RemoteConnection, java.lang.String, int);
- method public void onConferenceChanged(android.telecom.RemoteConnection, android.telecom.RemoteConference);
- method public void onConferenceableConnectionsChanged(android.telecom.RemoteConnection, java.util.List<android.telecom.RemoteConnection>);
- method public void onDestroyed(android.telecom.RemoteConnection);
- method public void onDisconnected(android.telecom.RemoteConnection, android.telecom.DisconnectCause);
- method public void onPostDialWait(android.telecom.RemoteConnection, java.lang.String);
- method public void onRingbackRequested(android.telecom.RemoteConnection, boolean);
- method public void onStateChanged(android.telecom.RemoteConnection, int);
- method public void onStatusHintsChanged(android.telecom.RemoteConnection, android.telecom.StatusHints);
- method public void onVoipAudioChanged(android.telecom.RemoteConnection, boolean);
- }
-
- public final class StatusHints implements android.os.Parcelable {
- ctor public StatusHints(android.content.ComponentName, java.lang.CharSequence, int, android.os.Bundle);
- method public int describeContents();
- method public android.os.Bundle getExtras();
- method public android.graphics.drawable.Drawable getIcon(android.content.Context);
- method public int getIconResId();
- method public java.lang.CharSequence getLabel();
- method public android.content.ComponentName getPackageName();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.telecom.StatusHints> CREATOR;
- }
-
public class TelecomManager {
- method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
method public void cancelMissedCallsNotification();
- method public void clearAccounts();
- method public android.net.Uri getAdnUriForPhoneAccount(android.telecom.PhoneAccountHandle);
- method public android.telecom.PhoneAccountHandle getConnectionManager();
- method public android.telecom.PhoneAccountHandle getDefaultOutgoingPhoneAccount(java.lang.String);
- method public android.telecom.PhoneAccount getPhoneAccount(android.telecom.PhoneAccountHandle);
- method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage();
- method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(java.lang.String);
method public boolean handleMmi(java.lang.String);
- method public boolean handleMmi(android.telecom.PhoneAccountHandle, java.lang.String);
- method public boolean hasMultipleCallCapableAccounts();
method public boolean isInCall();
- method public void registerPhoneAccount(android.telecom.PhoneAccount);
method public void showInCallScreen(boolean);
- method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle);
- field public static final java.lang.String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
- field public static final java.lang.String ACTION_CONNECTION_SERVICE_CONFIGURE = "android.telecom.action.CONNECTION_SERVICE_CONFIGURE";
field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecom.action.SHOW_CALL_SETTINGS";
field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ','
field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
- field public static final java.lang.String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE";
field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
- field public static final java.lang.String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE";
- field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE";
field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE";
field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS";
field public static final java.lang.String GATEWAY_PROVIDER_PACKAGE = "android.telecom.extra.GATEWAY_PROVIDER_PACKAGE";
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ffe2b60..216a901 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1269,26 +1269,31 @@
android:description="@string/permdesc_use_sip"
android:label="@string/permlab_use_sip" />
- <!-- Protects the ability to register any PhoneAccount with
+ <!-- @SystemApi Protects the ability to register any PhoneAccount with
PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION. This capability indicates that the PhoneAccount
- corresponds to a device SIM. -->
+ corresponds to a device SIM.
+ @hide -->
<permission android:name="android.permission.REGISTER_SIM_SUBSCRIPTION"
android:permissionGroup="android.permission-group.PHONE_CALLS"
android:protectionLevel="system|signature"
android:description="@string/permdesc_register_sim_subscription"
android:label="@string/permlab_register_sim_subscription" />
- <!-- Protects the ability to register any PhoneAccount with
- PhoneAccount#CAPABILITY_CALL_PROVIDER. -->
+ <!-- @SystemApi Protects the ability to register any PhoneAccount with
+ PhoneAccount#CAPABILITY_CALL_PROVIDER.
+ @hide -->
<permission android:name="android.permission.REGISTER_CALL_PROVIDER"
android:permissionGroup="android.permission-group.PHONE_CALLS"
+ android:protectionLevel="system|signature"
android:description="@string/permdesc_register_call_provider"
android:label="@string/permlab_register_call_provider" />
- <!-- Protects the ability to register any PhoneAccount with
- PhoneAccount#CAPABILITY_CONNECTION_MANAGER -->
+ <!-- @SystemApi Protects the ability to register any PhoneAccount with
+ PhoneAccount#CAPABILITY_CONNECTION_MANAGER
+ @hide -->
<permission android:name="android.permission.REGISTER_CONNECTION_MANAGER"
android:permissionGroup="android.permission-group.PHONE_CALLS"
+ android:protectionLevel="system|signature"
android:description="@string/permdesc_connection_manager"
android:label="@string/permlab_connection_manager" />
diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
index c4a949f..4b38ad3 100644
--- a/data/fonts/fallback_fonts.xml
+++ b/data/fonts/fallback_fonts.xml
@@ -223,7 +223,28 @@
</family>
<family>
<fileset>
- <file>NotoSansCherokee-Regular.ttf</file>
+ <file>NotoSansThaana-Regular.ttf</file>
+ <file>NotoSansThaana-Bold.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>NotoSansBalinese-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>NotoSansBatak-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>NotoSansBuginese-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>NotoSansBuhid-Regular.ttf</file>
</fileset>
</family>
<family>
@@ -233,6 +254,66 @@
</family>
<family>
<fileset>
+ <file>NotoSansCherokee-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>NotoSansHanunoo-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>NotoSansJavanese-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>NotoSansLepcha-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>NotoSansLimbu-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>NotoSansMeeteiMayek-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>NotoSansOlChiki-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>NotoSansRejang-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>NotoSansSaurashtra-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>NotoSansSundanese-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>NotoSansSylotiNagri-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>NotoSansTagbanwa-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
<file>NotoSansYi-Regular.ttf</file>
</fileset>
</family>
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 09055c6..4493554 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -220,12 +220,61 @@
<font weight="700" style="normal">NotoSansMyanmarUI-Bold.ttf</font>
</family>
<family>
- <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
+ <font weight="400" style="normal">NotoSansThaana-Regular.ttf</font>
+ <font weight="700" style="normal">NotoSansThaana-Bold.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansBalinese-Regular.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansBatak-Regular.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansBuginese-Regular.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansBuhid-Regular.ttf</font>
</family>
<family>
<font weight="400" style="normal">NotoSansCanadianAboriginal-Regular.ttf</font>
</family>
<family>
+ <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansHanunoo-Regular.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansJavanese-Regular.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansLepcha-Regular.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansLimbu-Regular.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansMeeteiMayek-Regular.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansOlChiki-Regular.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansRejang-Regular.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansSaurashtra-Regular.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansSundanese-Regular.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansSylotiNagri-Regular.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansTagbanwa-Regular.ttf</font>
+ </family>
+ <family>
<font weight="400" style="normal">NotoSansYi-Regular.ttf</font>
</family>
<family lang="zh-Hans">
diff --git a/docs/html/distribute/tools/promote/device-art-resources/galaxy_nexus/land_back.png b/docs/html/distribute/tools/promote/device-art-resources/galaxy_nexus/land_back.png
deleted file mode 100644
index f340a62..0000000
--- a/docs/html/distribute/tools/promote/device-art-resources/galaxy_nexus/land_back.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/galaxy_nexus/land_fore.png b/docs/html/distribute/tools/promote/device-art-resources/galaxy_nexus/land_fore.png
deleted file mode 100644
index 2a4e595..0000000
--- a/docs/html/distribute/tools/promote/device-art-resources/galaxy_nexus/land_fore.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/galaxy_nexus/land_shadow.png b/docs/html/distribute/tools/promote/device-art-resources/galaxy_nexus/land_shadow.png
deleted file mode 100644
index f3a3120..0000000
--- a/docs/html/distribute/tools/promote/device-art-resources/galaxy_nexus/land_shadow.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/galaxy_nexus/port_back.png b/docs/html/distribute/tools/promote/device-art-resources/galaxy_nexus/port_back.png
deleted file mode 100644
index c40b37c..0000000
--- a/docs/html/distribute/tools/promote/device-art-resources/galaxy_nexus/port_back.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/galaxy_nexus/port_fore.png b/docs/html/distribute/tools/promote/device-art-resources/galaxy_nexus/port_fore.png
deleted file mode 100644
index aae684b..0000000
--- a/docs/html/distribute/tools/promote/device-art-resources/galaxy_nexus/port_fore.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/galaxy_nexus/port_shadow.png b/docs/html/distribute/tools/promote/device-art-resources/galaxy_nexus/port_shadow.png
deleted file mode 100644
index 61a0da9..0000000
--- a/docs/html/distribute/tools/promote/device-art-resources/galaxy_nexus/port_shadow.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/galaxy_nexus/thumb.png b/docs/html/distribute/tools/promote/device-art-resources/galaxy_nexus/thumb.png
deleted file mode 100644
index e21a421..0000000
--- a/docs/html/distribute/tools/promote/device-art-resources/galaxy_nexus/thumb.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_6/land_back.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_6/land_back.png
new file mode 100644
index 0000000..06695f5
--- /dev/null
+++ b/docs/html/distribute/tools/promote/device-art-resources/nexus_6/land_back.png
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_6/land_fore.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_6/land_fore.png
new file mode 100644
index 0000000..9fe5409
--- /dev/null
+++ b/docs/html/distribute/tools/promote/device-art-resources/nexus_6/land_fore.png
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_6/land_shadow.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_6/land_shadow.png
new file mode 100644
index 0000000..99f826f
--- /dev/null
+++ b/docs/html/distribute/tools/promote/device-art-resources/nexus_6/land_shadow.png
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_6/port_back.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_6/port_back.png
new file mode 100644
index 0000000..6e1aec6
--- /dev/null
+++ b/docs/html/distribute/tools/promote/device-art-resources/nexus_6/port_back.png
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_6/port_fore.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_6/port_fore.png
new file mode 100644
index 0000000..53ec73a
--- /dev/null
+++ b/docs/html/distribute/tools/promote/device-art-resources/nexus_6/port_fore.png
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_6/port_shadow.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_6/port_shadow.png
new file mode 100644
index 0000000..66149c6
--- /dev/null
+++ b/docs/html/distribute/tools/promote/device-art-resources/nexus_6/port_shadow.png
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_6/thumb.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_6/thumb.png
new file mode 100644
index 0000000..bc1f492
--- /dev/null
+++ b/docs/html/distribute/tools/promote/device-art-resources/nexus_6/thumb.png
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_9/land_back.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_9/land_back.png
new file mode 100644
index 0000000..fdbc52c
--- /dev/null
+++ b/docs/html/distribute/tools/promote/device-art-resources/nexus_9/land_back.png
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_9/land_fore.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_9/land_fore.png
new file mode 100644
index 0000000..76fc78e
--- /dev/null
+++ b/docs/html/distribute/tools/promote/device-art-resources/nexus_9/land_fore.png
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_9/land_shadow.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_9/land_shadow.png
new file mode 100644
index 0000000..d40758f
--- /dev/null
+++ b/docs/html/distribute/tools/promote/device-art-resources/nexus_9/land_shadow.png
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_9/port_back.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_9/port_back.png
new file mode 100644
index 0000000..fd6f88f
--- /dev/null
+++ b/docs/html/distribute/tools/promote/device-art-resources/nexus_9/port_back.png
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_9/port_fore.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_9/port_fore.png
new file mode 100644
index 0000000..328ceef
--- /dev/null
+++ b/docs/html/distribute/tools/promote/device-art-resources/nexus_9/port_fore.png
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_9/port_shadow.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_9/port_shadow.png
new file mode 100644
index 0000000..13eba2f
--- /dev/null
+++ b/docs/html/distribute/tools/promote/device-art-resources/nexus_9/port_shadow.png
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_9/thumb.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_9/thumb.png
new file mode 100644
index 0000000..bb52e6c
--- /dev/null
+++ b/docs/html/distribute/tools/promote/device-art-resources/nexus_9/thumb.png
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_s/land_back.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_s/land_back.png
deleted file mode 100644
index f525e8e..0000000
--- a/docs/html/distribute/tools/promote/device-art-resources/nexus_s/land_back.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_s/land_fore.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_s/land_fore.png
deleted file mode 100644
index e26bfe1..0000000
--- a/docs/html/distribute/tools/promote/device-art-resources/nexus_s/land_fore.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_s/land_shadow.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_s/land_shadow.png
deleted file mode 100644
index ea26b73..0000000
--- a/docs/html/distribute/tools/promote/device-art-resources/nexus_s/land_shadow.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_s/port_back.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_s/port_back.png
deleted file mode 100644
index ed4ad0c..0000000
--- a/docs/html/distribute/tools/promote/device-art-resources/nexus_s/port_back.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_s/port_fore.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_s/port_fore.png
deleted file mode 100644
index 74bd077..0000000
--- a/docs/html/distribute/tools/promote/device-art-resources/nexus_s/port_fore.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_s/port_shadow.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_s/port_shadow.png
deleted file mode 100644
index bb4bec8..0000000
--- a/docs/html/distribute/tools/promote/device-art-resources/nexus_s/port_shadow.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/nexus_s/thumb.png b/docs/html/distribute/tools/promote/device-art-resources/nexus_s/thumb.png
deleted file mode 100644
index 8b9a3d9..0000000
--- a/docs/html/distribute/tools/promote/device-art-resources/nexus_s/thumb.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/xoom/land_back.png b/docs/html/distribute/tools/promote/device-art-resources/xoom/land_back.png
deleted file mode 100644
index e1eb075..0000000
--- a/docs/html/distribute/tools/promote/device-art-resources/xoom/land_back.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/xoom/land_fore.png b/docs/html/distribute/tools/promote/device-art-resources/xoom/land_fore.png
deleted file mode 100644
index 15e5f50..0000000
--- a/docs/html/distribute/tools/promote/device-art-resources/xoom/land_fore.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/xoom/land_shadow.png b/docs/html/distribute/tools/promote/device-art-resources/xoom/land_shadow.png
deleted file mode 100644
index 885508a..0000000
--- a/docs/html/distribute/tools/promote/device-art-resources/xoom/land_shadow.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/xoom/port_back.png b/docs/html/distribute/tools/promote/device-art-resources/xoom/port_back.png
deleted file mode 100644
index 290ca35..0000000
--- a/docs/html/distribute/tools/promote/device-art-resources/xoom/port_back.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/xoom/port_fore.png b/docs/html/distribute/tools/promote/device-art-resources/xoom/port_fore.png
deleted file mode 100644
index 8b3dca3..0000000
--- a/docs/html/distribute/tools/promote/device-art-resources/xoom/port_fore.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/xoom/port_shadow.png b/docs/html/distribute/tools/promote/device-art-resources/xoom/port_shadow.png
deleted file mode 100644
index 895b75e..0000000
--- a/docs/html/distribute/tools/promote/device-art-resources/xoom/port_shadow.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art-resources/xoom/thumb.png b/docs/html/distribute/tools/promote/device-art-resources/xoom/thumb.png
deleted file mode 100644
index 8fd08a4..0000000
--- a/docs/html/distribute/tools/promote/device-art-resources/xoom/thumb.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/distribute/tools/promote/device-art.jd b/docs/html/distribute/tools/promote/device-art.jd
index 1736060..3902b30 100644
--- a/docs/html/distribute/tools/promote/device-art.jd
+++ b/docs/html/distribute/tools/promote/device-art.jd
@@ -66,7 +66,7 @@
}
.device-list {
- padding: 0;
+ padding: 1em 0 0 0;
margin: 0;
}
@@ -187,6 +187,19 @@
portSize: [1080,1920],
},
{
+ id: 'nexus_6',
+ title: 'Nexus 6',
+ url: 'http://www.google.com/nexus/6/',
+ physicalSize: 6,
+ physicalHeight: 6.27,
+ density: '560DPI',
+ landRes: ['shadow', 'back', 'fore'],
+ landOffset: [489,327],
+ portRes: ['shadow', 'back', 'fore'],
+ portOffset: [327,489],
+ portSize: [1440, 2560],
+ },
+ {
id: 'nexus_7',
title: 'Nexus 7',
url: 'http://www.google.com/nexus/7/',
@@ -201,6 +214,20 @@
portSize: [800,1280]
},
{
+ id: 'nexus_9',
+ title: 'Nexus 9',
+ url: 'http://www.google.com/nexus/9/',
+ physicalSize: 9,
+ physicalHeight: 8.98,
+ actualResolution: [1536,2048],
+ density: 'XHDPI',
+ landRes: ['shadow', 'back', 'fore'],
+ landOffset: [514,350],
+ portRes: ['shadow', 'back', 'fore'],
+ portOffset: [348,514],
+ portSize: [1536,2048],
+ },
+ {
id: 'nexus_10',
title: 'Nexus 10',
url: 'http://www.google.com/nexus/10/',
@@ -212,19 +239,6 @@
landOffset: [227,217],
portRes: ['shadow', 'back', 'fore'],
portOffset: [217,223],
- portSize: [800,1280]
- },
- {
- id: 'xoom',
- title: 'Motorola XOOM',
- url: 'http://www.google.com/phone/detail/motorola-xoom',
- physicalSize: 10,
- physicalHeight: 6.61,
- density: 'MDPI',
- landRes: ['shadow', 'back', 'fore'],
- landOffset: [218,191],
- portRes: ['shadow', 'back', 'fore'],
- portOffset: [199,200],
portSize: [800,1280],
archived: true
},
@@ -256,34 +270,6 @@
portSize: [768,1280],
archived: true
},
- {
- id: 'galaxy_nexus',
- title: 'Galaxy Nexus',
- url: 'http://www.android.com/devices/detail/galaxy-nexus',
- physicalSize: 4.65,
- physicalHeight: 5.33,
- density: 'XHDPI',
- landRes: ['shadow', 'back', 'fore'],
- landOffset: [371,199],
- portRes: ['shadow', 'back', 'fore'],
- portOffset: [216,353],
- portSize: [720,1280],
- archived: true
- },
- {
- id: 'nexus_s',
- title: 'Nexus S',
- url: 'http://www.google.com/phone/detail/nexus-s',
- physicalSize: 4.0,
- physicalHeight: 4.88,
- density: 'HDPI',
- landRes: ['shadow', 'back', 'fore'],
- landOffset: [247,135],
- portRes: ['shadow', 'back', 'fore'],
- portOffset: [134,247],
- portSize: [480,800],
- archived: true
- }
];
DEVICES = DEVICES.sort(function(x, y) { return x.physicalSize - y.physicalSize; });
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 3ff9ff4..a1085d3 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -51,6 +51,7 @@
= new ArrayMap<IBinder, IConditionListener>();
private final ArrayList<ConditionRecord> mRecords = new ArrayList<ConditionRecord>();
private final CountdownConditionProvider mCountdown = new CountdownConditionProvider();
+ private final NextAlarmTracker mNextAlarmTracker;
private final DowntimeConditionProvider mDowntime = new DowntimeConditionProvider();
private final NextAlarmConditionProvider mNextAlarm = new NextAlarmConditionProvider();
@@ -63,6 +64,7 @@
mZenModeHelper = zenModeHelper;
mZenModeHelper.addCallback(new ZenModeHelperCallback());
loadZenConfig();
+ mNextAlarmTracker = new NextAlarmTracker(context);
}
@Override
@@ -101,6 +103,7 @@
mCountdown.dump(pw, filter);
mDowntime.dump(pw, filter);
mNextAlarm.dump(pw, filter);
+ mNextAlarmTracker.dump(pw, filter);
}
@Override
@@ -111,6 +114,7 @@
@Override
public void onBootPhaseAppsCanStart() {
super.onBootPhaseAppsCanStart();
+ mNextAlarmTracker.init();
mCountdown.attachBase(mContext);
registerService(mCountdown.asInterface(), CountdownConditionProvider.COMPONENT,
UserHandle.USER_OWNER);
@@ -121,20 +125,13 @@
mNextAlarm.attachBase(mContext);
registerService(mNextAlarm.asInterface(), NextAlarmConditionProvider.COMPONENT,
UserHandle.USER_OWNER);
- mNextAlarm.setCallback(new NextAlarmConditionProvider.Callback() {
- @Override
- public boolean isInDowntime() {
- return mDowntime.isInDowntime();
- }
- });
+ mNextAlarm.setCallback(new NextAlarmCallback());
}
@Override
public void onUserSwitched() {
super.onUserSwitched();
- if (mNextAlarm != null) {
- mNextAlarm.onUserSwitched();
- }
+ mNextAlarmTracker.onUserSwitched();
}
@Override
@@ -572,6 +569,23 @@
mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF, "downtimeExit");
}
}
+
+ @Override
+ public NextAlarmTracker getNextAlarmTracker() {
+ return mNextAlarmTracker;
+ }
+ }
+
+ private class NextAlarmCallback implements NextAlarmConditionProvider.Callback {
+ @Override
+ public boolean isInDowntime() {
+ return mDowntime.isInDowntime();
+ }
+
+ @Override
+ public NextAlarmTracker getNextAlarmTracker() {
+ return mNextAlarmTracker;
+ }
}
private static class ConditionRecord {
diff --git a/services/core/java/com/android/server/notification/DowntimeConditionProvider.java b/services/core/java/com/android/server/notification/DowntimeConditionProvider.java
index 881c9ad..24fd155 100644
--- a/services/core/java/com/android/server/notification/DowntimeConditionProvider.java
+++ b/services/core/java/com/android/server/notification/DowntimeConditionProvider.java
@@ -298,5 +298,6 @@
public interface Callback {
void onDowntimeChanged(int downtimeMode);
+ NextAlarmTracker getNextAlarmTracker();
}
}
diff --git a/services/core/java/com/android/server/notification/NextAlarmConditionProvider.java b/services/core/java/com/android/server/notification/NextAlarmConditionProvider.java
index 4a29573..35bbaa0 100644
--- a/services/core/java/com/android/server/notification/NextAlarmConditionProvider.java
+++ b/services/core/java/com/android/server/notification/NextAlarmConditionProvider.java
@@ -16,34 +16,23 @@
package com.android.server.notification;
-import android.app.ActivityManager;
import android.app.AlarmManager;
-import android.app.PendingIntent;
import android.app.AlarmManager.AlarmClockInfo;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.net.Uri;
-import android.os.Handler;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.UserHandle;
import android.service.notification.Condition;
import android.service.notification.ConditionProviderService;
import android.service.notification.IConditionProvider;
import android.service.notification.ZenModeConfig;
-import android.util.TimeUtils;
-import android.text.format.DateFormat;
import android.util.Log;
import android.util.Slog;
+import android.util.TimeUtils;
import com.android.internal.R;
import com.android.server.notification.NotificationManagerService.DumpFilter;
import java.io.PrintWriter;
-import java.util.Locale;
/**
* Built-in zen condition provider for alarm-clock-based conditions.
@@ -62,33 +51,21 @@
private static final String TAG = "NextAlarmConditions";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final String ACTION_TRIGGER = TAG + ".trigger";
- private static final String EXTRA_TRIGGER = "trigger";
- private static final int REQUEST_CODE = 100;
private static final long SECONDS = 1000;
private static final long MINUTES = 60 * SECONDS;
private static final long HOURS = 60 * MINUTES;
- private static final long NEXT_ALARM_UPDATE_DELAY = 1 * SECONDS; // treat clear+set as update
- private static final long EARLY = 5 * SECONDS; // fire early, ensure alarm stream is unmuted
- private static final long WAIT_AFTER_CONNECT = 5 * MINUTES;// for initial alarm re-registration
- private static final long WAIT_AFTER_BOOT = 20 * SECONDS; // for initial alarm re-registration
+
private static final String NEXT_ALARM_PATH = "next_alarm";
public static final ComponentName COMPONENT =
new ComponentName("android", NextAlarmConditionProvider.class.getName());
private final Context mContext = this;
- private final H mHandler = new H();
- private long mConnected;
- private boolean mRegistered;
- private AlarmManager mAlarmManager;
- private int mCurrentUserId;
+ private NextAlarmTracker mTracker;
+ private boolean mConnected;
private long mLookaheadThreshold;
- private long mScheduledAlarmTime;
private Callback mCallback;
private Uri mCurrentSubscription;
- private PowerManager.WakeLock mWakeLock;
- private long mBootCompleted;
public NextAlarmConditionProvider() {
if (DEBUG) Slog.d(TAG, "new NextAlarmConditionProvider()");
@@ -97,14 +74,9 @@
public void dump(PrintWriter pw, DumpFilter filter) {
pw.println(" NextAlarmConditionProvider:");
pw.print(" mConnected="); pw.println(mConnected);
- pw.print(" mBootCompleted="); pw.println(mBootCompleted);
- pw.print(" mRegistered="); pw.println(mRegistered);
- pw.print(" mCurrentUserId="); pw.println(mCurrentUserId);
- pw.print(" mScheduledAlarmTime="); pw.println(formatAlarmDebug(mScheduledAlarmTime));
pw.print(" mLookaheadThreshold="); pw.print(mLookaheadThreshold);
pw.print(" ("); TimeUtils.formatDuration(mLookaheadThreshold, pw); pw.println(")");
pw.print(" mCurrentSubscription="); pw.println(mCurrentSubscription);
- pw.print(" mWakeLock="); pw.println(mWakeLock);
}
public void setCallback(Callback callback) {
@@ -114,38 +86,26 @@
@Override
public void onConnected() {
if (DEBUG) Slog.d(TAG, "onConnected");
- mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
- final PowerManager p = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- mWakeLock = p.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mLookaheadThreshold = mContext.getResources()
.getInteger(R.integer.config_next_alarm_condition_lookahead_threshold_hrs) * HOURS;
- init();
- mConnected = System.currentTimeMillis();
- }
-
- public void onUserSwitched() {
- if (DEBUG) Slog.d(TAG, "onUserSwitched");
- if (mConnected != 0) {
- init();
- }
+ mConnected = true;
+ mTracker = mCallback.getNextAlarmTracker();
+ mTracker.addCallback(mTrackerCallback);
}
@Override
public void onDestroy() {
super.onDestroy();
if (DEBUG) Slog.d(TAG, "onDestroy");
- if (mRegistered) {
- mContext.unregisterReceiver(mReceiver);
- mRegistered = false;
- }
- mConnected = 0;
+ mTracker.removeCallback(mTrackerCallback);
+ mConnected = false;
}
@Override
public void onRequestConditions(int relevance) {
- if (mConnected == 0 || (relevance & Condition.FLAG_RELEVANT_NOW) == 0) return;
+ if (!mConnected || (relevance & Condition.FLAG_RELEVANT_NOW) == 0) return;
- final AlarmClockInfo nextAlarm = mAlarmManager.getNextAlarmClock(mCurrentUserId);
+ final AlarmClockInfo nextAlarm = mTracker.getNextAlarm();
if (nextAlarm == null) return; // no next alarm
if (mCallback != null && mCallback.isInDowntime()) return; // prefer downtime condition
if (!isWithinLookaheadThreshold(nextAlarm)) return; // alarm not within window
@@ -154,12 +114,6 @@
notifyCondition(newConditionId(), nextAlarm, Condition.STATE_TRUE, "request");
}
- private boolean isWithinLookaheadThreshold(AlarmClockInfo alarm) {
- if (alarm == null) return false;
- final long delta = getEarlyTriggerTime(alarm) - System.currentTimeMillis();
- return delta > 0 && (mLookaheadThreshold <= 0 || delta < mLookaheadThreshold);
- }
-
@Override
public void onSubscribe(Uri conditionId) {
if (DEBUG) Slog.d(TAG, "onSubscribe " + conditionId);
@@ -168,79 +122,7 @@
return;
}
mCurrentSubscription = conditionId;
- mHandler.postEvaluate(0);
- }
-
- private static long getEarlyTriggerTime(AlarmClockInfo alarm) {
- return alarm != null ? (alarm.getTriggerTime() - EARLY) : 0;
- }
-
- private boolean isDoneWaitingAfterBoot(long time) {
- if (mBootCompleted > 0) return (time - mBootCompleted) > WAIT_AFTER_BOOT;
- if (mConnected > 0) return (time - mConnected) > WAIT_AFTER_CONNECT;
- return true;
- }
-
- private void handleEvaluate() {
- final AlarmClockInfo nextAlarm = mAlarmManager.getNextAlarmClock(mCurrentUserId);
- final long triggerTime = getEarlyTriggerTime(nextAlarm);
- final boolean withinThreshold = isWithinLookaheadThreshold(nextAlarm);
- final long now = System.currentTimeMillis();
- final boolean booted = isDoneWaitingAfterBoot(now);
- if (DEBUG) Slog.d(TAG, "handleEvaluate mCurrentSubscription=" + mCurrentSubscription
- + " nextAlarm=" + formatAlarmDebug(triggerTime)
- + " withinThreshold=" + withinThreshold
- + " booted=" + booted);
- if (mCurrentSubscription == null) return; // no one cares
- if (!booted) {
- // we don't know yet
- notifyCondition(mCurrentSubscription, nextAlarm, Condition.STATE_UNKNOWN, "!booted");
- final long recheckTime = (mBootCompleted > 0 ? mBootCompleted : now) + WAIT_AFTER_BOOT;
- rescheduleAlarm(recheckTime);
- return;
- }
- if (!withinThreshold) {
- // triggertime invalid or in the past, condition = false
- notifyCondition(mCurrentSubscription, nextAlarm, Condition.STATE_FALSE, "!within");
- mCurrentSubscription = null;
- return;
- }
- // triggertime in the future, condition = true, schedule alarm
- notifyCondition(mCurrentSubscription, nextAlarm, Condition.STATE_TRUE, "within");
- rescheduleAlarm(triggerTime);
- }
-
- private static String formatDuration(long millis) {
- final StringBuilder sb = new StringBuilder();
- TimeUtils.formatDuration(millis, sb);
- return sb.toString();
- }
-
- private void rescheduleAlarm(long time) {
- if (DEBUG) Slog.d(TAG, "rescheduleAlarm " + time);
- final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
- final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, REQUEST_CODE,
- new Intent(ACTION_TRIGGER)
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
- .putExtra(EXTRA_TRIGGER, time),
- PendingIntent.FLAG_UPDATE_CURRENT);
- alarms.cancel(pendingIntent);
- mScheduledAlarmTime = time;
- if (time > 0) {
- if (DEBUG) Slog.d(TAG, String.format("Scheduling alarm for %s (in %s)",
- formatAlarmDebug(time), formatDuration(time - System.currentTimeMillis())));
- alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
- }
- }
-
- private void notifyCondition(Uri id, AlarmClockInfo alarm, int state, String reason) {
- final String formattedAlarm = alarm == null ? "" : formatAlarm(alarm.getTriggerTime());
- if (DEBUG) Slog.d(TAG, "notifyCondition " + Condition.stateToString(state)
- + " alarm=" + formattedAlarm + " reason=" + reason);
- notifyCondition(new Condition(id,
- mContext.getString(R.string.zen_mode_next_alarm_summary, formattedAlarm),
- mContext.getString(R.string.zen_mode_next_alarm_line_one),
- formattedAlarm, 0, state, Condition.FLAG_RELEVANT_NOW));
+ mTracker.evaluate();
}
@Override
@@ -248,7 +130,6 @@
if (DEBUG) Slog.d(TAG, "onUnsubscribe " + conditionId);
if (conditionId != null && conditionId.equals(mCurrentSubscription)) {
mCurrentSubscription = null;
- rescheduleAlarm(0);
}
}
@@ -260,11 +141,27 @@
return (IConditionProvider) onBind(null);
}
+ private boolean isWithinLookaheadThreshold(AlarmClockInfo alarm) {
+ if (alarm == null) return false;
+ final long delta = NextAlarmTracker.getEarlyTriggerTime(alarm) - System.currentTimeMillis();
+ return delta > 0 && (mLookaheadThreshold <= 0 || delta < mLookaheadThreshold);
+ }
+
+ private void notifyCondition(Uri id, AlarmClockInfo alarm, int state, String reason) {
+ final String formattedAlarm = alarm == null ? "" : mTracker.formatAlarm(alarm);
+ if (DEBUG) Slog.d(TAG, "notifyCondition " + Condition.stateToString(state)
+ + " alarm=" + formattedAlarm + " reason=" + reason);
+ notifyCondition(new Condition(id,
+ mContext.getString(R.string.zen_mode_next_alarm_summary, formattedAlarm),
+ mContext.getString(R.string.zen_mode_next_alarm_line_one),
+ formattedAlarm, 0, state, Condition.FLAG_RELEVANT_NOW));
+ }
+
private Uri newConditionId() {
return new Uri.Builder().scheme(Condition.SCHEME)
.authority(ZenModeConfig.SYSTEM_AUTHORITY)
.appendPath(NEXT_ALARM_PATH)
- .appendPath(Integer.toString(mCurrentUserId))
+ .appendPath(Integer.toString(mTracker.getCurrentUserId()))
.build();
}
@@ -273,81 +170,41 @@
&& conditionId.getAuthority().equals(ZenModeConfig.SYSTEM_AUTHORITY)
&& conditionId.getPathSegments().size() == 2
&& conditionId.getPathSegments().get(0).equals(NEXT_ALARM_PATH)
- && conditionId.getPathSegments().get(1).equals(Integer.toString(mCurrentUserId));
+ && conditionId.getPathSegments().get(1)
+ .equals(Integer.toString(mTracker.getCurrentUserId()));
}
- private void init() {
- if (mRegistered) {
- mContext.unregisterReceiver(mReceiver);
+ private void onEvaluate(AlarmClockInfo nextAlarm, long wakeupTime, boolean booted) {
+ final boolean withinThreshold = isWithinLookaheadThreshold(nextAlarm);
+ if (DEBUG) Slog.d(TAG, "onEvaluate mCurrentSubscription=" + mCurrentSubscription
+ + " nextAlarmWakeup=" + mTracker.formatAlarmDebug(wakeupTime)
+ + " withinThreshold=" + withinThreshold
+ + " booted=" + booted);
+ if (mCurrentSubscription == null) return; // no one cares
+ if (!booted) {
+ // we don't know yet
+ notifyCondition(mCurrentSubscription, nextAlarm, Condition.STATE_UNKNOWN, "!booted");
+ return;
}
- mCurrentUserId = ActivityManager.getCurrentUser();
- final IntentFilter filter = new IntentFilter();
- filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
- filter.addAction(ACTION_TRIGGER);
- filter.addAction(Intent.ACTION_TIME_CHANGED);
- filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
- filter.addAction(Intent.ACTION_BOOT_COMPLETED);
- mContext.registerReceiverAsUser(mReceiver, new UserHandle(mCurrentUserId), filter, null,
- null);
- mRegistered = true;
- mHandler.postEvaluate(0);
+ if (!withinThreshold) {
+ // next alarm outside threshold or in the past, condition = false
+ notifyCondition(mCurrentSubscription, nextAlarm, Condition.STATE_FALSE, "!within");
+ mCurrentSubscription = null;
+ return;
+ }
+ // next alarm in the future and within threshold, condition = true
+ notifyCondition(mCurrentSubscription, nextAlarm, Condition.STATE_TRUE, "within");
}
- private String formatAlarm(long time) {
- return formatAlarm(time, "Hm", "hma");
- }
-
- private String formatAlarm(long time, String skeleton24, String skeleton12) {
- final String skeleton = DateFormat.is24HourFormat(mContext) ? skeleton24 : skeleton12;
- final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
- return DateFormat.format(pattern, time).toString();
- }
-
- private String formatAlarmDebug(AlarmClockInfo alarm) {
- return formatAlarmDebug(alarm != null ? alarm.getTriggerTime() : 0);
- }
-
- private String formatAlarmDebug(long time) {
- if (time <= 0) return Long.toString(time);
- return String.format("%s (%s)", time, formatAlarm(time, "Hms", "hmsa"));
- }
-
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ private final NextAlarmTracker.Callback mTrackerCallback = new NextAlarmTracker.Callback() {
@Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (DEBUG) Slog.d(TAG, "onReceive " + action);
- long delay = 0;
- if (action.equals(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)) {
- delay = NEXT_ALARM_UPDATE_DELAY;
- if (DEBUG) Slog.d(TAG, String.format(" next alarm for user %s: %s",
- mCurrentUserId,
- formatAlarmDebug(mAlarmManager.getNextAlarmClock(mCurrentUserId))));
- } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
- mBootCompleted = System.currentTimeMillis();
- }
- mHandler.postEvaluate(delay);
- mWakeLock.acquire(delay + 5000); // stay awake during evaluate
+ public void onEvaluate(AlarmClockInfo nextAlarm, long wakeupTime, boolean booted) {
+ NextAlarmConditionProvider.this.onEvaluate(nextAlarm, wakeupTime, booted);
}
};
public interface Callback {
boolean isInDowntime();
- }
-
- private class H extends Handler {
- private static final int MSG_EVALUATE = 1;
-
- public void postEvaluate(long delay) {
- removeMessages(MSG_EVALUATE);
- sendEmptyMessageDelayed(MSG_EVALUATE, delay);
- }
-
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == MSG_EVALUATE) {
- handleEvaluate();
- }
- }
+ NextAlarmTracker getNextAlarmTracker();
}
}
diff --git a/services/core/java/com/android/server/notification/NextAlarmTracker.java b/services/core/java/com/android/server/notification/NextAlarmTracker.java
new file mode 100644
index 0000000..d197afd
--- /dev/null
+++ b/services/core/java/com/android/server/notification/NextAlarmTracker.java
@@ -0,0 +1,263 @@
+/*
+ * 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.server.notification;
+
+import android.app.ActivityManager;
+import android.app.AlarmManager;
+import android.app.AlarmManager.AlarmClockInfo;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.UserHandle;
+import android.text.format.DateFormat;
+import android.util.Log;
+import android.util.Slog;
+import android.util.TimeUtils;
+
+import com.android.server.notification.NotificationManagerService.DumpFilter;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Locale;
+
+/** Helper for tracking updates to the current user's next alarm. */
+public class NextAlarmTracker {
+ private static final String TAG = "NextAlarmTracker";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private static final String ACTION_TRIGGER = TAG + ".trigger";
+ private static final String EXTRA_TRIGGER = "trigger";
+ private static final int REQUEST_CODE = 100;
+
+ private static final long SECONDS = 1000;
+ private static final long MINUTES = 60 * SECONDS;
+ private static final long NEXT_ALARM_UPDATE_DELAY = 1 * SECONDS; // treat clear+set as update
+ private static final long EARLY = 5 * SECONDS; // fire early, ensure alarm stream is unmuted
+ private static final long WAIT_AFTER_INIT = 5 * MINUTES;// for initial alarm re-registration
+ private static final long WAIT_AFTER_BOOT = 20 * SECONDS; // for initial alarm re-registration
+
+ private final Context mContext;
+ private final H mHandler = new H();
+ private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
+
+ private long mInit;
+ private boolean mRegistered;
+ private AlarmManager mAlarmManager;
+ private int mCurrentUserId;
+ private long mScheduledAlarmTime;
+ private long mBootCompleted;
+ private PowerManager.WakeLock mWakeLock;
+
+ public NextAlarmTracker(Context context) {
+ mContext = context;
+ }
+
+ public void dump(PrintWriter pw, DumpFilter filter) {
+ pw.println(" NextAlarmTracker:");
+ pw.print(" len(mCallbacks)="); pw.println(mCallbacks.size());
+ pw.print(" mRegistered="); pw.println(mRegistered);
+ pw.print(" mInit="); pw.println(mInit);
+ pw.print(" mBootCompleted="); pw.println(mBootCompleted);
+ pw.print(" mCurrentUserId="); pw.println(mCurrentUserId);
+ pw.print(" mScheduledAlarmTime="); pw.println(formatAlarmDebug(mScheduledAlarmTime));
+ pw.print(" mWakeLock="); pw.println(mWakeLock);
+ }
+
+ public void addCallback(Callback callback) {
+ mCallbacks.add(callback);
+ }
+
+ public void removeCallback(Callback callback) {
+ mCallbacks.remove(callback);
+ }
+
+ public int getCurrentUserId() {
+ return mCurrentUserId;
+ }
+
+ public AlarmClockInfo getNextAlarm() {
+ return mAlarmManager.getNextAlarmClock(mCurrentUserId);
+ }
+
+ public void onUserSwitched() {
+ reset();
+ }
+
+ public void init() {
+ mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ final PowerManager p = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = p.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ mInit = System.currentTimeMillis();
+ reset();
+ }
+
+ public void reset() {
+ if (mRegistered) {
+ mContext.unregisterReceiver(mReceiver);
+ }
+ mCurrentUserId = ActivityManager.getCurrentUser();
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
+ filter.addAction(ACTION_TRIGGER);
+ filter.addAction(Intent.ACTION_TIME_CHANGED);
+ filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+ filter.addAction(Intent.ACTION_BOOT_COMPLETED);
+ mContext.registerReceiverAsUser(mReceiver, new UserHandle(mCurrentUserId), filter, null,
+ null);
+ mRegistered = true;
+ evaluate();
+ }
+
+ public void destroy() {
+ if (mRegistered) {
+ mContext.unregisterReceiver(mReceiver);
+ mRegistered = false;
+ }
+ }
+
+ public void evaluate() {
+ mHandler.postEvaluate(0);
+ }
+
+ private void fireEvaluate(AlarmClockInfo nextAlarm, long wakeupTime, boolean booted) {
+ for (Callback callback : mCallbacks) {
+ callback.onEvaluate(nextAlarm, wakeupTime, booted);
+ }
+ }
+
+ private void handleEvaluate() {
+ final AlarmClockInfo nextAlarm = mAlarmManager.getNextAlarmClock(mCurrentUserId);
+ final long triggerTime = getEarlyTriggerTime(nextAlarm);
+ final long now = System.currentTimeMillis();
+ final boolean alarmUpcoming = triggerTime > now;
+ final boolean booted = isDoneWaitingAfterBoot(now);
+ if (DEBUG) Slog.d(TAG, "handleEvaluate nextAlarm=" + formatAlarmDebug(triggerTime)
+ + " alarmUpcoming=" + alarmUpcoming
+ + " booted=" + booted);
+ fireEvaluate(nextAlarm, triggerTime, booted);
+ if (!booted) {
+ // recheck after boot
+ final long recheckTime = (mBootCompleted > 0 ? mBootCompleted : now) + WAIT_AFTER_BOOT;
+ rescheduleAlarm(recheckTime);
+ return;
+ }
+ if (alarmUpcoming) {
+ // wake up just before the next alarm
+ rescheduleAlarm(triggerTime);
+ }
+ }
+
+ public static long getEarlyTriggerTime(AlarmClockInfo alarm) {
+ return alarm != null ? (alarm.getTriggerTime() - EARLY) : 0;
+ }
+
+ private boolean isDoneWaitingAfterBoot(long time) {
+ if (mBootCompleted > 0) return (time - mBootCompleted) > WAIT_AFTER_BOOT;
+ if (mInit > 0) return (time - mInit) > WAIT_AFTER_INIT;
+ return true;
+ }
+
+ private static String formatDuration(long millis) {
+ final StringBuilder sb = new StringBuilder();
+ TimeUtils.formatDuration(millis, sb);
+ return sb.toString();
+ }
+
+ public String formatAlarm(AlarmClockInfo alarm) {
+ return alarm != null ? formatAlarm(alarm.getTriggerTime()) : null;
+ }
+
+ private String formatAlarm(long time) {
+ return formatAlarm(time, "Hm", "hma");
+ }
+
+ private String formatAlarm(long time, String skeleton24, String skeleton12) {
+ final String skeleton = DateFormat.is24HourFormat(mContext) ? skeleton24 : skeleton12;
+ final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
+ return DateFormat.format(pattern, time).toString();
+ }
+
+ private String formatAlarmDebug(AlarmClockInfo alarm) {
+ return formatAlarmDebug(alarm != null ? alarm.getTriggerTime() : 0);
+ }
+
+ public String formatAlarmDebug(long time) {
+ if (time <= 0) return Long.toString(time);
+ return String.format("%s (%s)", time, formatAlarm(time, "Hms", "hmsa"));
+ }
+
+ private void rescheduleAlarm(long time) {
+ if (DEBUG) Slog.d(TAG, "rescheduleAlarm " + time);
+ final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, REQUEST_CODE,
+ new Intent(ACTION_TRIGGER)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+ .putExtra(EXTRA_TRIGGER, time),
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ alarms.cancel(pendingIntent);
+ mScheduledAlarmTime = time;
+ if (time > 0) {
+ if (DEBUG) Slog.d(TAG, String.format("Scheduling alarm for %s (in %s)",
+ formatAlarmDebug(time), formatDuration(time - System.currentTimeMillis())));
+ alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
+ }
+ }
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (DEBUG) Slog.d(TAG, "onReceive " + action);
+ long delay = 0;
+ if (action.equals(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)) {
+ delay = NEXT_ALARM_UPDATE_DELAY;
+ if (DEBUG) Slog.d(TAG, String.format(" next alarm for user %s: %s",
+ mCurrentUserId,
+ formatAlarmDebug(mAlarmManager.getNextAlarmClock(mCurrentUserId))));
+ } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
+ mBootCompleted = System.currentTimeMillis();
+ }
+ mHandler.postEvaluate(delay);
+ mWakeLock.acquire(delay + 5000); // stay awake during evaluate
+ }
+ };
+
+ private class H extends Handler {
+ private static final int MSG_EVALUATE = 1;
+
+ public void postEvaluate(long delay) {
+ removeMessages(MSG_EVALUATE);
+ sendEmptyMessageDelayed(MSG_EVALUATE, delay);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_EVALUATE) {
+ handleEvaluate();
+ }
+ }
+ }
+
+ public interface Callback {
+ void onEvaluate(AlarmClockInfo nextAlarm, long wakeupTime, boolean booted);
+ }
+}
diff --git a/telecomm/java/android/telecom/AudioState.java b/telecomm/java/android/telecom/AudioState.java
index 43da38f..f78ce29 100644
--- a/telecomm/java/android/telecom/AudioState.java
+++ b/telecomm/java/android/telecom/AudioState.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -24,7 +25,9 @@
/**
* Encapsulates the telecom audio state, including the current audio routing, supported audio
* routing and mute.
+ * @hide
*/
+@SystemApi
public final class AudioState implements Parcelable {
/** Direct the audio stream through the device's earpiece. */
public static final int ROUTE_EARPIECE = 0x00000001;
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 32bdbe0..215c682 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -16,6 +16,8 @@
package android.telecom;
+import android.annotation.SystemApi;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -25,7 +27,9 @@
/**
* Represents a conference call which can contain any number of {@link Connection} objects.
+ * @hide
*/
+@SystemApi
public abstract class Conference {
/** @hide */
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 61b471c..63b44a6 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -19,6 +19,7 @@
import com.android.internal.telecom.IVideoCallback;
import com.android.internal.telecom.IVideoProvider;
+import android.annotation.SystemApi;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
@@ -43,7 +44,9 @@
* Implementations are then responsible for updating the state of the {@code Connection}, and
* must call {@link #destroy()} to signal to the framework that the {@code Connection} is no
* longer used and associated resources may be recovered.
+ * @hide
*/
+@SystemApi
public abstract class Connection {
public static final int STATE_INITIALIZING = 0;
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index 71b481b..f691c17 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.annotation.SystemApi;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
@@ -24,7 +25,9 @@
/**
* Simple data container encapsulating a request to some entity to
* create a new {@link Connection}.
+ * @hide
*/
+@SystemApi
public final class ConnectionRequest implements Parcelable {
// TODO: Token to limit recursive invocations
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 65d48f1..48e6ff3 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -17,6 +17,7 @@
package android.telecom;
import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
@@ -71,7 +72,9 @@
* receives call-commands such as answer, reject, hold and disconnect.
* <p>
* When there are no more live calls, telecom will unbind from the {@code ConnectionService}.
+ * @hide
*/
+@SystemApi
public abstract class ConnectionService extends Service {
/**
* The {@link Intent} that must be declared as handled by the service.
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 73bcd0c..130d676 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.media.ToneGenerator;
@@ -29,7 +30,9 @@
* user. It is the responsibility of the {@link ConnectionService} to provide localized versions of
* the label and description. It also may contain a reason for the disconnect, which is intended for
* logging and not for display to the user.
+ * @hide
*/
+@SystemApi
public final class DisconnectCause implements Parcelable {
/** Disconnected because of an unknown or unspecified reason. */
diff --git a/telecomm/java/android/telecom/GatewayInfo.java b/telecomm/java/android/telecom/GatewayInfo.java
index 7105602..5b8e4ab 100644
--- a/telecomm/java/android/telecom/GatewayInfo.java
+++ b/telecomm/java/android/telecom/GatewayInfo.java
@@ -34,7 +34,9 @@
* <li> Call the appropriate gateway address.
* <li> Display information about how the call is being routed to the user.
* </ol>
+ * @hide
*/
+@SystemApi
public class GatewayInfo implements Parcelable {
private final String mGatewayProviderPackageName;
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 11da0f2..a85e84d 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -54,6 +54,7 @@
private static final int MSG_SET_POST_DIAL_WAIT = 4;
private static final int MSG_ON_AUDIO_STATE_CHANGED = 5;
private static final int MSG_BRING_TO_FOREGROUND = 6;
+ private static final int MSG_ON_CAN_ADD_CALL_CHANGED = 7;
/** Default Handler used to consolidate binder method calls onto a single thread. */
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@@ -91,6 +92,9 @@
case MSG_BRING_TO_FOREGROUND:
mPhone.internalBringToForeground(msg.arg1 == 1);
break;
+ case MSG_ON_CAN_ADD_CALL_CHANGED:
+ mPhone.internalSetCanAddCall(msg.arg1 == 1);
+ break;
default:
break;
}
@@ -136,6 +140,12 @@
public void bringToForeground(boolean showDialpad) {
mHandler.obtainMessage(MSG_BRING_TO_FOREGROUND, showDialpad ? 1 : 0, 0).sendToTarget();
}
+
+ @Override
+ public void onCanAddCallChanged(boolean canAddCall) {
+ mHandler.obtainMessage(MSG_ON_CAN_ADD_CALL_CHANGED, canAddCall ? 1 : 0, 0)
+ .sendToTarget();
+ }
}
private Phone mPhone;
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index 5131790..6344181 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -74,6 +74,16 @@
* @param call A newly removed {@code Call}.
*/
public void onCallRemoved(Phone phone, Call call) { }
+
+ /**
+ * Called when the {@code Phone} ability to add more calls changes. If the phone cannot
+ * support more calls then {@code canAddCall} is set to {@code false}. If it can, then it
+ * is set to {@code true}.
+ *
+ * @param phone The {@code Phone} calling this method.
+ * @param canAddCall Indicates whether an additional call can be added.
+ */
+ public void onCanAddCallChanged(Phone phone, boolean canAddCall) { }
}
// A Map allows us to track each Call by its Telecom-specified call ID
@@ -92,6 +102,8 @@
private final List<Listener> mListeners = new CopyOnWriteArrayList<>();
+ private boolean mCanAddCall = true;
+
/** {@hide} */
Phone(InCallAdapter adapter) {
mInCallAdapter = adapter;
@@ -149,6 +161,14 @@
fireBringToForeground(showDialpad);
}
+ /** {@hide} */
+ final void internalSetCanAddCall(boolean canAddCall) {
+ if (mCanAddCall != canAddCall) {
+ mCanAddCall = canAddCall;
+ fireCanAddCallChanged(canAddCall);
+ }
+ }
+
/**
* Called to destroy the phone and cleanup any lingering calls.
* @hide
@@ -191,6 +211,15 @@
}
/**
+ * Returns if the {@code Phone} can support additional calls.
+ *
+ * @return Whether the phone supports adding more calls.
+ */
+ public final boolean canAddCall() {
+ return mCanAddCall;
+ }
+
+ /**
* Sets the microphone mute state. When this request is honored, there will be change to
* the {@link #getAudioState()}.
*
@@ -266,6 +295,12 @@
}
}
+ private void fireCanAddCallChanged(boolean canAddCall) {
+ for (Listener listener : mListeners) {
+ listener.onCanAddCallChanged(this, canAddCall);
+ }
+ }
+
private void checkCallTree(ParcelableCall parcelableCall) {
if (parcelableCall.getParentCallId() != null &&
!mCallByTelecomCallId.containsKey(parcelableCall.getParentCallId())) {
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 3fc1d3d..f9ee5d2 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -45,7 +46,9 @@
* alternative options when placing a phone call. When building a {@link PhoneAccount}, the app
* should supply a valid {@link PhoneAccountHandle} that references the {@link ConnectionService}
* implementation Telecom will use to interact with the app.
+ * @hide
*/
+@SystemApi
public class PhoneAccount implements Parcelable {
/**
diff --git a/telecomm/java/android/telecom/PhoneAccountHandle.java b/telecomm/java/android/telecom/PhoneAccountHandle.java
index bc4cc8c..7bcf147 100644
--- a/telecomm/java/android/telecom/PhoneAccountHandle.java
+++ b/telecomm/java/android/telecom/PhoneAccountHandle.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.annotation.SystemApi;
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
@@ -33,7 +34,9 @@
*
* See {@link PhoneAccount},
* {@link TelecomManager#registerPhoneAccount TelecomManager.registerPhoneAccount}.
+ * @hide
*/
+@SystemApi
public class PhoneAccountHandle implements Parcelable {
private ComponentName mComponentName;
private String mId;
diff --git a/telecomm/java/android/telecom/PhoneCapabilities.java b/telecomm/java/android/telecom/PhoneCapabilities.java
index 3d3c6bd..feb7a95 100644
--- a/telecomm/java/android/telecom/PhoneCapabilities.java
+++ b/telecomm/java/android/telecom/PhoneCapabilities.java
@@ -16,10 +16,14 @@
package android.telecom;
+import android.annotation.SystemApi;
+
/**
* Defines capabilities for {@link Connection}s and {@link Conference}s such as hold, swap, and
* merge.
+ * @hide
*/
+@SystemApi
public final class PhoneCapabilities {
/** Call can currently be put on hold or unheld. */
public static final int HOLD = 0x00000001;
@@ -46,8 +50,10 @@
*/
public static final int SWAP_CONFERENCE = 0x00000008;
- /** Call currently supports adding another call to this one. */
- public static final int ADD_CALL = 0x00000010;
+ /**
+ * @hide
+ */
+ public static final int UNUSED = 0x00000010;
/** Call supports responding via text option. */
public static final int RESPOND_VIA_TEXT = 0x00000020;
@@ -96,7 +102,7 @@
public static final int DISCONNECT_FROM_CONFERENCE = 0x00002000;
public static final int ALL = HOLD | SUPPORT_HOLD | MERGE_CONFERENCE | SWAP_CONFERENCE
- | ADD_CALL | RESPOND_VIA_TEXT | MUTE | MANAGE_CONFERENCE | SEPARATE_FROM_CONFERENCE
+ | RESPOND_VIA_TEXT | MUTE | MANAGE_CONFERENCE | SEPARATE_FROM_CONFERENCE
| DISCONNECT_FROM_CONFERENCE;
/**
@@ -136,9 +142,6 @@
if (can(capabilities, SWAP_CONFERENCE)) {
builder.append(" SWAP_CONFERENCE");
}
- if (can(capabilities, ADD_CALL)) {
- builder.append(" ADD_CALL");
- }
if (can(capabilities, RESPOND_VIA_TEXT)) {
builder.append(" RESPOND_VIA_TEXT");
}
diff --git a/telecomm/java/android/telecom/RemoteConference.java b/telecomm/java/android/telecom/RemoteConference.java
index eba7580..b548274 100644
--- a/telecomm/java/android/telecom/RemoteConference.java
+++ b/telecomm/java/android/telecom/RemoteConference.java
@@ -18,6 +18,7 @@
import com.android.internal.telecom.IConnectionService;
+import android.annotation.SystemApi;
import android.os.RemoteException;
import java.util.ArrayList;
@@ -29,7 +30,9 @@
/**
* Represents a conference call which can contain any number of {@link Connection} objects.
+ * @hide
*/
+@SystemApi
public final class RemoteConference {
public abstract static class Callback {
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index 816e2bf..d70ddf6 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -20,6 +20,7 @@
import com.android.internal.telecom.IVideoCallback;
import com.android.internal.telecom.IVideoProvider;
+import android.annotation.SystemApi;
import android.net.Uri;
import android.os.IBinder;
import android.os.RemoteException;
@@ -37,7 +38,9 @@
*
* @see ConnectionService#createRemoteOutgoingConnection(PhoneAccountHandle, ConnectionRequest)
* @see ConnectionService#createRemoteIncomingConnection(PhoneAccountHandle, ConnectionRequest)
+ * @hide
*/
+@SystemApi
public final class RemoteConnection {
public static abstract class Callback {
diff --git a/telecomm/java/android/telecom/StatusHints.java b/telecomm/java/android/telecom/StatusHints.java
index a32eae7..dd3a639 100644
--- a/telecomm/java/android/telecom/StatusHints.java
+++ b/telecomm/java/android/telecom/StatusHints.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -29,7 +30,9 @@
/**
* Contains status label and icon displayed in the in-call UI.
+ * @hide
*/
+@SystemApi
public final class StatusHints implements Parcelable {
private final ComponentName mPackageName;
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index ecf6005..05108c7 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -33,8 +33,7 @@
/**
* Provides access to information about active calls and registration/call-management functionality.
- * Apps can use methods in this class to determine the current call state. Apps can also register new
- * {@link PhoneAccount}s and get a listing of existing {@link PhoneAccount}s.
+ * Apps can use methods in this class to determine the current call state.
* <p>
* Apps do not instantiate this class directly; instead, they retrieve a reference to an instance
* through {@link Context#getSystemService Context.getSystemService(Context.TELECOM_SERVICE)}.
@@ -71,7 +70,9 @@
/**
* The {@link android.content.Intent} action used to configure a
* {@link android.telecom.ConnectionService}.
+ * @hide
*/
+ @SystemApi
public static final String ACTION_CONNECTION_SERVICE_CONFIGURE =
"android.telecom.action.CONNECTION_SERVICE_CONFIGURE";
@@ -84,7 +85,9 @@
/**
* The {@link android.content.Intent} action used to show the settings page used to configure
* {@link PhoneAccount} preferences.
+ * @hide
*/
+ @SystemApi
public static final String ACTION_CHANGE_PHONE_ACCOUNTS =
"android.telecom.action.CHANGE_PHONE_ACCOUNTS";
@@ -114,7 +117,9 @@
* {@link PhoneAccountHandle} to use when making the call.
* <p class="note">
* Retrieve with {@link android.content.Intent#getParcelableExtra(String)}.
+ * @hide
*/
+ @SystemApi
public static final String EXTRA_PHONE_ACCOUNT_HANDLE =
"android.telecom.extra.PHONE_ACCOUNT_HANDLE";
@@ -125,6 +130,7 @@
*
* @hide
*/
+ @SystemApi
public static final String EXTRA_INCOMING_CALL_EXTRAS =
"android.telecom.extra.INCOMING_CALL_EXTRAS";
@@ -136,6 +142,7 @@
*
* @hide
*/
+ @SystemApi
public static final String EXTRA_OUTGOING_CALL_EXTRAS =
"android.telecom.extra.OUTGOING_CALL_EXTRAS";
@@ -162,7 +169,9 @@
/**
* Optional extra for {@link android.telephony.TelephonyManager#ACTION_PHONE_STATE_CHANGED}
* containing the component name of the associated connection service.
+ * @hide
*/
+ @SystemApi
public static final String EXTRA_CONNECTION_SERVICE =
"android.telecom.extra.CONNECTION_SERVICE";
@@ -197,7 +206,9 @@
* {@link ConnectionService}s which interact with {@link RemoteConnection}s should only populate
* this if the {@link android.telephony.TelephonyManager#getLine1Number()} value, as that is the
* user's expected caller ID.
+ * @hide
*/
+ @SystemApi
public static final String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
/**
@@ -338,7 +349,9 @@
* @param uriScheme The URI scheme.
* @return The {@link PhoneAccountHandle} corresponding to the user-chosen default for outgoing
* phone calls for a specified URI scheme.
+ * @hide
*/
+ @SystemApi
public PhoneAccountHandle getDefaultOutgoingPhoneAccount(String uriScheme) {
try {
if (isServiceConnected()) {
@@ -441,7 +454,9 @@
* {@code PhoneAccount}.
*
* @return The phone account handle of the current connection manager.
+ * @hide
*/
+ @SystemApi
public PhoneAccountHandle getConnectionManager() {
return getSimCallManager();
}
@@ -457,7 +472,9 @@
*
* @param uriScheme The URI scheme.
* @return A list of {@code PhoneAccountHandle} objects supporting the URI scheme.
+ * @hide
*/
+ @SystemApi
public List<PhoneAccountHandle> getPhoneAccountsSupportingScheme(String uriScheme) {
try {
if (isServiceConnected()) {
@@ -496,7 +513,9 @@
*
* @return {@code true} if the device has more than one account registered and {@code false}
* otherwise.
+ * @hide
*/
+ @SystemApi
public boolean hasMultipleCallCapableAccounts() {
return getCallCapablePhoneAccounts().size() > 1;
}
@@ -505,7 +524,9 @@
* Returns a list of all {@link PhoneAccount}s registered for the calling package.
*
* @return A list of {@code PhoneAccountHandle} objects.
+ * @hide
*/
+ @SystemApi
public List<PhoneAccountHandle> getPhoneAccountsForPackage() {
try {
if (isServiceConnected()) {
@@ -523,7 +544,9 @@
*
* @param account The {@link PhoneAccountHandle}.
* @return The {@link PhoneAccount} object.
+ * @hide
*/
+ @SystemApi
public PhoneAccount getPhoneAccount(PhoneAccountHandle account) {
try {
if (isServiceConnected()) {
@@ -602,7 +625,10 @@
* {@link PhoneAccountHandle#getComponentName()} does not match the package name of the app.
*
* @param account The complete {@link PhoneAccount}.
+ *
+ * @hide
*/
+ @SystemApi
public void registerPhoneAccount(PhoneAccount account) {
try {
if (isServiceConnected()) {
@@ -617,7 +643,9 @@
* Remove a {@link PhoneAccount} registration from the system.
*
* @param accountHandle A {@link PhoneAccountHandle} for the {@link PhoneAccount} to unregister.
+ * @hide
*/
+ @SystemApi
public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
try {
if (isServiceConnected()) {
@@ -630,7 +658,9 @@
/**
* Remove all Accounts that belong to the calling package from the system.
+ * @hide
*/
+ @SystemApi
public void clearAccounts() {
try {
if (isServiceConnected()) {
@@ -853,7 +883,9 @@
* {@link #registerPhoneAccount}.
* @param extras A bundle that will be passed through to
* {@link ConnectionService#onCreateIncomingConnection}.
+ * @hide
*/
+ @SystemApi
public void addNewIncomingCall(PhoneAccountHandle phoneAccount, Bundle extras) {
try {
if (isServiceConnected()) {
@@ -923,7 +955,9 @@
* @param accountHandle The handle for the account the MMI code should apply to.
* @param dialString The digits to dial.
* @return True if the digits were processed as an MMI code, false otherwise.
+ * @hide
*/
+ @SystemApi
public boolean handleMmi(PhoneAccountHandle accountHandle, String dialString) {
ITelecomService service = getTelecomService();
if (service != null) {
@@ -941,7 +975,9 @@
* {@code null} to return a URI which will use the default account.
* @return The URI (with the content:// scheme) specific to the specified {@link PhoneAccount}
* for the the content retrieve.
+ * @hide
*/
+ @SystemApi
public Uri getAdnUriForPhoneAccount(PhoneAccountHandle accountHandle) {
ITelecomService service = getTelecomService();
if (service != null && accountHandle != null) {
diff --git a/telecomm/java/com/android/internal/telecom/IInCallService.aidl b/telecomm/java/com/android/internal/telecom/IInCallService.aidl
index 35f6f65..d26f6cb 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallService.aidl
@@ -43,4 +43,6 @@
void onAudioStateChanged(in AudioState audioState);
void bringToForeground(boolean showDialpad);
+
+ void onCanAddCallChanged(boolean canAddCall);
}
diff --git a/tools/aapt/AaptConfig.cpp b/tools/aapt/AaptConfig.cpp
index 848d9a1..e88c27a 100644
--- a/tools/aapt/AaptConfig.cpp
+++ b/tools/aapt/AaptConfig.cpp
@@ -21,6 +21,7 @@
#include "AaptAssets.h"
#include "AaptUtil.h"
#include "ResourceFilter.h"
+#include "SdkConstants.h"
using android::String8;
using android::Vector;
@@ -241,7 +242,7 @@
uint16_t minSdk = 0;
if (config->density == ResTable_config::DENSITY_ANY) {
- minSdk = SDK_L;
+ minSdk = SDK_LOLLIPOP;
} else if (config->smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
|| config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY
|| config->screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
@@ -800,7 +801,7 @@
}
if (config.density == ResTable_config::DENSITY_ANY) {
- if (config.sdkVersion != SDK_L) {
+ if (config.sdkVersion != SDK_LOLLIPOP) {
// Someone modified the sdkVersion from the default, this is not safe to assume.
return false;
}
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index cb34448..0e130f4 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -14,18 +14,7 @@
#include <utils/String8.h>
#include <utils/Vector.h>
-enum {
- SDK_CUPCAKE = 3,
- SDK_DONUT = 4,
- SDK_ECLAIR = 5,
- SDK_ECLAIR_0_1 = 6,
- SDK_MR1 = 7,
- SDK_FROYO = 8,
- SDK_HONEYCOMB_MR2 = 13,
- SDK_ICE_CREAM_SANDWICH = 14,
- SDK_ICE_CREAM_SANDWICH_MR1 = 15,
- SDK_L = 21,
-};
+#include "SdkConstants.h"
/*
* Things we can do.
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index beff604..bdc6586 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -10,6 +10,7 @@
#include "XMLNode.h"
#include "ResourceFilter.h"
#include "ResourceIdCache.h"
+#include "SdkConstants.h"
#include <androidfw/ResourceTypes.h>
#include <utils/ByteOrder.h>
@@ -4223,7 +4224,7 @@
}
const int minSdk = atoi(bundle->getMinSdkVersion());
- if (minSdk >= SDK_L) {
+ if (minSdk >= SDK_LOLLIPOP) {
return true;
}
}
@@ -4314,7 +4315,7 @@
}
const ConfigDescription& config = entries.keyAt(ei);
- if (config.sdkVersion >= SDK_L) {
+ if (config.sdkVersion >= SDK_LOLLIPOP) {
// We don't need to do anything if the resource is
// already qualified for version 21 or higher.
continue;
@@ -4336,9 +4337,9 @@
}
// Duplicate the entry under the same configuration
- // but with sdkVersion == SDK_L.
+ // but with sdkVersion == SDK_LOLLIPOP.
ConfigDescription newConfig(config);
- newConfig.sdkVersion = SDK_L;
+ newConfig.sdkVersion = SDK_LOLLIPOP;
entriesToAdd.add(key_value_pair_t<ConfigDescription, sp<Entry> >(
newConfig, new Entry(*e)));
@@ -4361,7 +4362,7 @@
if (bundle->getVerbose()) {
entriesToAdd[i].value->getPos()
.printf("using v%d attributes; synthesizing resource %s:%s/%s for configuration %s.",
- SDK_L,
+ SDK_LOLLIPOP,
String8(p->getName()).string(),
String8(t->getName()).string(),
String8(entriesToAdd[i].value->getName()).string(),
@@ -4388,7 +4389,7 @@
return NO_ERROR;
}
- if (target->getResourceType() == "" || target->getGroupEntry().toParams().sdkVersion >= SDK_L) {
+ if (target->getResourceType() == "" || target->getGroupEntry().toParams().sdkVersion >= SDK_LOLLIPOP) {
// Skip resources that have no type (AndroidManifest.xml) or are already version qualified with v21
// or higher.
return NO_ERROR;
@@ -4424,7 +4425,7 @@
}
ConfigDescription newConfig(target->getGroupEntry().toParams());
- newConfig.sdkVersion = SDK_L;
+ newConfig.sdkVersion = SDK_LOLLIPOP;
// Look to see if we already have an overriding v21 configuration.
sp<ConfigList> cl = getConfigList(String16(mAssets->getPackage()),
@@ -4446,7 +4447,7 @@
if (bundle->getVerbose()) {
SourcePos(target->getSourceFile(), -1).printf(
"using v%d attributes; synthesizing resource %s:%s/%s for configuration %s.",
- SDK_L,
+ SDK_LOLLIPOP,
mAssets->getPackage().string(),
newFile->getResourceType().string(),
String8(resourceName).string(),
diff --git a/tools/aapt/SdkConstants.h b/tools/aapt/SdkConstants.h
new file mode 100644
index 0000000..7fd1030
--- /dev/null
+++ b/tools/aapt/SdkConstants.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef H_AAPT_SDK_CONSTANTS
+#define H_AAPT_SDK_CONSTANTS
+
+enum {
+ SDK_CUPCAKE = 3,
+ SDK_DONUT = 4,
+ SDK_ECLAIR = 5,
+ SDK_ECLAIR_0_1 = 6,
+ SDK_ECLAIR_MR1 = 7,
+ SDK_FROYO = 8,
+ SDK_GINGERBREAD = 9,
+ SDK_GINGERBREAD_MR1 = 10,
+ SDK_HONEYCOMB = 11,
+ SDK_HONEYCOMB_MR1 = 12,
+ SDK_HONEYCOMB_MR2 = 13,
+ SDK_ICE_CREAM_SANDWICH = 14,
+ SDK_ICE_CREAM_SANDWICH_MR1 = 15,
+ SDK_JELLY_BEAN = 16,
+ SDK_JELLY_BEAN_MR1 = 17,
+ SDK_JELLY_BEAN_MR2 = 18,
+ SDK_KITKAT = 19,
+ SDK_KITKAT_WATCH = 20,
+ SDK_LOLLIPOP = 21,
+};
+
+#endif // H_AAPT_SDK_CONSTANTS
diff --git a/tools/layoutlib/bridge/resources/icons/shadow-b.png b/tools/layoutlib/bridge/resources/icons/shadow-b.png
new file mode 100644
index 0000000..68f4f4b
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow-b.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow-bl.png b/tools/layoutlib/bridge/resources/icons/shadow-bl.png
new file mode 100644
index 0000000..ee7dbe8
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow-bl.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow-br.png b/tools/layoutlib/bridge/resources/icons/shadow-br.png
new file mode 100644
index 0000000..c45ad77
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow-br.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow-l.png b/tools/layoutlib/bridge/resources/icons/shadow-l.png
new file mode 100644
index 0000000..77d0bd0
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow-l.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow-r.png b/tools/layoutlib/bridge/resources/icons/shadow-r.png
new file mode 100644
index 0000000..4af7a33
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow-r.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow-tl.png b/tools/layoutlib/bridge/resources/icons/shadow-tl.png
new file mode 100644
index 0000000..424fb36
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow-tl.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow-tr.png b/tools/layoutlib/bridge/resources/icons/shadow-tr.png
new file mode 100644
index 0000000..1fd0c772
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow-tr.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow2-b.png b/tools/layoutlib/bridge/resources/icons/shadow2-b.png
new file mode 100644
index 0000000..963973e
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow2-b.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow2-bl.png b/tools/layoutlib/bridge/resources/icons/shadow2-bl.png
new file mode 100644
index 0000000..7612487
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow2-bl.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow2-br.png b/tools/layoutlib/bridge/resources/icons/shadow2-br.png
new file mode 100644
index 0000000..8e20252
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow2-br.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow2-l.png b/tools/layoutlib/bridge/resources/icons/shadow2-l.png
new file mode 100644
index 0000000..2db18a0
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow2-l.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow2-r.png b/tools/layoutlib/bridge/resources/icons/shadow2-r.png
new file mode 100644
index 0000000..8e026f1
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow2-r.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow2-tl.png b/tools/layoutlib/bridge/resources/icons/shadow2-tl.png
new file mode 100644
index 0000000..a8045ed
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow2-tl.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/icons/shadow2-tr.png b/tools/layoutlib/bridge/resources/icons/shadow2-tr.png
new file mode 100644
index 0000000..590373c
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/icons/shadow2-tr.png
Binary files differ
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
index f4282ad..8d24d38 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
@@ -141,7 +141,6 @@
* Creates and returns a {@link Bitmap} initialized with the given stream content.
*
* @param input the stream from which to read the bitmap content
- * @param createFlags
* @param density the density associated with the bitmap
*
* @see Bitmap#isPremultiplied()
@@ -166,8 +165,7 @@
* @see Bitmap#isMutable()
* @see Bitmap#getDensity()
*/
- public static Bitmap createBitmap(BufferedImage image, boolean isMutable,
- Density density) throws IOException {
+ public static Bitmap createBitmap(BufferedImage image, boolean isMutable, Density density) {
return createBitmap(image, getPremultipliedBitmapCreateFlags(isMutable), density);
}
@@ -175,7 +173,6 @@
* Creates and returns a {@link Bitmap} initialized with the given {@link BufferedImage}
*
* @param image the bitmap content
- * @param createFlags
* @param density the density associated with the bitmap
*
* @see Bitmap#isPremultiplied()
@@ -183,7 +180,7 @@
* @see Bitmap#getDensity()
*/
public static Bitmap createBitmap(BufferedImage image, Set<BitmapCreateFlags> createFlags,
- Density density) throws IOException {
+ Density density) {
// create a delegate with the given image.
Bitmap_Delegate delegate = new Bitmap_Delegate(image, Config.ARGB_8888);
diff --git a/tools/layoutlib/bridge/src/android/view/RenderNode_Delegate.java b/tools/layoutlib/bridge/src/android/view/RenderNode_Delegate.java
new file mode 100644
index 0000000..6c949d9
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/view/RenderNode_Delegate.java
@@ -0,0 +1,72 @@
+/*
+ * 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.view;
+
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate implementing the native methods of {@link RenderNode}
+ * <p/>
+ * Through the layoutlib_create tool, some native methods of RenderNode have been replaced by calls
+ * to methods of the same name in this delegate class.
+ *
+ * @see DelegateManager
+ */
+public class RenderNode_Delegate {
+
+
+ // ---- delegate manager ----
+ private static final DelegateManager<RenderNode_Delegate> sManager =
+ new DelegateManager<RenderNode_Delegate>(RenderNode_Delegate.class);
+
+
+ private float mLift;
+ @SuppressWarnings("UnusedDeclaration")
+ private String mName;
+
+ @LayoutlibDelegate
+ /*package*/ static long nCreate(String name) {
+ RenderNode_Delegate renderNodeDelegate = new RenderNode_Delegate();
+ renderNodeDelegate.mName = name;
+ return sManager.addNewDelegate(renderNodeDelegate);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nDestroyRenderNode(long renderNode) {
+ sManager.removeJavaReferenceFor(renderNode);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static boolean nSetElevation(long renderNode, float lift) {
+ RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
+ if (delegate != null && delegate.mLift != lift) {
+ delegate.mLift = lift;
+ return true;
+ }
+ return false;
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static float nGetElevation(long renderNode) {
+ RenderNode_Delegate delegate = sManager.getDelegate(renderNode);
+ if (delegate != null) {
+ return delegate.mLift;
+ }
+ return 0f;
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/view/ShadowPainter.java b/tools/layoutlib/bridge/src/android/view/ShadowPainter.java
new file mode 100644
index 0000000..38846bd
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/view/ShadowPainter.java
@@ -0,0 +1,415 @@
+/*
+ * 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.view;
+
+import com.android.annotations.NonNull;
+
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.imageio.ImageIO;
+
+public class ShadowPainter {
+
+ /**
+ * Adds a drop shadow to a semi-transparent image (of an arbitrary shape) and returns it as a
+ * new image. This method attempts to mimic the same visual characteristics as the rectangular
+ * shadow painting methods in this class, {@link #createRectangularDropShadow(java.awt.image.BufferedImage)}
+ * and {@link #createSmallRectangularDropShadow(java.awt.image.BufferedImage)}.
+ *
+ * @param source the source image
+ * @param shadowSize the size of the shadow, normally {@link #SHADOW_SIZE or {@link
+ * #SMALL_SHADOW_SIZE}}
+ *
+ * @return a new image with the shadow painted in
+ */
+ @NonNull
+ public static BufferedImage createDropShadow(BufferedImage source, int shadowSize) {
+ shadowSize /= 2; // make shadow size have the same meaning as in the other shadow paint methods in this class
+
+ return createDropShadow(source, shadowSize, 0.7f, 0);
+ }
+
+ /**
+ * Creates a drop shadow of a given image and returns a new image which shows the input image on
+ * top of its drop shadow.
+ * <p/>
+ * <b>NOTE: If the shape is rectangular and opaque, consider using {@link
+ * #drawRectangleShadow(Graphics2D, int, int, int, int)} instead.</b>
+ *
+ * @param source the source image to be shadowed
+ * @param shadowSize the size of the shadow in pixels
+ * @param shadowOpacity the opacity of the shadow, with 0=transparent and 1=opaque
+ * @param shadowRgb the RGB int to use for the shadow color
+ *
+ * @return a new image with the source image on top of its shadow
+ */
+ @SuppressWarnings({"SuspiciousNameCombination", "UnnecessaryLocalVariable"}) // Imported code
+ public static BufferedImage createDropShadow(BufferedImage source, int shadowSize,
+ float shadowOpacity, int shadowRgb) {
+
+ // This code is based on
+ // http://www.jroller.com/gfx/entry/non_rectangular_shadow
+
+ BufferedImage image;
+ int width = source.getWidth();
+ int height = source.getHeight();
+ image = new BufferedImage(width + SHADOW_SIZE, height + SHADOW_SIZE,
+ BufferedImage.TYPE_INT_ARGB);
+
+ Graphics2D g2 = image.createGraphics();
+ g2.drawImage(image, shadowSize, shadowSize, null);
+
+ int dstWidth = image.getWidth();
+ int dstHeight = image.getHeight();
+
+ int left = (shadowSize - 1) >> 1;
+ int right = shadowSize - left;
+ int xStart = left;
+ int xStop = dstWidth - right;
+ int yStart = left;
+ int yStop = dstHeight - right;
+
+ shadowRgb &= 0x00FFFFFF;
+
+ int[] aHistory = new int[shadowSize];
+ int historyIdx;
+
+ int aSum;
+
+ int[] dataBuffer = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
+ int lastPixelOffset = right * dstWidth;
+ float sumDivider = shadowOpacity / shadowSize;
+
+ // horizontal pass
+ for (int y = 0, bufferOffset = 0; y < dstHeight; y++, bufferOffset = y * dstWidth) {
+ aSum = 0;
+ historyIdx = 0;
+ for (int x = 0; x < shadowSize; x++, bufferOffset++) {
+ int a = dataBuffer[bufferOffset] >>> 24;
+ aHistory[x] = a;
+ aSum += a;
+ }
+
+ bufferOffset -= right;
+
+ for (int x = xStart; x < xStop; x++, bufferOffset++) {
+ int a = (int) (aSum * sumDivider);
+ dataBuffer[bufferOffset] = a << 24 | shadowRgb;
+
+ // subtract the oldest pixel from the sum
+ aSum -= aHistory[historyIdx];
+
+ // get the latest pixel
+ a = dataBuffer[bufferOffset + right] >>> 24;
+ aHistory[historyIdx] = a;
+ aSum += a;
+
+ if (++historyIdx >= shadowSize) {
+ historyIdx -= shadowSize;
+ }
+ }
+ }
+ // vertical pass
+ for (int x = 0, bufferOffset = 0; x < dstWidth; x++, bufferOffset = x) {
+ aSum = 0;
+ historyIdx = 0;
+ for (int y = 0; y < shadowSize; y++, bufferOffset += dstWidth) {
+ int a = dataBuffer[bufferOffset] >>> 24;
+ aHistory[y] = a;
+ aSum += a;
+ }
+
+ bufferOffset -= lastPixelOffset;
+
+ for (int y = yStart; y < yStop; y++, bufferOffset += dstWidth) {
+ int a = (int) (aSum * sumDivider);
+ dataBuffer[bufferOffset] = a << 24 | shadowRgb;
+
+ // subtract the oldest pixel from the sum
+ aSum -= aHistory[historyIdx];
+
+ // get the latest pixel
+ a = dataBuffer[bufferOffset + lastPixelOffset] >>> 24;
+ aHistory[historyIdx] = a;
+ aSum += a;
+
+ if (++historyIdx >= shadowSize) {
+ historyIdx -= shadowSize;
+ }
+ }
+ }
+
+ g2.drawImage(source, null, 0, 0);
+ g2.dispose();
+
+ return image;
+ }
+
+ /**
+ * Draws a rectangular drop shadow (of size {@link #SHADOW_SIZE} by {@link #SHADOW_SIZE} around
+ * the given source and returns a new image with both combined
+ *
+ * @param source the source image
+ *
+ * @return the source image with a drop shadow on the bottom and right
+ */
+ @SuppressWarnings("UnusedDeclaration")
+ public static BufferedImage createRectangularDropShadow(BufferedImage source) {
+ int type = source.getType();
+ if (type == BufferedImage.TYPE_CUSTOM) {
+ type = BufferedImage.TYPE_INT_ARGB;
+ }
+
+ int width = source.getWidth();
+ int height = source.getHeight();
+ BufferedImage image;
+ image = new BufferedImage(width + SHADOW_SIZE, height + SHADOW_SIZE, type);
+ Graphics2D g = image.createGraphics();
+ g.drawImage(source, 0, 0, null);
+ drawRectangleShadow(image, 0, 0, width, height);
+ g.dispose();
+
+ return image;
+ }
+
+ /**
+ * Draws a small rectangular drop shadow (of size {@link #SMALL_SHADOW_SIZE} by {@link
+ * #SMALL_SHADOW_SIZE} around the given source and returns a new image with both combined
+ *
+ * @param source the source image
+ *
+ * @return the source image with a drop shadow on the bottom and right
+ */
+ @SuppressWarnings("UnusedDeclaration")
+ public static BufferedImage createSmallRectangularDropShadow(BufferedImage source) {
+ int type = source.getType();
+ if (type == BufferedImage.TYPE_CUSTOM) {
+ type = BufferedImage.TYPE_INT_ARGB;
+ }
+
+ int width = source.getWidth();
+ int height = source.getHeight();
+
+ BufferedImage image;
+ image = new BufferedImage(width + SMALL_SHADOW_SIZE, height + SMALL_SHADOW_SIZE, type);
+
+ Graphics2D g = image.createGraphics();
+ g.drawImage(source, 0, 0, null);
+ drawSmallRectangleShadow(image, 0, 0, width, height);
+ g.dispose();
+
+ return image;
+ }
+
+ /**
+ * Draws a drop shadow for the given rectangle into the given context. It will not draw anything
+ * if the rectangle is smaller than a minimum determined by the assets used to draw the shadow
+ * graphics. The size of the shadow is {@link #SHADOW_SIZE}.
+ *
+ * @param image the image to draw the shadow into
+ * @param x the left coordinate of the left hand side of the rectangle
+ * @param y the top coordinate of the top of the rectangle
+ * @param width the width of the rectangle
+ * @param height the height of the rectangle
+ */
+ public static void drawRectangleShadow(BufferedImage image,
+ int x, int y, int width, int height) {
+ Graphics2D gc = image.createGraphics();
+ try {
+ drawRectangleShadow(gc, x, y, width, height);
+ } finally {
+ gc.dispose();
+ }
+ }
+
+ /**
+ * Draws a small drop shadow for the given rectangle into the given context. It will not draw
+ * anything if the rectangle is smaller than a minimum determined by the assets used to draw the
+ * shadow graphics. The size of the shadow is {@link #SMALL_SHADOW_SIZE}.
+ *
+ * @param image the image to draw the shadow into
+ * @param x the left coordinate of the left hand side of the rectangle
+ * @param y the top coordinate of the top of the rectangle
+ * @param width the width of the rectangle
+ * @param height the height of the rectangle
+ */
+ public static void drawSmallRectangleShadow(BufferedImage image,
+ int x, int y, int width, int height) {
+ Graphics2D gc = image.createGraphics();
+ try {
+ drawSmallRectangleShadow(gc, x, y, width, height);
+ } finally {
+ gc.dispose();
+ }
+ }
+
+ /**
+ * The width and height of the drop shadow painted by
+ * {@link #drawRectangleShadow(Graphics2D, int, int, int, int)}
+ */
+ public static final int SHADOW_SIZE = 20; // DO NOT EDIT. This corresponds to bitmap graphics
+
+ /**
+ * The width and height of the drop shadow painted by
+ * {@link #drawSmallRectangleShadow(Graphics2D, int, int, int, int)}
+ */
+ public static final int SMALL_SHADOW_SIZE = 10; // DO NOT EDIT. Corresponds to bitmap graphics
+
+ /**
+ * Draws a drop shadow for the given rectangle into the given context. It will not draw anything
+ * if the rectangle is smaller than a minimum determined by the assets used to draw the shadow
+ * graphics.
+ *
+ * @param gc the graphics context to draw into
+ * @param x the left coordinate of the left hand side of the rectangle
+ * @param y the top coordinate of the top of the rectangle
+ * @param width the width of the rectangle
+ * @param height the height of the rectangle
+ */
+ public static void drawRectangleShadow(Graphics2D gc, int x, int y, int width, int height) {
+ assert ShadowBottomLeft != null;
+ assert ShadowBottomRight.getWidth(null) == SHADOW_SIZE;
+ assert ShadowBottomRight.getHeight(null) == SHADOW_SIZE;
+
+ int blWidth = ShadowBottomLeft.getWidth(null);
+ int trHeight = ShadowTopRight.getHeight(null);
+ if (width < blWidth) {
+ return;
+ }
+ if (height < trHeight) {
+ return;
+ }
+
+ gc.drawImage(ShadowBottomLeft, x - ShadowBottomLeft.getWidth(null), y + height, null);
+ gc.drawImage(ShadowBottomRight, x + width, y + height, null);
+ gc.drawImage(ShadowTopRight, x + width, y, null);
+ gc.drawImage(ShadowTopLeft, x - ShadowTopLeft.getWidth(null), y, null);
+ gc.drawImage(ShadowBottom,
+ x, y + height, x + width, y + height + ShadowBottom.getHeight(null),
+ 0, 0, ShadowBottom.getWidth(null), ShadowBottom.getHeight(null), null);
+ gc.drawImage(ShadowRight,
+ x + width, y + ShadowTopRight.getHeight(null), x + width + ShadowRight.getWidth(null), y + height,
+ 0, 0, ShadowRight.getWidth(null), ShadowRight.getHeight(null), null);
+ gc.drawImage(ShadowLeft,
+ x - ShadowLeft.getWidth(null), y + ShadowTopLeft.getHeight(null), x, y + height,
+ 0, 0, ShadowLeft.getWidth(null), ShadowLeft.getHeight(null), null);
+ }
+
+ /**
+ * Draws a small drop shadow for the given rectangle into the given context. It will not draw
+ * anything if the rectangle is smaller than a minimum determined by the assets used to draw the
+ * shadow graphics.
+ * <p/>
+ *
+ * @param gc the graphics context to draw into
+ * @param x the left coordinate of the left hand side of the rectangle
+ * @param y the top coordinate of the top of the rectangle
+ * @param width the width of the rectangle
+ * @param height the height of the rectangle
+ */
+ public static void drawSmallRectangleShadow(Graphics2D gc, int x, int y, int width,
+ int height) {
+ assert Shadow2BottomLeft != null;
+ assert Shadow2TopRight != null;
+ assert Shadow2BottomRight.getWidth(null) == SMALL_SHADOW_SIZE;
+ assert Shadow2BottomRight.getHeight(null) == SMALL_SHADOW_SIZE;
+
+ int blWidth = Shadow2BottomLeft.getWidth(null);
+ int trHeight = Shadow2TopRight.getHeight(null);
+ if (width < blWidth) {
+ return;
+ }
+ if (height < trHeight) {
+ return;
+ }
+
+ gc.drawImage(Shadow2BottomLeft, x - Shadow2BottomLeft.getWidth(null), y + height, null);
+ gc.drawImage(Shadow2BottomRight, x + width, y + height, null);
+ gc.drawImage(Shadow2TopRight, x + width, y, null);
+ gc.drawImage(Shadow2TopLeft, x - Shadow2TopLeft.getWidth(null), y, null);
+ gc.drawImage(Shadow2Bottom,
+ x, y + height, x + width, y + height + Shadow2Bottom.getHeight(null),
+ 0, 0, Shadow2Bottom.getWidth(null), Shadow2Bottom.getHeight(null), null);
+ gc.drawImage(Shadow2Right,
+ x + width, y + Shadow2TopRight.getHeight(null), x + width + Shadow2Right.getWidth(null), y + height,
+ 0, 0, Shadow2Right.getWidth(null), Shadow2Right.getHeight(null), null);
+ gc.drawImage(Shadow2Left,
+ x - Shadow2Left.getWidth(null), y + Shadow2TopLeft.getHeight(null), x, y + height,
+ 0, 0, Shadow2Left.getWidth(null), Shadow2Left.getHeight(null), null);
+ }
+
+ private static Image loadIcon(String name) {
+ InputStream inputStream = ShadowPainter.class.getResourceAsStream(name);
+ if (inputStream == null) {
+ throw new RuntimeException("Unable to load image for shadow: " + name);
+ }
+ try {
+ return ImageIO.read(inputStream);
+ } catch (IOException e) {
+ throw new RuntimeException("Unable to load image for shadow:" + name, e);
+ } finally {
+ try {
+ inputStream.close();
+ } catch (IOException e) {
+ // ignore.
+ }
+ }
+ }
+
+ // Shadow graphics. This was generated by creating a drop shadow in
+ // Gimp, using the parameters x offset=10, y offset=10, blur radius=10,
+ // (for the small drop shadows x offset=10, y offset=10, blur radius=10)
+ // color=black, and opacity=51. These values attempt to make a shadow
+ // that is legible both for dark and light themes, on top of the
+ // canvas background (rgb(150,150,150). Darker shadows would tend to
+ // blend into the foreground for a dark holo screen, and lighter shadows
+ // would be hard to spot on the canvas background. If you make adjustments,
+ // make sure to check the shadow with both dark and light themes.
+ //
+ // After making the graphics, I cut out the top right, bottom left
+ // and bottom right corners as 20x20 images, and these are reproduced by
+ // painting them in the corresponding places in the target graphics context.
+ // I then grabbed a single horizontal gradient line from the middle of the
+ // right edge,and a single vertical gradient line from the bottom. These
+ // are then painted scaled/stretched in the target to fill the gaps between
+ // the three corner images.
+ //
+ // Filenames: bl=bottom left, b=bottom, br=bottom right, r=right, tr=top right
+
+ // Normal Drop Shadow
+ private static final Image ShadowBottom = loadIcon("/icons/shadow-b.png");
+ private static final Image ShadowBottomLeft = loadIcon("/icons/shadow-bl.png");
+ private static final Image ShadowBottomRight = loadIcon("/icons/shadow-br.png");
+ private static final Image ShadowRight = loadIcon("/icons/shadow-r.png");
+ private static final Image ShadowTopRight = loadIcon("/icons/shadow-tr.png");
+ private static final Image ShadowTopLeft = loadIcon("/icons/shadow-tl.png");
+ private static final Image ShadowLeft = loadIcon("/icons/shadow-l.png");
+
+ // Small Drop Shadow
+ private static final Image Shadow2Bottom = loadIcon("/icons/shadow2-b.png");
+ private static final Image Shadow2BottomLeft = loadIcon("/icons/shadow2-bl.png");
+ private static final Image Shadow2BottomRight = loadIcon("/icons/shadow2-br.png");
+ private static final Image Shadow2Right = loadIcon("/icons/shadow2-r.png");
+ private static final Image Shadow2TopRight = loadIcon("/icons/shadow2-tr.png");
+ private static final Image Shadow2TopLeft = loadIcon("/icons/shadow2-tl.png");
+ private static final Image Shadow2Left = loadIcon("/icons/shadow2-l.png");
+}
diff --git a/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java b/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java
new file mode 100644
index 0000000..a6c00f7
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java
@@ -0,0 +1,205 @@
+/*
+ * 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.view;
+
+import com.android.annotations.NonNull;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.resources.Density;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap_Delegate;
+import android.graphics.Canvas;
+import android.graphics.Outline;
+import android.graphics.Path_Delegate;
+import android.graphics.Rect;
+import android.graphics.Region.Op;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.animation.Transformation;
+
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+
+/**
+ * Delegate used to provide new implementation of a select few methods of {@link ViewGroup}
+ * <p/>
+ * Through the layoutlib_create tool, the original methods of ViewGroup have been replaced by calls
+ * to methods of the same name in this delegate class.
+ */
+public class ViewGroup_Delegate {
+
+ /**
+ * Overrides the original drawChild call in ViewGroup to draw the shadow.
+ */
+ @LayoutlibDelegate
+ /*package*/ static boolean drawChild(ViewGroup thisVG, Canvas canvas, View child,
+ long drawingTime) {
+ boolean retVal = thisVG.drawChild_Original(canvas, child, drawingTime);
+ if (child.getZ() > thisVG.getZ()) {
+ ViewOutlineProvider outlineProvider = child.getOutlineProvider();
+ Outline outline = new Outline();
+ outlineProvider.getOutline(child, outline);
+
+ if (outline.mPath != null || (outline.mRect != null && !outline.mRect.isEmpty())) {
+ int restoreTo = transformCanvas(thisVG, canvas, child);
+ drawShadow(thisVG, canvas, child, outline);
+ canvas.restoreToCount(restoreTo);
+ }
+ }
+ return retVal;
+ }
+
+ private static void drawShadow(ViewGroup parent, Canvas canvas, View child,
+ Outline outline) {
+ BufferedImage shadow = null;
+ int x = 0;
+ if (outline.mRect != null) {
+ Shadow s = getRectShadow(parent, canvas, child, outline);
+ shadow = s.mShadow;
+ x = -s.mShadowWidth;
+ } else if (outline.mPath != null) {
+ shadow = getPathShadow(child, outline, canvas);
+ }
+ if (shadow == null) {
+ return;
+ }
+ Bitmap bitmap = Bitmap_Delegate.createBitmap(shadow, false,
+ Density.getEnum(canvas.getDensity()));
+ Rect clipBounds = canvas.getClipBounds();
+ Rect newBounds = new Rect(clipBounds);
+ newBounds.left = newBounds.left + x;
+ canvas.clipRect(newBounds, Op.REPLACE);
+ canvas.drawBitmap(bitmap, x, 0, null);
+ canvas.clipRect(clipBounds, Op.REPLACE);
+ }
+
+ private static Shadow getRectShadow(ViewGroup parent, Canvas canvas, View child,
+ Outline outline) {
+ BufferedImage shadow;
+ Rect clipBounds = canvas.getClipBounds();
+ if (clipBounds.isEmpty()) {
+ return null;
+ }
+ float height = child.getZ() - parent.getZ();
+ // Draw large shadow if difference in z index is more than 10dp
+ float largeShadowThreshold = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10f,
+ getMetrics(child));
+ boolean largeShadow = height > largeShadowThreshold;
+ int shadowSize = largeShadow ? ShadowPainter.SHADOW_SIZE : ShadowPainter.SMALL_SHADOW_SIZE;
+ shadow = new BufferedImage(clipBounds.width() + shadowSize, clipBounds.height(),
+ BufferedImage.TYPE_INT_ARGB);
+ Graphics2D graphics = shadow.createGraphics();
+ Rect rect = outline.mRect;
+ if (largeShadow) {
+ ShadowPainter.drawRectangleShadow(graphics,
+ rect.left + shadowSize, rect.top, rect.width(), rect.height());
+ } else {
+ ShadowPainter.drawSmallRectangleShadow(graphics,
+ rect.left + shadowSize, rect.top, rect.width(), rect.height());
+ }
+ graphics.dispose();
+ return new Shadow(shadow, shadowSize);
+ }
+
+ @NonNull
+ private static DisplayMetrics getMetrics(View view) {
+ Context context = view.getContext();
+ while (context instanceof ContextThemeWrapper) {
+ context = ((ContextThemeWrapper) context).getBaseContext();
+ }
+ if (context instanceof BridgeContext) {
+ return ((BridgeContext) context).getMetrics();
+ }
+ throw new RuntimeException("View " + view.getClass().getName() + " not created with the " +
+ "right context");
+ }
+
+ private static BufferedImage getPathShadow(View child, Outline outline, Canvas canvas) {
+ Rect clipBounds = canvas.getClipBounds();
+ BufferedImage image = new BufferedImage(clipBounds.width(), clipBounds.height(),
+ BufferedImage.TYPE_INT_ARGB);
+ Graphics2D graphics = image.createGraphics();
+ graphics.draw(Path_Delegate.getDelegate(outline.mPath.mNativePath).getJavaShape());
+ graphics.dispose();
+ return ShadowPainter.createDropShadow(image, ((int) child.getZ()));
+ }
+
+ // Copied from android.view.View#draw(Canvas, ViewGroup, long) and removed code paths
+ // which were never taken. Ideally, we should hook up the shadow code in the same method so
+ // that we don't have to transform the canvas twice.
+ private static int transformCanvas(ViewGroup thisVG, Canvas canvas, View child) {
+ final int restoreTo = canvas.save();
+ final boolean childHasIdentityMatrix = child.hasIdentityMatrix();
+ int flags = thisVG.mGroupFlags;
+ Transformation transformToApply = null;
+ boolean concatMatrix = false;
+ if ((flags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
+ final Transformation t = thisVG.getChildTransformation();
+ final boolean hasTransform = thisVG.getChildStaticTransformation(child, t);
+ if (hasTransform) {
+ final int transformType = t.getTransformationType();
+ transformToApply = transformType != Transformation.TYPE_IDENTITY ? t : null;
+ concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
+ }
+ }
+ concatMatrix |= childHasIdentityMatrix;
+
+ child.computeScroll();
+ int sx = child.mScrollX;
+ int sy = child.mScrollY;
+
+ canvas.translate(child.mLeft - sx, child.mTop - sy);
+ float alpha = child.getAlpha() * child.getTransitionAlpha();
+
+ if (transformToApply != null || alpha < 1 || !childHasIdentityMatrix) {
+ if (transformToApply != null || !childHasIdentityMatrix) {
+ int transX = -sx;
+ int transY = -sy;
+
+ if (transformToApply != null) {
+ if (concatMatrix) {
+ // Undo the scroll translation, apply the transformation matrix,
+ // then redo the scroll translate to get the correct result.
+ canvas.translate(-transX, -transY);
+ canvas.concat(transformToApply.getMatrix());
+ canvas.translate(transX, transY);
+ }
+ if (!childHasIdentityMatrix) {
+ canvas.translate(-transX, -transY);
+ canvas.concat(child.getMatrix());
+ canvas.translate(transX, transY);
+ }
+ }
+
+ }
+ }
+ return restoreTo;
+ }
+
+ private static class Shadow {
+ public BufferedImage mShadow;
+ public int mShadowWidth;
+
+ public Shadow(BufferedImage shadow, int shadowWidth) {
+ mShadow = shadow;
+ mShadowWidth = shadowWidth;
+ }
+
+ }
+}
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 3d3afa4..aeb70e9 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
@@ -479,6 +479,23 @@
StyleResourceValue style = getStyleByDynamicId(resid);
if (style == null) {
+ // In some cases, style may not be a dynamic id, so we do a full search.
+ ResourceReference ref = resolveId(resid);
+ if (ref != null) {
+ if (ref.isFramework()) {
+ ref =
+ getRenderResources().getFrameworkResource(ResourceType.STYLE, ref.getName());
+ } else {
+ ref =
+ getRenderResources().getProjectResource(ResourceType.STYLE, ref.getName());
+ }
+ if (ref instanceof StyleResourceValue) {
+ style = ((StyleResourceValue) ref);
+ }
+ }
+ }
+
+ if (style == null) {
throw new Resources.NotFoundException();
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
index 57fd68e..2ff8d37 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
@@ -18,6 +18,7 @@
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
+import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.SessionParams;
import com.android.internal.R;
@@ -37,7 +38,6 @@
import android.view.ViewGroup.LayoutParams;
import android.widget.ActionMenuPresenter;
import android.widget.FrameLayout;
-import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;
@@ -49,15 +49,23 @@
private static final String LAYOUT_ATTR_NAME = "windowActionBarFullscreenDecorLayout";
// The Action Bar
- @NonNull private CustomActionBarWrapper mActionBar;
+ @NonNull
+ private CustomActionBarWrapper mActionBar;
// Store another reference to the context so that we don't have to cast it repeatedly.
- @NonNull private final BridgeContext mBridgeContext;
+ @NonNull
+ private final BridgeContext mBridgeContext;
- @NonNull private FrameLayout mContentRoot;
+ @NonNull
+ private FrameLayout mContentRoot;
// A fake parent for measuring views.
- @Nullable private ViewGroup mMeasureParent;
+ @Nullable
+ private ViewGroup mMeasureParent;
+
+ // A Layout that contains the inflated action bar. The menu popup is added to this layout.
+ @NonNull
+ private final RelativeLayout mEnclosingLayout;
/**
* Inflate the action bar and attach it to {@code parentView}
@@ -90,20 +98,25 @@
if (layoutId == 0) {
throw new RuntimeException(error);
}
+ // Create a RelativeLayout to hold the action bar. The layout is needed so that we may
+ // add the menu popup to it.
+ mEnclosingLayout = new RelativeLayout(mBridgeContext);
+ setMatchParent(mEnclosingLayout);
+ parentView.addView(mEnclosingLayout);
+
// Inflate action bar layout.
- View decorContent = LayoutInflater.from(context).inflate(layoutId, parentView, true);
+ View decorContent = LayoutInflater.from(context).inflate(layoutId, mEnclosingLayout, true);
mActionBar = CustomActionBarWrapper.getActionBarWrapper(context, params, decorContent);
- FrameLayout contentRoot = (FrameLayout) parentView.findViewById(android.R.id.content);
+ FrameLayout contentRoot = (FrameLayout) mEnclosingLayout.findViewById(android.R.id.content);
// If something went wrong and we were not able to initialize the content root,
// just add a frame layout inside this and return.
if (contentRoot == null) {
contentRoot = new FrameLayout(context);
- contentRoot.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT));
- parentView.addView(contentRoot);
+ setMatchParent(contentRoot);
+ mEnclosingLayout.addView(contentRoot);
mContentRoot = contentRoot;
} else {
mContentRoot = contentRoot;
@@ -112,70 +125,49 @@
}
}
+ private void setMatchParent(View view) {
+ view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT));
+ }
+
/**
* Creates a Popup and adds it to the content frame. It also adds another {@link FrameLayout} to
* the content frame which shall serve as the new content root.
*/
public void createMenuPopup() {
- assert mContentRoot.getId() == android.R.id.content
+ assert mEnclosingLayout.getChildCount() == 1
: "Action Bar Menus have already been created.";
if (!isOverflowPopupNeeded()) {
return;
}
- // Create a layout to hold the menus and the user's content.
- RelativeLayout layout = new RelativeLayout(mActionBar.getPopupContext());
- layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
- LayoutParams.MATCH_PARENT));
- mContentRoot.addView(layout);
- // Create a layout for the user's content.
- FrameLayout contentRoot = new FrameLayout(mBridgeContext);
- contentRoot.setLayoutParams(new LayoutParams(
- LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
- // Add contentRoot and menus to the layout.
- layout.addView(contentRoot);
- layout.addView(createMenuView());
- // ContentRoot is now the view we just created.
- mContentRoot = contentRoot;
- }
-
- /**
- * Returns a {@link LinearLayout} containing the menu list view to be embedded in a
- * {@link RelativeLayout}
- */
- @NonNull
- private View createMenuView() {
DisplayMetrics metrics = mBridgeContext.getMetrics();
MenuBuilder menu = mActionBar.getMenuBuilder();
OverflowMenuAdapter adapter = new OverflowMenuAdapter(menu, mActionBar.getPopupContext());
- LinearLayout layout = new LinearLayout(mActionBar.getPopupContext());
+ ListView listView = new ListView(mActionBar.getPopupContext(), null,
+ R.attr.dropDownListViewStyle);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
measureContentWidth(adapter), LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_END);
if (mActionBar.isSplit()) {
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
- // TODO: Find correct value instead of hardcoded 10dp.
- layoutParams.bottomMargin = getPixelValue("-10dp", metrics);
+ layoutParams.bottomMargin = getActionBarHeight() + mActionBar.getMenuPopupMargin();
} else {
- layoutParams.topMargin = getPixelValue("-10dp", metrics);
+ layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
+ layoutParams.topMargin = getActionBarHeight() + mActionBar.getMenuPopupMargin();
}
- layout.setLayoutParams(layoutParams);
+ layoutParams.setMarginEnd(getPixelValue("5dp", metrics));
+ listView.setLayoutParams(layoutParams);
+ listView.setAdapter(adapter);
final TypedArray a = mActionBar.getPopupContext().obtainStyledAttributes(null,
R.styleable.PopupWindow, R.attr.popupMenuStyle, 0);
- layout.setBackground(a.getDrawable(R.styleable.PopupWindow_popupBackground));
- layout.setDividerDrawable(a.getDrawable(R.attr.actionBarDivider));
+ listView.setBackground(a.getDrawable(R.styleable.PopupWindow_popupBackground));
+ listView.setDivider(a.getDrawable(R.attr.actionBarDivider));
a.recycle();
- layout.setOrientation(LinearLayout.VERTICAL);
- layout.setDividerPadding(getPixelValue("12dp", metrics));
- layout.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE);
-
- ListView listView = new ListView(mActionBar.getPopupContext(), null,
- R.attr.dropDownListViewStyle);
- listView.setAdapter(adapter);
- layout.addView(listView);
- return layout;
+ listView.setElevation(mActionBar.getMenuPopupElevation());
+ mEnclosingLayout.addView(listView);
}
private boolean isOverflowPopupNeeded() {
@@ -244,9 +236,30 @@
return maxWidth;
}
- private int getPixelValue(@NonNull String value, @NonNull DisplayMetrics metrics) {
+ static int getPixelValue(@NonNull String value, @NonNull DisplayMetrics metrics) {
TypedValue typedValue = ResourceHelper.getValue(null, value, false /*requireUnit*/);
return (int) typedValue.getDimension(metrics);
}
+ // TODO: This is duplicated from RenderSessionImpl.
+ private int getActionBarHeight() {
+ RenderResources resources = mBridgeContext.getRenderResources();
+ DisplayMetrics metrics = mBridgeContext.getMetrics();
+ ResourceValue value = resources.findItemInTheme("actionBarSize", true);
+
+ // resolve it
+ value = resources.resolveResValue(value);
+
+ if (value != null) {
+ // get the numerical value, if available
+ TypedValue typedValue = ResourceHelper.getValue("actionBarSize", value.getValue(),
+ true);
+ if (typedValue != null) {
+ // compute the pixel value based on the display metrics
+ return (int) typedValue.getDimension(metrics);
+
+ }
+ }
+ return 0;
+ }
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomActionBarWrapper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomActionBarWrapper.java
index 70b9cc3..6db722e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomActionBarWrapper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomActionBarWrapper.java
@@ -65,18 +65,17 @@
* Returns a wrapper around different implementations of the Action Bar to provide a common API.
*
* @param decorContent the top level view returned by inflating
- * ?attr/windowActionBarFullscreenDecorLayout
+ * ?attr/windowActionBarFullscreenDecorLayout
*/
@NonNull
public static CustomActionBarWrapper getActionBarWrapper(@NonNull BridgeContext context,
@NonNull SessionParams params, @NonNull View decorContent) {
View view = decorContent.findViewById(R.id.action_bar);
if (view instanceof Toolbar) {
- return new ToolbarWrapper(context, params, ((Toolbar) view)
- );
+ return new ToolbarWrapper(context, params, ((Toolbar) view));
} else if (view instanceof ActionBarView) {
- return new WindowActionBarWrapper(context, params, decorContent, ((ActionBarView) view)
- );
+ return new WindowActionBarWrapper(context, params, decorContent,
+ ((ActionBarView) view));
} else {
throw new IllegalStateException("Can't make an action bar out of " +
view.getClass().getSimpleName());
@@ -174,6 +173,13 @@
@NonNull
abstract DecorToolbar getDecorToolbar();
+ abstract int getMenuPopupElevation();
+
+ /**
+ * Margin between the menu popup and the action bar.
+ */
+ abstract int getMenuPopupMargin();
+
// ---- The implementations ----
/**
@@ -226,25 +232,38 @@
DecorToolbar getDecorToolbar() {
return mToolbar.getWrapper();
}
+
+ @Override
+ int getMenuPopupElevation() {
+ return 10;
+ }
+
+ @Override
+ int getMenuPopupMargin() {
+ return 0;
+ }
}
/**
* Holo theme uses {@link WindowDecorActionBar} as the action bar. This wrapper provides
* access to it using a common API.
*/
- private static class WindowActionBarWrapper extends CustomActionBarWrapper{
+ private static class WindowActionBarWrapper extends CustomActionBarWrapper {
@NonNull
private final WindowDecorActionBar mActionBar;
+ @NonNull
private final ActionBarView mActionBarView;
+ @NonNull
+ private final View mDecorContentRoot;
private MenuBuilder mMenuBuilder;
public WindowActionBarWrapper(@NonNull BridgeContext context, @NonNull SessionParams params,
@NonNull View decorContentRoot, @NonNull ActionBarView actionBarView) {
- super(context, params, new WindowDecorActionBar(decorContentRoot)
- );
+ super(context, params, new WindowDecorActionBar(decorContentRoot));
mActionBarView = actionBarView;
mActionBar = ((WindowDecorActionBar) super.mActionBar);
+ mDecorContentRoot = decorContentRoot;
}
@Override
@@ -270,7 +289,7 @@
}
// Set action bar to be split, if needed.
- ViewGroup splitView = (ViewGroup) mActionBarView.findViewById(R.id.split_action_bar);
+ ViewGroup splitView = (ViewGroup) mDecorContentRoot.findViewById(R.id.split_action_bar);
if (splitView != null) {
mActionBarView.setSplitView(splitView);
Resources res = mContext.getResources();
@@ -314,6 +333,16 @@
return mActionBarView;
}
+ @Override
+ int getMenuPopupElevation() {
+ return 0;
+ }
+
+ @Override
+ int getMenuPopupMargin() {
+ return -ActionBarLayout.getPixelValue("10dp", mContext.getMetrics());
+ }
+
// TODO: Use an adapter, like List View to set up tabs.
@SuppressWarnings("deprecation") // For Tab
private void setupTabs(int num) {
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 b677131..669e6b5 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
@@ -57,63 +57,59 @@
public Result render() {
checkLock();
- try {
- // get the drawable resource value
- DrawableParams params = getParams();
- HardwareConfig hardwareConfig = params.getHardwareConfig();
- ResourceValue drawableResource = params.getDrawable();
+ // get the drawable resource value
+ DrawableParams params = getParams();
+ HardwareConfig hardwareConfig = params.getHardwareConfig();
+ ResourceValue drawableResource = params.getDrawable();
- // resolve it
- BridgeContext context = getContext();
- drawableResource = context.getRenderResources().resolveResValue(drawableResource);
+ // resolve it
+ BridgeContext context = getContext();
+ drawableResource = context.getRenderResources().resolveResValue(drawableResource);
- if (drawableResource == null ||
- drawableResource.getResourceType() != ResourceType.DRAWABLE) {
- return Status.ERROR_NOT_A_DRAWABLE.createResult();
- }
-
- // create a simple FrameLayout
- FrameLayout content = new FrameLayout(context);
-
- // get the actual Drawable object to draw
- Drawable d = ResourceHelper.getDrawable(drawableResource, context);
- content.setBackground(d);
-
- // set the AttachInfo on the root view.
- AttachInfo_Accessor.setAttachInfo(content);
-
-
- // measure
- int w = hardwareConfig.getScreenWidth();
- int h = hardwareConfig.getScreenHeight();
- int w_spec = MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY);
- int h_spec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY);
- content.measure(w_spec, h_spec);
-
- // now do the layout.
- content.layout(0, 0, w, h);
-
- // preDraw setup
- AttachInfo_Accessor.dispatchOnPreDraw(content);
-
- // draw into a new image
- BufferedImage image = getImage(w, h);
-
- // create an Android bitmap around the BufferedImage
- Bitmap bitmap = Bitmap_Delegate.createBitmap(image,
- true /*isMutable*/, hardwareConfig.getDensity());
-
- // create a Canvas around the Android bitmap
- Canvas canvas = new Canvas(bitmap);
- canvas.setDensity(hardwareConfig.getDensity().getDpiValue());
-
- // and draw
- content.draw(canvas);
-
- return Status.SUCCESS.createResult(image);
- } catch (IOException e) {
- return ERROR_UNKNOWN.createResult(e.getMessage(), e);
+ if (drawableResource == null ||
+ drawableResource.getResourceType() != ResourceType.DRAWABLE) {
+ return Status.ERROR_NOT_A_DRAWABLE.createResult();
}
+
+ // create a simple FrameLayout
+ FrameLayout content = new FrameLayout(context);
+
+ // get the actual Drawable object to draw
+ Drawable d = ResourceHelper.getDrawable(drawableResource, context);
+ content.setBackground(d);
+
+ // set the AttachInfo on the root view.
+ AttachInfo_Accessor.setAttachInfo(content);
+
+
+ // measure
+ int w = hardwareConfig.getScreenWidth();
+ int h = hardwareConfig.getScreenHeight();
+ int w_spec = MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY);
+ int h_spec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY);
+ content.measure(w_spec, h_spec);
+
+ // now do the layout.
+ content.layout(0, 0, w, h);
+
+ // preDraw setup
+ AttachInfo_Accessor.dispatchOnPreDraw(content);
+
+ // draw into a new image
+ BufferedImage image = getImage(w, h);
+
+ // create an Android bitmap around the BufferedImage
+ Bitmap bitmap = Bitmap_Delegate.createBitmap(image,
+ true /*isMutable*/, hardwareConfig.getDensity());
+
+ // create a Canvas around the Android bitmap
+ Canvas canvas = new Canvas(bitmap);
+ canvas.setDensity(hardwareConfig.getDensity().getDpiValue());
+
+ // and draw
+ content.draw(canvas);
+
+ return Status.SUCCESS.createResult(image);
}
protected BufferedImage getImage(int w, int h) {
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 2fcdf34..4e6f456 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -162,6 +162,11 @@
"android.view.WindowManagerGlobal#getWindowManagerService",
"android.view.inputmethod.InputMethodManager#getInstance",
"android.view.MenuInflater#registerMenu",
+ "android.view.RenderNode#nCreate",
+ "android.view.RenderNode#nDestroyRenderNode",
+ "android.view.RenderNode#nSetElevation",
+ "android.view.RenderNode#nGetElevation",
+ "android.view.ViewGroup#drawChild",
"com.android.internal.view.menu.MenuBuilder#createNewMenuItem",
"com.android.internal.util.XmlUtils#convertValueToInt",
"com.android.internal.textservice.ITextServicesManager$Stub#asInterface",
diff --git a/tools/split-select/Android.mk b/tools/split-select/Android.mk
index dc48ea8..968d22b 100644
--- a/tools/split-select/Android.mk
+++ b/tools/split-select/Android.mk
@@ -34,7 +34,8 @@
testSources := \
Grouper_test.cpp \
Rule_test.cpp \
- RuleGenerator_test.cpp
+ RuleGenerator_test.cpp \
+ TestRules.cpp
cIncludes := \
external/zlib \
diff --git a/tools/split-select/Rule.cpp b/tools/split-select/Rule.cpp
index 9559fe2..48d21ff 100644
--- a/tools/split-select/Rule.cpp
+++ b/tools/split-select/Rule.cpp
@@ -29,6 +29,16 @@
}
}
+Rule::Rule(const Rule& rhs)
+ : RefBase()
+ , op(rhs.op)
+ , key(rhs.key)
+ , negate(rhs.negate)
+ , stringArgs(rhs.stringArgs)
+ , longArgs(rhs.longArgs)
+ , subrules(rhs.subrules) {
+}
+
String8 Rule::toJson(int indent) const {
String8 str;
indentStr(str, indent);
diff --git a/tools/split-select/Rule.h b/tools/split-select/Rule.h
index 8029931..08a2075 100644
--- a/tools/split-select/Rule.h
+++ b/tools/split-select/Rule.h
@@ -28,6 +28,7 @@
struct Rule : public virtual android::RefBase {
inline Rule();
+ Rule(const Rule& rhs);
enum Operator {
LESS_THAN = 1,
diff --git a/tools/split-select/RuleGenerator.cpp b/tools/split-select/RuleGenerator.cpp
index b8f3bcb..72bb0c7 100644
--- a/tools/split-select/RuleGenerator.cpp
+++ b/tools/split-select/RuleGenerator.cpp
@@ -15,6 +15,7 @@
*/
#include "RuleGenerator.h"
+#include "aapt/SdkConstants.h"
#include <algorithm>
#include <cmath>
@@ -32,18 +33,21 @@
}
sp<Rule> RuleGenerator::generateDensity(const Vector<int>& allDensities, size_t index) {
- sp<Rule> densityRule = new Rule();
- densityRule->op = Rule::AND_SUBRULES;
+ if (allDensities[index] != ResTable_config::DENSITY_ANY) {
+ sp<Rule> densityRule = new Rule();
+ densityRule->op = Rule::AND_SUBRULES;
- const bool anyDensity = allDensities[index] == ResTable_config::DENSITY_ANY;
- sp<Rule> any = new Rule();
- any->op = Rule::EQUALS;
- any->key = Rule::SCREEN_DENSITY;
- any->longArgs.add((int)ResTable_config::DENSITY_ANY);
- any->negate = !anyDensity;
- densityRule->subrules.add(any);
+ const bool hasAnyDensity = std::find(allDensities.begin(),
+ allDensities.end(), ResTable_config::DENSITY_ANY) != allDensities.end();
- if (!anyDensity) {
+ if (hasAnyDensity) {
+ sp<Rule> version = new Rule();
+ version->op = Rule::LESS_THAN;
+ version->key = Rule::SDK_VERSION;
+ version->longArgs.add((long) SDK_LOLLIPOP);
+ densityRule->subrules.add(version);
+ }
+
if (index > 0) {
sp<Rule> gt = new Rule();
gt->op = Rule::GREATER_THAN;
@@ -59,8 +63,14 @@
lt->longArgs.add(findMid(allDensities[index], allDensities[index + 1]));
densityRule->subrules.add(lt);
}
+ return densityRule;
+ } else {
+ // SDK_VERSION is handled elsewhere, so we always pick DENSITY_ANY if it's
+ // available.
+ sp<Rule> always = new Rule();
+ always->op = Rule::ALWAYS_TRUE;
+ return always;
}
- return densityRule;
}
sp<Rule> RuleGenerator::generateAbi(const Vector<abi::Variant>& splitAbis, size_t index) {
diff --git a/tools/split-select/RuleGenerator_test.cpp b/tools/split-select/RuleGenerator_test.cpp
index ee387be..778d604 100644
--- a/tools/split-select/RuleGenerator_test.cpp
+++ b/tools/split-select/RuleGenerator_test.cpp
@@ -16,154 +16,95 @@
#include "RuleGenerator.h"
-#include <algorithm>
+#include "aapt/SdkConstants.h"
+#include "TestRules.h"
+
#include <gtest/gtest.h>
-#include <utils/String8.h>
+#include <utils/Vector.h>
using namespace android;
+using namespace split::test;
namespace split {
-static void expectDensityRule(const Vector<int>& densities, int density, int greaterThan, int lessThan);
-static void expectAbiRule(const Vector<abi::Variant>& abis, abi::Variant variant, const char* a);
-static void expectAbiRule(const Vector<abi::Variant>& abis, abi::Variant variant, const char* a, const char* b);
-
TEST(RuleGeneratorTest, testAbiRules) {
Vector<abi::Variant> abis;
- abis.add(abi::Variant_armeabi);
- abis.add(abi::Variant_armeabi_v7a);
- abis.add(abi::Variant_x86);
- std::sort(abis.begin(), abis.end());
+ const ssize_t armeabiIndex = abis.add(abi::Variant_armeabi);
+ const ssize_t armeabi_v7aIndex = abis.add(abi::Variant_armeabi_v7a);
+ const ssize_t x86Index = abis.add(abi::Variant_x86);
- expectAbiRule(abis, abi::Variant_armeabi, "armeabi");
- expectAbiRule(abis, abi::Variant_armeabi_v7a, "armeabi-v7a", "arm64-v8a");
- expectAbiRule(abis, abi::Variant_x86, "x86", "x86_64");
+ EXPECT_RULES_EQ(RuleGenerator::generateAbi(abis, armeabiIndex),
+ ContainsAnyRule(Rule::NATIVE_PLATFORM, "armeabi")
+ );
+
+ EXPECT_RULES_EQ(RuleGenerator::generateAbi(abis, armeabi_v7aIndex),
+ ContainsAnyRule(Rule::NATIVE_PLATFORM, "armeabi-v7a", "arm64-v8a")
+ );
+
+ EXPECT_RULES_EQ(RuleGenerator::generateAbi(abis, x86Index),
+ ContainsAnyRule(Rule::NATIVE_PLATFORM, "x86", "x86_64")
+ );
+}
+
+TEST(RuleGeneratorTest, densityConstantsAreSane) {
+ EXPECT_LT(263, ConfigDescription::DENSITY_XHIGH);
+ EXPECT_GT(262, ConfigDescription::DENSITY_HIGH);
+ EXPECT_LT(363, ConfigDescription::DENSITY_XXHIGH);
+ EXPECT_GT(362, ConfigDescription::DENSITY_XHIGH);
}
TEST(RuleGeneratorTest, testDensityRules) {
Vector<int> densities;
- densities.add(ConfigDescription::DENSITY_HIGH);
- densities.add(ConfigDescription::DENSITY_XHIGH);
- densities.add(ConfigDescription::DENSITY_XXHIGH);
- densities.add(ConfigDescription::DENSITY_ANY);
+ const ssize_t highIndex = densities.add(ConfigDescription::DENSITY_HIGH);
+ const ssize_t xhighIndex = densities.add(ConfigDescription::DENSITY_XHIGH);
+ const ssize_t xxhighIndex = densities.add(ConfigDescription::DENSITY_XXHIGH);
- ASSERT_LT(263, ConfigDescription::DENSITY_XHIGH);
- ASSERT_GT(262, ConfigDescription::DENSITY_HIGH);
- ASSERT_LT(363, ConfigDescription::DENSITY_XXHIGH);
- ASSERT_GT(362, ConfigDescription::DENSITY_XHIGH);
+ EXPECT_RULES_EQ(RuleGenerator::generateDensity(densities, highIndex),
+ AndRule()
+ .add(LtRule(Rule::SCREEN_DENSITY, 263))
+ );
- expectDensityRule(densities, ConfigDescription::DENSITY_HIGH, 0, 263);
- expectDensityRule(densities, ConfigDescription::DENSITY_XHIGH, 262, 363);
- expectDensityRule(densities, ConfigDescription::DENSITY_XXHIGH, 362, 0);
- expectDensityRule(densities, ConfigDescription::DENSITY_ANY, 0, 0);
+ EXPECT_RULES_EQ(RuleGenerator::generateDensity(densities, xhighIndex),
+ AndRule()
+ .add(GtRule(Rule::SCREEN_DENSITY, 262))
+ .add(LtRule(Rule::SCREEN_DENSITY, 363))
+ );
+
+ EXPECT_RULES_EQ(RuleGenerator::generateDensity(densities, xxhighIndex),
+ AndRule()
+ .add(GtRule(Rule::SCREEN_DENSITY, 362))
+ );
}
-//
-// Helper methods.
-//
+TEST(RuleGeneratorTest, testDensityRulesWithAnyDpi) {
+ Vector<int> densities;
+ const ssize_t highIndex = densities.add(ConfigDescription::DENSITY_HIGH);
+ const ssize_t xhighIndex = densities.add(ConfigDescription::DENSITY_XHIGH);
+ const ssize_t xxhighIndex = densities.add(ConfigDescription::DENSITY_XXHIGH);
+ const ssize_t anyIndex = densities.add(ConfigDescription::DENSITY_ANY);
-static void expectDensityRule(const Vector<int>& densities, int density, int greaterThan, int lessThan) {
- const int* iter = std::find(densities.begin(), densities.end(), density);
- if (densities.end() == iter) {
- ADD_FAILURE() << density << "dpi was not in the density list.";
- return;
- }
+ EXPECT_RULES_EQ(RuleGenerator::generateDensity(densities, highIndex),
+ AndRule()
+ .add(LtRule(Rule::SDK_VERSION, SDK_LOLLIPOP))
+ .add(LtRule(Rule::SCREEN_DENSITY, 263))
+ );
- sp<Rule> rule = RuleGenerator::generateDensity(densities, iter - densities.begin());
- if (rule->op != Rule::AND_SUBRULES) {
- ADD_FAILURE() << "Op in rule for " << density << "dpi is not Rule::AND_SUBRULES.";
- return;
- }
+ EXPECT_RULES_EQ(RuleGenerator::generateDensity(densities, xhighIndex),
+ AndRule()
+ .add(LtRule(Rule::SDK_VERSION, SDK_LOLLIPOP))
+ .add(GtRule(Rule::SCREEN_DENSITY, 262))
+ .add(LtRule(Rule::SCREEN_DENSITY, 363))
+ );
- size_t index = 0;
+ EXPECT_RULES_EQ(RuleGenerator::generateDensity(densities, xxhighIndex),
+ AndRule()
+ .add(LtRule(Rule::SDK_VERSION, SDK_LOLLIPOP))
+ .add(GtRule(Rule::SCREEN_DENSITY, 362))
+ );
- bool isAnyDpi = density == ConfigDescription::DENSITY_ANY;
-
- sp<Rule> anyDpiRule = rule->subrules[index++];
- EXPECT_EQ(Rule::EQUALS, anyDpiRule->op)
- << "for " << density << "dpi ANY DPI rule";
- EXPECT_EQ(Rule::SCREEN_DENSITY, anyDpiRule->key)
- << "for " << density << "dpi ANY DPI rule";
- EXPECT_EQ(isAnyDpi == false, anyDpiRule->negate)
- << "for " << density << "dpi ANY DPI rule";
- if (anyDpiRule->longArgs.size() == 1) {
- EXPECT_EQ(ConfigDescription::DENSITY_ANY, anyDpiRule->longArgs[0])
- << "for " << density << "dpi ANY DPI rule";
- } else {
- EXPECT_EQ(1u, anyDpiRule->longArgs.size())
- << "for " << density << "dpi ANY DPI rule";
- }
-
-
- if (greaterThan != 0) {
- sp<Rule> greaterThanRule = rule->subrules[index++];
- EXPECT_EQ(Rule::GREATER_THAN, greaterThanRule->op)
- << "for " << density << "dpi GREATER_THAN rule";
- EXPECT_EQ(Rule::SCREEN_DENSITY, greaterThanRule->key)
- << "for " << density << "dpi GREATER_THAN rule";
- if (greaterThanRule->longArgs.size() == 1) {
- EXPECT_EQ(greaterThan, greaterThanRule->longArgs[0])
- << "for " << density << "dpi GREATER_THAN rule";
- } else {
- EXPECT_EQ(1u, greaterThanRule->longArgs.size())
- << "for " << density << "dpi GREATER_THAN rule";
- }
- }
-
- if (lessThan != 0) {
- sp<Rule> lessThanRule = rule->subrules[index++];
- EXPECT_EQ(Rule::LESS_THAN, lessThanRule->op)
- << "for " << density << "dpi LESS_THAN rule";
- EXPECT_EQ(Rule::SCREEN_DENSITY, lessThanRule->key)
- << "for " << density << "dpi LESS_THAN rule";
- if (lessThanRule->longArgs.size() == 1) {
- EXPECT_EQ(lessThan, lessThanRule->longArgs[0])
- << "for " << density << "dpi LESS_THAN rule";
- } else {
- EXPECT_EQ(1u, lessThanRule->longArgs.size())
- << "for " << density << "dpi LESS_THAN rule";
- }
- }
-}
-
-static void expectAbiRule(const Vector<abi::Variant>& abis, abi::Variant variant, const Vector<const char*>& matches) {
- const abi::Variant* iter = std::find(abis.begin(), abis.end(), variant);
- if (abis.end() == iter) {
- ADD_FAILURE() << abi::toString(variant) << " was not in the abi list.";
- return;
- }
-
- sp<Rule> rule = RuleGenerator::generateAbi(abis, iter - abis.begin());
-
- EXPECT_EQ(Rule::CONTAINS_ANY, rule->op)
- << "for " << abi::toString(variant) << " rule";
- EXPECT_EQ(Rule::NATIVE_PLATFORM, rule->key)
- << " for " << abi::toString(variant) << " rule";
- EXPECT_EQ(matches.size(), rule->stringArgs.size())
- << " for " << abi::toString(variant) << " rule";
-
- const size_t matchCount = matches.size();
- for (size_t i = 0; i < matchCount; i++) {
- const char* match = matches[i];
- if (rule->stringArgs.end() ==
- std::find(rule->stringArgs.begin(), rule->stringArgs.end(), String8(match))) {
- ADD_FAILURE() << "Rule for abi " << abi::toString(variant)
- << " does not contain match for expected abi " << match;
- }
- }
-}
-
-static void expectAbiRule(const Vector<abi::Variant>& abis, abi::Variant variant, const char* a) {
- Vector<const char*> matches;
- matches.add(a);
- expectAbiRule(abis, variant, matches);
-}
-
-static void expectAbiRule(const Vector<abi::Variant>& abis, abi::Variant variant, const char* a, const char* b) {
- Vector<const char*> matches;
- matches.add(a);
- matches.add(b);
- expectAbiRule(abis, variant, matches);
+ // We expect AlwaysTrue because anydpi always has attached v21 to the configuration
+ // and the rest of the rule generation code generates the sdk version checks.
+ EXPECT_RULES_EQ(RuleGenerator::generateDensity(densities, anyIndex), AlwaysTrue());
}
} // namespace split
diff --git a/tools/split-select/Rule_test.cpp b/tools/split-select/Rule_test.cpp
index aca7433..c6cff0d 100644
--- a/tools/split-select/Rule_test.cpp
+++ b/tools/split-select/Rule_test.cpp
@@ -17,42 +17,28 @@
#include "Rule.h"
#include "SplitDescription.h"
+#include "TestRules.h"
#include <algorithm>
-#include <string>
#include <gtest/gtest.h>
+#include <string>
#include <utils/String8.h>
using namespace android;
+using namespace split::test;
namespace split {
TEST(RuleTest, generatesValidJson) {
- sp<Rule> rule = new Rule();
- rule->op = Rule::AND_SUBRULES;
+ Rule rule(AndRule()
+ .add(EqRule(Rule::SDK_VERSION, 7))
+ .add(OrRule()
+ .add(GtRule(Rule::SCREEN_DENSITY, 10))
+ .add(LtRule(Rule::SCREEN_DENSITY, 5))
+ )
+ );
- sp<Rule> subrule = new Rule();
- subrule->op = Rule::EQUALS;
- subrule->key = Rule::SDK_VERSION;
- subrule->longArgs.add(7);
- rule->subrules.add(subrule);
-
- subrule = new Rule();
- subrule->op = Rule::OR_SUBRULES;
- rule->subrules.add(subrule);
-
- sp<Rule> subsubrule = new Rule();
- subsubrule->op = Rule::GREATER_THAN;
- subsubrule->key = Rule::SCREEN_DENSITY;
- subsubrule->longArgs.add(10);
- subrule->subrules.add(subsubrule);
-
- subsubrule = new Rule();
- subsubrule->op = Rule::LESS_THAN;
- subsubrule->key = Rule::SCREEN_DENSITY;
- subsubrule->longArgs.add(5);
- subrule->subrules.add(subsubrule);
-
+ // Expected
std::string expected(
"{"
" \"op\": \"AND_SUBRULES\","
@@ -79,69 +65,36 @@
" }"
" ]"
"}");
- // Trim
expected.erase(std::remove_if(expected.begin(), expected.end(), ::isspace), expected.end());
- std::string result(rule->toJson().string());
-
- // Trim
+ // Result
+ std::string result(rule.toJson().string());
result.erase(std::remove_if(result.begin(), result.end(), ::isspace), result.end());
ASSERT_EQ(expected, result);
}
TEST(RuleTest, simplifiesSingleSubruleRules) {
- sp<Rule> rule = new Rule();
- rule->op = Rule::AND_SUBRULES;
+ sp<Rule> rule = new Rule(AndRule()
+ .add(EqRule(Rule::SDK_VERSION, 7))
+ );
- sp<Rule> subrule = new Rule();
- subrule->op = Rule::EQUALS;
- subrule->key = Rule::SDK_VERSION;
- subrule->longArgs.add(7);
- rule->subrules.add(subrule);
-
- sp<Rule> simplified = Rule::simplify(rule);
- EXPECT_EQ(Rule::EQUALS, simplified->op);
- EXPECT_EQ(Rule::SDK_VERSION, simplified->key);
- ASSERT_EQ(1u, simplified->longArgs.size());
- EXPECT_EQ(7, simplified->longArgs[0]);
+ EXPECT_RULES_EQ(Rule::simplify(rule), EqRule(Rule::SDK_VERSION, 7));
}
TEST(RuleTest, simplifiesNestedSameOpSubrules) {
- sp<Rule> rule = new Rule();
- rule->op = Rule::AND_SUBRULES;
+ sp<Rule> rule = new Rule(AndRule()
+ .add(AndRule()
+ .add(EqRule(Rule::SDK_VERSION, 7))
+ )
+ .add(EqRule(Rule::SDK_VERSION, 8))
+ );
- sp<Rule> subrule = new Rule();
- subrule->op = Rule::AND_SUBRULES;
- rule->subrules.add(subrule);
-
- sp<Rule> subsubrule = new Rule();
- subsubrule->op = Rule::EQUALS;
- subsubrule->key = Rule::SDK_VERSION;
- subsubrule->longArgs.add(7);
- subrule->subrules.add(subsubrule);
-
- subrule = new Rule();
- subrule->op = Rule::EQUALS;
- subrule->key = Rule::SDK_VERSION;
- subrule->longArgs.add(8);
- rule->subrules.add(subrule);
-
- sp<Rule> simplified = Rule::simplify(rule);
- EXPECT_EQ(Rule::AND_SUBRULES, simplified->op);
- ASSERT_EQ(2u, simplified->subrules.size());
-
- sp<Rule> simplifiedSubrule = simplified->subrules[0];
- EXPECT_EQ(Rule::EQUALS, simplifiedSubrule->op);
- EXPECT_EQ(Rule::SDK_VERSION, simplifiedSubrule->key);
- ASSERT_EQ(1u, simplifiedSubrule->longArgs.size());
- EXPECT_EQ(7, simplifiedSubrule->longArgs[0]);
-
- simplifiedSubrule = simplified->subrules[1];
- EXPECT_EQ(Rule::EQUALS, simplifiedSubrule->op);
- EXPECT_EQ(Rule::SDK_VERSION, simplifiedSubrule->key);
- ASSERT_EQ(1u, simplifiedSubrule->longArgs.size());
- EXPECT_EQ(8, simplifiedSubrule->longArgs[0]);
+ EXPECT_RULES_EQ(Rule::simplify(rule),
+ AndRule()
+ .add(EqRule(Rule::SDK_VERSION, 7))
+ .add(EqRule(Rule::SDK_VERSION, 8))
+ );
}
} // namespace split
diff --git a/tools/split-select/TestRules.cpp b/tools/split-select/TestRules.cpp
new file mode 100644
index 0000000..f980dc4
--- /dev/null
+++ b/tools/split-select/TestRules.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestRules.h"
+
+#include <utils/String8.h>
+
+using android::String8;
+using android::sp;
+
+namespace split {
+namespace test {
+
+const Rule EqRule(Rule::Key key, long value) {
+ Rule rule;
+ rule.op = Rule::EQUALS;
+ rule.key = key;
+ rule.longArgs.add(value);
+ return rule;
+}
+
+const Rule GtRule(Rule::Key key, long value) {
+ Rule rule;
+ rule.op = Rule::GREATER_THAN;
+ rule.key = key;
+ rule.longArgs.add(value);
+ return rule;
+}
+
+const Rule LtRule(Rule::Key key, long value) {
+ Rule rule;
+ rule.op = Rule::LESS_THAN;
+ rule.key = key;
+ rule.longArgs.add(value);
+ return rule;
+}
+
+const Rule ContainsAnyRule(Rule::Key key, const char* str1) {
+ Rule rule;
+ rule.op = Rule::CONTAINS_ANY;
+ rule.key = key;
+ rule.stringArgs.add(String8(str1));
+ return rule;
+}
+
+const Rule ContainsAnyRule(Rule::Key key, const char* str1, const char* str2) {
+ Rule rule;
+ rule.op = Rule::CONTAINS_ANY;
+ rule.key = key;
+ rule.stringArgs.add(String8(str1));
+ rule.stringArgs.add(String8(str2));
+ return rule;
+}
+
+const Rule AlwaysTrue() {
+ Rule rule;
+ rule.op = Rule::ALWAYS_TRUE;
+ return rule;
+}
+
+::testing::AssertionResult RulePredFormat(
+ const char*, const char*,
+ const sp<Rule>& actual, const Rule& expected) {
+ const String8 expectedStr(expected.toJson());
+ const String8 actualStr(actual != NULL ? actual->toJson() : "");
+
+ if (expectedStr != actualStr) {
+ return ::testing::AssertionFailure()
+ << "Expected: " << expectedStr.string() << "\n"
+ << " Actual: " << actualStr.string();
+ }
+ return ::testing::AssertionSuccess();
+}
+
+
+} // namespace test
+} // namespace split
diff --git a/tools/split-select/TestRules.h b/tools/split-select/TestRules.h
new file mode 100644
index 0000000..50b7ad1
--- /dev/null
+++ b/tools/split-select/TestRules.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef H_AAPT_SPLIT_TEST_RULES
+#define H_AAPT_SPLIT_TEST_RULES
+
+#include "Rule.h"
+
+#include <gtest/gtest.h>
+
+namespace split {
+namespace test {
+
+struct AndRule : public Rule {
+ AndRule() {
+ op = Rule::AND_SUBRULES;
+ }
+
+ AndRule& add(const Rule& rhs) {
+ subrules.add(new Rule(rhs));
+ return *this;
+ }
+};
+
+struct OrRule : public Rule {
+ OrRule() {
+ op = Rule::OR_SUBRULES;
+ }
+
+ OrRule& add(const Rule& rhs) {
+ subrules.add(new Rule(rhs));
+ return *this;
+ }
+};
+
+const Rule EqRule(Rule::Key key, long value);
+const Rule LtRule(Rule::Key key, long value);
+const Rule GtRule(Rule::Key key, long value);
+const Rule ContainsAnyRule(Rule::Key key, const char* str1);
+const Rule ContainsAnyRule(Rule::Key key, const char* str1, const char* str2);
+const Rule AlwaysTrue();
+
+::testing::AssertionResult RulePredFormat(
+ const char* actualExpr, const char* expectedExpr,
+ const android::sp<Rule>& actual, const Rule& expected);
+
+#define EXPECT_RULES_EQ(actual, expected) \
+ EXPECT_PRED_FORMAT2(::split::test::RulePredFormat, actual, expected)
+
+} // namespace test
+} // namespace split
+
+#endif // H_AAPT_SPLIT_TEST_RULES